mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-04-24 06:37:14 -04:00
Merge branch 'main' into feat/link_preview
This commit is contained in:
commit
136e0dbb7d
184 changed files with 8914 additions and 1730 deletions
3210
frontend/appflowy_flutter/assets/translations/mr-IN.json
Normal file
3210
frontend/appflowy_flutter/assets/translations/mr-IN.json
Normal file
File diff suppressed because it is too large
Load diff
|
@ -15,6 +15,7 @@ void main() {
|
|||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
// create a database and add a linked database view
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Grid);
|
||||
|
||||
|
@ -29,6 +30,11 @@ void main() {
|
|||
await tester.tapHidePropertyButton();
|
||||
tester.noFieldWithName('New field 1');
|
||||
|
||||
// create another field, New field 1 to be hidden still
|
||||
await tester.tapNewPropertyButton();
|
||||
await tester.dismissFieldEditor();
|
||||
tester.noFieldWithName('New field 1');
|
||||
|
||||
// go back to inline database view, expect field to be shown
|
||||
await tester.tapTabBarLinkedViewByViewName('Untitled');
|
||||
tester.findFieldWithName('New field 1');
|
||||
|
@ -60,5 +66,40 @@ void main() {
|
|||
await tester.tapDatabaseSortButton();
|
||||
await tester.tapCreateSortByFieldType(FieldType.RichText, "New field 1");
|
||||
});
|
||||
|
||||
testWidgets('field cell width', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
// create a database and add a linked database view
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Grid);
|
||||
|
||||
// create a field
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
await tester.tapNewPropertyButton();
|
||||
await tester.renameField('New field 1');
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// check the width of the field
|
||||
expect(tester.getFieldWidth('New field 1'), 150);
|
||||
|
||||
// change the width of the field
|
||||
await tester.changeFieldWidth('New field 1', 200);
|
||||
expect(tester.getFieldWidth('New field 1'), 205);
|
||||
|
||||
// create another field, New field 1 to be same width
|
||||
await tester.tapNewPropertyButton();
|
||||
await tester.dismissFieldEditor();
|
||||
expect(tester.getFieldWidth('New field 1'), 205);
|
||||
|
||||
// go back to inline database view, expect New field 1 to be 150px
|
||||
await tester.tapTabBarLinkedViewByViewName('Untitled');
|
||||
expect(tester.getFieldWidth('New field 1'), 150);
|
||||
|
||||
// go back to linked database view, expect New field 1 to be 205px
|
||||
await tester.tapTabBarLinkedViewByViewName('Grid');
|
||||
expect(tester.getFieldWidth('New field 1'), 205);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -942,6 +942,31 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
|||
await pumpAndSettle(const Duration(milliseconds: 200));
|
||||
}
|
||||
|
||||
Future<void> changeFieldWidth(String fieldName, double width) async {
|
||||
final field = find.byWidgetPredicate(
|
||||
(widget) => widget is GridFieldCell && widget.fieldInfo.name == fieldName,
|
||||
);
|
||||
await hoverOnWidget(
|
||||
field,
|
||||
onHover: () async {
|
||||
final dragHandle = find.descendant(
|
||||
of: field,
|
||||
matching: find.byType(DragToExpandLine),
|
||||
);
|
||||
await drag(dragHandle, Offset(width - getSize(field).width, 0));
|
||||
await pumpAndSettle(const Duration(milliseconds: 200));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
double getFieldWidth(String fieldName) {
|
||||
final field = find.byWidgetPredicate(
|
||||
(widget) => widget is GridFieldCell && widget.fieldInfo.name == fieldName,
|
||||
);
|
||||
|
||||
return getSize(field).width;
|
||||
}
|
||||
|
||||
Future<void> findDateEditor(dynamic matcher) async {
|
||||
final finder = find.byType(DateCellEditor);
|
||||
expect(finder, matcher);
|
||||
|
|
|
@ -203,7 +203,7 @@ class MobileViewPageMoreBottomSheet extends StatelessWidget {
|
|||
);
|
||||
showToastNotification(
|
||||
context,
|
||||
message: LocaleKeys.grid_url_copy.tr(),
|
||||
message: LocaleKeys.message_copy_success.tr(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@ class MobileInlineActionsWidget extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final hasIcon = item.icon != null;
|
||||
final hasIcon = item.iconBuilder != null;
|
||||
return Container(
|
||||
height: 36,
|
||||
decoration: BoxDecoration(
|
||||
|
@ -119,7 +119,7 @@ class MobileInlineActionsWidget extends StatelessWidget {
|
|||
child: Row(
|
||||
children: [
|
||||
if (hasIcon) ...[
|
||||
item.icon!.call(isSelected),
|
||||
item.iconBuilder!.call(isSelected),
|
||||
SizedBox(width: 12),
|
||||
],
|
||||
Flexible(
|
||||
|
|
|
@ -185,7 +185,7 @@ class CopyButton extends StatelessWidget {
|
|||
if (context.mounted) {
|
||||
showToastNotification(
|
||||
context,
|
||||
message: LocaleKeys.grid_url_copiedNotification.tr(),
|
||||
message: LocaleKeys.message_copy_success.tr(),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -377,7 +377,7 @@ class ChatAIMessagePopup extends StatelessWidget {
|
|||
if (context.mounted) {
|
||||
showToastNotification(
|
||||
context,
|
||||
message: LocaleKeys.grid_url_copiedNotification.tr(),
|
||||
message: LocaleKeys.message_copy_success.tr(),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -241,6 +241,11 @@ class SelectOptionCellEditorBloc
|
|||
} else if (!state.selectedOptions
|
||||
.any((option) => option.id == focusedOptionId)) {
|
||||
_selectOptionService.select(optionIds: [focusedOptionId]);
|
||||
emit(
|
||||
state.copyWith(
|
||||
clearFilter: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -411,23 +411,28 @@ class FieldController {
|
|||
/// Listen for field setting changes in the backend.
|
||||
void _listenOnFieldSettingsChanged() {
|
||||
FieldInfo? updateFieldSettings(FieldSettingsPB updatedFieldSettings) {
|
||||
final List<FieldInfo> newFields = fieldInfos;
|
||||
var updatedField = newFields.firstOrNull;
|
||||
final newFields = [...fieldInfos];
|
||||
|
||||
if (updatedField == null) {
|
||||
if (newFields.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final index = newFields
|
||||
.indexWhere((field) => field.id == updatedFieldSettings.fieldId);
|
||||
|
||||
if (index != -1) {
|
||||
newFields[index] =
|
||||
newFields[index].copyWith(fieldSettings: updatedFieldSettings);
|
||||
updatedField = newFields[index];
|
||||
_fieldNotifier.fieldInfos = newFields;
|
||||
_fieldSettings
|
||||
..removeWhere(
|
||||
(field) => field.fieldId == updatedFieldSettings.fieldId,
|
||||
)
|
||||
..add(updatedFieldSettings);
|
||||
return newFields[index];
|
||||
}
|
||||
|
||||
_fieldNotifier.fieldInfos = newFields;
|
||||
return updatedField;
|
||||
return null;
|
||||
}
|
||||
|
||||
_fieldSettingsListener.start(
|
||||
|
|
|
@ -108,7 +108,7 @@ class _GridFieldCellState extends State<GridFieldCell> {
|
|||
top: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
child: _DragToExpandLine(),
|
||||
child: DragToExpandLine(),
|
||||
);
|
||||
|
||||
return _GridHeaderCellContainer(
|
||||
|
@ -158,8 +158,11 @@ class _GridHeaderCellContainer extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
class _DragToExpandLine extends StatelessWidget {
|
||||
const _DragToExpandLine();
|
||||
@visibleForTesting
|
||||
class DragToExpandLine extends StatelessWidget {
|
||||
const DragToExpandLine({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
|
@ -203,7 +203,7 @@ class MobileURLEditor extends StatelessWidget {
|
|||
ClipboardData(text: textEditingController.text),
|
||||
);
|
||||
Fluttertoast.showToast(
|
||||
msg: LocaleKeys.grid_url_copiedNotification.tr(),
|
||||
msg: LocaleKeys.message_copy_success.tr(),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
context.pop();
|
||||
|
|
|
@ -70,13 +70,12 @@ extension InsertDatabase on EditorState {
|
|||
node,
|
||||
selection.end.offset,
|
||||
0,
|
||||
r'$',
|
||||
attributes: {
|
||||
MentionBlockKeys.mention: {
|
||||
MentionBlockKeys.type: MentionType.page.name,
|
||||
MentionBlockKeys.pageId: view.id,
|
||||
},
|
||||
},
|
||||
MentionBlockKeys.mentionChar,
|
||||
attributes: MentionBlockKeys.buildMentionPageAttributes(
|
||||
mentionType: MentionType.page,
|
||||
pageId: view.id,
|
||||
blockId: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -43,13 +43,11 @@ extension PasteFromBlockLink on EditorState {
|
|||
node,
|
||||
selection.startIndex,
|
||||
MentionBlockKeys.mentionChar,
|
||||
attributes: {
|
||||
MentionBlockKeys.mention: {
|
||||
MentionBlockKeys.type: MentionType.page.name,
|
||||
MentionBlockKeys.blockId: blockId,
|
||||
MentionBlockKeys.pageId: pageId,
|
||||
},
|
||||
},
|
||||
attributes: MentionBlockKeys.buildMentionPageAttributes(
|
||||
mentionType: MentionType.page,
|
||||
pageId: pageId,
|
||||
blockId: blockId,
|
||||
),
|
||||
);
|
||||
await apply(transaction);
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'package:appflowy/shared/icon_emoji_picker/icon_picker.dart';
|
|||
import 'package:appflowy/user/application/user_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_svg/flowy_svg.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:string_validator/string_validator.dart';
|
||||
|
@ -113,24 +114,21 @@ class _RawEmojiIconWidgetState extends State<RawEmojiIconWidget> {
|
|||
try {
|
||||
switch (widget.emoji.type) {
|
||||
case FlowyIconType.emoji:
|
||||
return EmojiText(
|
||||
emoji: widget.emoji.emoji,
|
||||
return FlowyText.emoji(
|
||||
widget.emoji.emoji,
|
||||
fontSize: widget.emojiSize,
|
||||
textAlign: TextAlign.justify,
|
||||
lineHeight: widget.lineHeight,
|
||||
);
|
||||
case FlowyIconType.icon:
|
||||
IconsData iconData =
|
||||
IconsData.fromJson(jsonDecode(widget.emoji.emoji));
|
||||
IconsData iconData = IconsData.fromJson(
|
||||
jsonDecode(widget.emoji.emoji),
|
||||
);
|
||||
if (!widget.enableColor) {
|
||||
iconData = iconData.noColor();
|
||||
}
|
||||
|
||||
/// Under the same width conditions, icons on macOS seem to appear
|
||||
/// larger than emojis, so 0.9 is used here to slightly reduce the
|
||||
/// size of the icons
|
||||
final iconSize =
|
||||
Platform.isMacOS ? widget.emojiSize * 0.9 : widget.emojiSize;
|
||||
final iconSize = widget.emojiSize;
|
||||
return IconWidget(
|
||||
iconsData: iconData,
|
||||
size: iconSize,
|
||||
|
|
|
@ -179,13 +179,6 @@ class ChildPageTransactionHandler extends MentionTransactionHandler {
|
|||
|
||||
await duplicatedViewOrFailure.fold(
|
||||
(newView) async {
|
||||
final newMentionAttributes = {
|
||||
MentionBlockKeys.mention: {
|
||||
MentionBlockKeys.type: MentionType.childPage.name,
|
||||
MentionBlockKeys.pageId: newView.id,
|
||||
},
|
||||
};
|
||||
|
||||
// The index is the index of the delta, to get the index of the mention character
|
||||
// in all the text, we need to calculate it based on the deltas before the current delta.
|
||||
int mentionIndex = 0;
|
||||
|
@ -202,7 +195,11 @@ class ChildPageTransactionHandler extends MentionTransactionHandler {
|
|||
node,
|
||||
mentionIndex,
|
||||
MentionBlockKeys.mentionChar.length,
|
||||
newMentionAttributes,
|
||||
MentionBlockKeys.buildMentionPageAttributes(
|
||||
mentionType: MentionType.childPage,
|
||||
pageId: newView.id,
|
||||
blockId: null,
|
||||
),
|
||||
);
|
||||
await editorState.apply(
|
||||
transaction,
|
||||
|
|
|
@ -192,15 +192,12 @@ class DateTransactionHandler extends MentionTransactionHandler {
|
|||
),
|
||||
);
|
||||
|
||||
final newMentionAttributes = {
|
||||
MentionBlockKeys.mention: {
|
||||
MentionBlockKeys.type: MentionType.date.name,
|
||||
MentionBlockKeys.date: dateTime.toIso8601String(),
|
||||
MentionBlockKeys.reminderId: reminderId,
|
||||
MentionBlockKeys.includeTime: data.includeTime,
|
||||
MentionBlockKeys.reminderOption: data.reminderOption.name,
|
||||
},
|
||||
};
|
||||
final newMentionAttributes = MentionBlockKeys.buildMentionDateAttributes(
|
||||
date: dateTime.toIso8601String(),
|
||||
reminderId: reminderId,
|
||||
reminderOption: data.reminderOption.name,
|
||||
includeTime: data.includeTime,
|
||||
);
|
||||
|
||||
// The index is the index of the delta, to get the index of the mention character
|
||||
// in all the text, we need to calculate it based on the deltas before the current delta.
|
||||
|
|
|
@ -31,12 +31,12 @@ Node dateMentionNode() {
|
|||
operations: [
|
||||
TextInsert(
|
||||
MentionBlockKeys.mentionChar,
|
||||
attributes: {
|
||||
MentionBlockKeys.mention: {
|
||||
MentionBlockKeys.type: MentionType.date.name,
|
||||
MentionBlockKeys.date: DateTime.now().toIso8601String(),
|
||||
},
|
||||
},
|
||||
attributes: MentionBlockKeys.buildMentionDateAttributes(
|
||||
date: DateTime.now().toIso8601String(),
|
||||
reminderId: null,
|
||||
reminderOption: null,
|
||||
includeTime: false,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -46,9 +46,9 @@ Node dateMentionNode() {
|
|||
class MentionBlockKeys {
|
||||
const MentionBlockKeys._();
|
||||
|
||||
static const reminderId = 'reminder_id'; // ReminderID
|
||||
static const mention = 'mention';
|
||||
static const type = 'type'; // MentionType, String
|
||||
|
||||
static const pageId = 'page_id';
|
||||
static const blockId = 'block_id';
|
||||
static const url = 'url';
|
||||
|
@ -56,9 +56,42 @@ class MentionBlockKeys {
|
|||
// Related to Reminder and Date blocks
|
||||
static const date = 'date'; // Start Date
|
||||
static const includeTime = 'include_time';
|
||||
static const reminderId = 'reminder_id'; // ReminderID
|
||||
static const reminderOption = 'reminder_option';
|
||||
|
||||
static const mentionChar = '\$';
|
||||
|
||||
static Map<String, dynamic> buildMentionPageAttributes({
|
||||
required MentionType mentionType,
|
||||
required String pageId,
|
||||
required String? blockId,
|
||||
}) {
|
||||
return {
|
||||
MentionBlockKeys.mention: {
|
||||
MentionBlockKeys.type: mentionType.name,
|
||||
MentionBlockKeys.pageId: pageId,
|
||||
if (blockId != null) MentionBlockKeys.blockId: blockId,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
static Map<String, dynamic> buildMentionDateAttributes({
|
||||
required String date,
|
||||
required String? reminderId,
|
||||
required String? reminderOption,
|
||||
required bool includeTime,
|
||||
}) {
|
||||
return {
|
||||
MentionBlockKeys.mention: {
|
||||
MentionBlockKeys.type: MentionType.date.name,
|
||||
MentionBlockKeys.date: date,
|
||||
MentionBlockKeys.includeTime: includeTime,
|
||||
if (reminderId != null) MentionBlockKeys.reminderId: reminderId,
|
||||
if (reminderOption != null)
|
||||
MentionBlockKeys.reminderOption: reminderOption,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class MentionBlock extends StatelessWidget {
|
||||
|
|
|
@ -201,16 +201,17 @@ class _MentionDateBlockState extends State<MentionDateBlock> {
|
|||
(reminderOption == ReminderOption.none ? null : widget.reminderId);
|
||||
|
||||
final transaction = widget.editorState.transaction
|
||||
..formatText(widget.node, widget.index, 1, {
|
||||
MentionBlockKeys.mention: {
|
||||
MentionBlockKeys.type: MentionType.date.name,
|
||||
MentionBlockKeys.date: date.toIso8601String(),
|
||||
MentionBlockKeys.reminderId: rId,
|
||||
MentionBlockKeys.includeTime: includeTime,
|
||||
MentionBlockKeys.reminderOption:
|
||||
reminderOption?.name ?? widget.reminderOption.name,
|
||||
},
|
||||
});
|
||||
..formatText(
|
||||
widget.node,
|
||||
widget.index,
|
||||
1,
|
||||
MentionBlockKeys.buildMentionDateAttributes(
|
||||
date: date.toIso8601String(),
|
||||
reminderId: rId,
|
||||
includeTime: includeTime,
|
||||
reminderOption: reminderOption?.name ?? widget.reminderOption.name,
|
||||
),
|
||||
);
|
||||
|
||||
widget.editorState.apply(transaction, withUpdateSelection: false);
|
||||
|
||||
|
|
|
@ -50,12 +50,11 @@ Node pageMentionNode(String viewId) {
|
|||
operations: [
|
||||
TextInsert(
|
||||
MentionBlockKeys.mentionChar,
|
||||
attributes: {
|
||||
MentionBlockKeys.mention: {
|
||||
MentionBlockKeys.type: MentionType.page.name,
|
||||
MentionBlockKeys.pageId: viewId,
|
||||
},
|
||||
},
|
||||
attributes: MentionBlockKeys.buildMentionPageAttributes(
|
||||
mentionType: MentionType.page,
|
||||
pageId: viewId,
|
||||
blockId: null,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -284,12 +283,11 @@ class _MentionSubPageBlockState extends State<MentionSubPageBlock> {
|
|||
widget.node,
|
||||
widget.index,
|
||||
MentionBlockKeys.mentionChar.length,
|
||||
{
|
||||
MentionBlockKeys.mention: {
|
||||
MentionBlockKeys.type: MentionType.page.name,
|
||||
MentionBlockKeys.pageId: widget.pageId,
|
||||
},
|
||||
},
|
||||
MentionBlockKeys.buildMentionPageAttributes(
|
||||
mentionType: MentionType.page,
|
||||
pageId: widget.pageId,
|
||||
blockId: null,
|
||||
),
|
||||
);
|
||||
|
||||
widget.editorState.apply(
|
||||
|
@ -383,25 +381,24 @@ Future<void> _handleDoubleTap(
|
|||
}
|
||||
|
||||
final currentViewId = context.read<DocumentBloc>().documentId;
|
||||
final newViewId = await showPageSelectorSheet(
|
||||
final newView = await showPageSelectorSheet(
|
||||
context,
|
||||
currentViewId: currentViewId,
|
||||
selectedViewId: viewId,
|
||||
);
|
||||
|
||||
if (newViewId != null) {
|
||||
if (newView != null) {
|
||||
// Update this nodes pageId
|
||||
final transaction = editorState.transaction
|
||||
..formatText(
|
||||
node,
|
||||
index,
|
||||
1,
|
||||
{
|
||||
MentionBlockKeys.mention: {
|
||||
MentionBlockKeys.type: MentionType.page.name,
|
||||
MentionBlockKeys.pageId: newViewId,
|
||||
},
|
||||
},
|
||||
MentionBlockKeys.buildMentionPageAttributes(
|
||||
mentionType: MentionType.page,
|
||||
pageId: newView.id,
|
||||
blockId: null,
|
||||
),
|
||||
);
|
||||
|
||||
await editorState.apply(transaction, withUpdateSelection: false);
|
||||
|
|
|
@ -46,12 +46,12 @@ extension on EditorState {
|
|||
selection.start.offset,
|
||||
0,
|
||||
MentionBlockKeys.mentionChar,
|
||||
attributes: {
|
||||
MentionBlockKeys.mention: {
|
||||
MentionBlockKeys.type: MentionType.date.name,
|
||||
MentionBlockKeys.date: DateTime.now().toIso8601String(),
|
||||
},
|
||||
},
|
||||
attributes: MentionBlockKeys.buildMentionDateAttributes(
|
||||
date: DateTime.now().toIso8601String(),
|
||||
reminderId: null,
|
||||
reminderOption: null,
|
||||
includeTime: false,
|
||||
),
|
||||
);
|
||||
|
||||
await apply(transaction);
|
||||
|
|
|
@ -568,7 +568,6 @@ class EditorStyleCustomizer {
|
|||
if (style == null) {
|
||||
return null;
|
||||
}
|
||||
final fontSize = style.fontSize ?? 14.0;
|
||||
final isLight = Theme.of(context).isLightMode;
|
||||
final textColor = isLight ? Color(0xFF007296) : Color(0xFF49CFF4);
|
||||
final underlineColor = isLight ? Color(0x33005A7A) : Color(0x3349CFF4);
|
||||
|
@ -578,17 +577,10 @@ class EditorStyleCustomizer {
|
|||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
AiWriterBlockKeys.suggestionReplacement => style.copyWith(
|
||||
color: Colors.transparent,
|
||||
color: textColor,
|
||||
decoration: TextDecoration.underline,
|
||||
decorationColor: underlineColor,
|
||||
decorationThickness: 1.0,
|
||||
// hack: https://jtmuller5.medium.com/the-ultimate-guide-to-underlining-text-in-flutter-57936f5c79bb
|
||||
shadows: [
|
||||
Shadow(
|
||||
color: textColor,
|
||||
offset: Offset(0, -fontSize * 0.2),
|
||||
),
|
||||
],
|
||||
),
|
||||
_ => style,
|
||||
};
|
||||
|
|
|
@ -24,7 +24,7 @@ class InlineChildPageService extends InlineActionsDelegate {
|
|||
results.add(
|
||||
InlineActionsMenuItem(
|
||||
label: LocaleKeys.inlineActions_createPage.tr(args: [search]),
|
||||
icon: (_) => const FlowySvg(FlowySvgs.add_s),
|
||||
iconBuilder: (_) => const FlowySvg(FlowySvgs.add_s),
|
||||
onSelected: (context, editorState, service, replacement) =>
|
||||
_onSelected(context, editorState, service, replacement, search),
|
||||
),
|
||||
|
@ -71,12 +71,11 @@ class InlineChildPageService extends InlineActionsDelegate {
|
|||
replacement.$1,
|
||||
replacement.$2,
|
||||
MentionBlockKeys.mentionChar,
|
||||
attributes: {
|
||||
MentionBlockKeys.mention: {
|
||||
MentionBlockKeys.type: MentionType.childPage.name,
|
||||
MentionBlockKeys.pageId: view.id,
|
||||
},
|
||||
},
|
||||
attributes: MentionBlockKeys.buildMentionPageAttributes(
|
||||
mentionType: MentionType.childPage,
|
||||
pageId: view.id,
|
||||
blockId: null,
|
||||
),
|
||||
);
|
||||
|
||||
await editorState.apply(transaction);
|
||||
|
|
|
@ -122,12 +122,12 @@ class DateReferenceService extends InlineActionsDelegate {
|
|||
start,
|
||||
end,
|
||||
MentionBlockKeys.mentionChar,
|
||||
attributes: {
|
||||
MentionBlockKeys.mention: {
|
||||
MentionBlockKeys.type: MentionType.date.name,
|
||||
MentionBlockKeys.date: date.toIso8601String(),
|
||||
},
|
||||
},
|
||||
attributes: MentionBlockKeys.buildMentionDateAttributes(
|
||||
date: date.toIso8601String(),
|
||||
includeTime: false,
|
||||
reminderId: null,
|
||||
reminderOption: null,
|
||||
),
|
||||
);
|
||||
|
||||
await editorState.apply(transaction);
|
||||
|
|
|
@ -221,12 +221,11 @@ class InlinePageReferenceService extends InlineActionsDelegate {
|
|||
replace.$1,
|
||||
replace.$2,
|
||||
MentionBlockKeys.mentionChar,
|
||||
attributes: {
|
||||
MentionBlockKeys.mention: {
|
||||
MentionBlockKeys.type: MentionType.page.name,
|
||||
MentionBlockKeys.pageId: view.id,
|
||||
},
|
||||
},
|
||||
attributes: MentionBlockKeys.buildMentionPageAttributes(
|
||||
mentionType: MentionType.page,
|
||||
pageId: view.id,
|
||||
blockId: null,
|
||||
),
|
||||
);
|
||||
|
||||
await editorState.apply(transaction);
|
||||
|
@ -235,12 +234,19 @@ class InlinePageReferenceService extends InlineActionsDelegate {
|
|||
InlineActionsMenuItem _fromView(ViewPB view) => InlineActionsMenuItem(
|
||||
keywords: [view.nameOrDefault.toLowerCase()],
|
||||
label: view.nameOrDefault,
|
||||
icon: (onSelected) => view.icon.value.isNotEmpty
|
||||
? RawEmojiIconWidget(
|
||||
emoji: view.icon.toEmojiIconData(),
|
||||
emojiSize: 14,
|
||||
)
|
||||
: view.defaultIcon(),
|
||||
iconBuilder: (onSelected) {
|
||||
final child = view.icon.value.isNotEmpty
|
||||
? RawEmojiIconWidget(
|
||||
emoji: view.icon.toEmojiIconData(),
|
||||
emojiSize: 16.0,
|
||||
lineHeight: 18.0 / 16.0,
|
||||
)
|
||||
: view.defaultIcon(size: const Size(16, 16));
|
||||
return SizedBox(
|
||||
width: 16,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
onSelected: (context, editorState, menu, replace) => insertPage
|
||||
? _onInsertPageRef(view, context, editorState, replace)
|
||||
: _onInsertLinkRef(view, context, editorState, menu, replace),
|
||||
|
|
|
@ -148,14 +148,12 @@ class ReminderReferenceService extends InlineActionsDelegate {
|
|||
start,
|
||||
end,
|
||||
MentionBlockKeys.mentionChar,
|
||||
attributes: {
|
||||
MentionBlockKeys.mention: {
|
||||
MentionBlockKeys.type: MentionType.date.name,
|
||||
MentionBlockKeys.date: date.toIso8601String(),
|
||||
MentionBlockKeys.reminderId: reminder.id,
|
||||
MentionBlockKeys.reminderOption: ReminderOption.atTimeOfEvent.name,
|
||||
},
|
||||
},
|
||||
attributes: MentionBlockKeys.buildMentionDateAttributes(
|
||||
date: date.toIso8601String(),
|
||||
reminderId: reminder.id,
|
||||
reminderOption: ReminderOption.atTimeOfEvent.name,
|
||||
includeTime: false,
|
||||
),
|
||||
);
|
||||
|
||||
await editorState.apply(transaction);
|
||||
|
|
|
@ -12,13 +12,13 @@ typedef SelectItemHandler = void Function(
|
|||
class InlineActionsMenuItem {
|
||||
InlineActionsMenuItem({
|
||||
required this.label,
|
||||
this.icon,
|
||||
this.iconBuilder,
|
||||
this.keywords,
|
||||
this.onSelected,
|
||||
});
|
||||
|
||||
final String label;
|
||||
final Widget Function(bool onSelected)? icon;
|
||||
final Widget Function(bool onSelected)? iconBuilder;
|
||||
final List<String>? keywords;
|
||||
final SelectItemHandler? onSelected;
|
||||
}
|
||||
|
|
|
@ -92,8 +92,8 @@ class InlineActionsWidget extends StatefulWidget {
|
|||
class _InlineActionsWidgetState extends State<InlineActionsWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final icon = widget.item.icon;
|
||||
final hasIcon = icon != null;
|
||||
final iconBuilder = widget.item.iconBuilder;
|
||||
final hasIcon = iconBuilder != null;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 2),
|
||||
child: SizedBox(
|
||||
|
@ -104,7 +104,7 @@ class _InlineActionsWidgetState extends State<InlineActionsWidget> {
|
|||
text: Row(
|
||||
children: [
|
||||
if (hasIcon) ...[
|
||||
icon.call(widget.isSelected),
|
||||
iconBuilder.call(widget.isSelected),
|
||||
SizedBox(width: 12),
|
||||
],
|
||||
Flexible(
|
||||
|
|
|
@ -175,7 +175,7 @@ class ExportTab extends StatelessWidget {
|
|||
);
|
||||
showToastNotification(
|
||||
context,
|
||||
message: LocaleKeys.grid_url_copiedNotification.tr(),
|
||||
message: LocaleKeys.message_copy_success.tr(),
|
||||
);
|
||||
},
|
||||
(error) => showToastNotification(context, message: error.msg),
|
||||
|
|
|
@ -183,7 +183,7 @@ class _PublishedWidgetState extends State<_PublishedWidget> {
|
|||
|
||||
showToastNotification(
|
||||
context,
|
||||
message: LocaleKeys.grid_url_copy.tr(),
|
||||
message: LocaleKeys.message_copy_success.tr(),
|
||||
);
|
||||
},
|
||||
onSubmitted: (pathName) {
|
||||
|
|
|
@ -118,7 +118,7 @@ class _ShareTabContent extends StatelessWidget {
|
|||
|
||||
showToastNotification(
|
||||
context,
|
||||
message: LocaleKeys.grid_url_copy.tr(),
|
||||
message: LocaleKeys.message_copy_success.tr(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/core/helpers/url_launcher.dart';
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flowy_svg/flowy_svg.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class FlowyErrorPage extends StatelessWidget {
|
||||
factory FlowyErrorPage.error(
|
||||
|
@ -86,7 +90,9 @@ class FlowyErrorPage extends StatelessWidget {
|
|||
Listener(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onPointerDown: (_) async {
|
||||
await Clipboard.setData(ClipboardData(text: message));
|
||||
await getIt<ClipboardService>().setData(
|
||||
ClipboardServiceData(plainText: message),
|
||||
);
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
|
@ -188,8 +194,8 @@ class StackTracePreview extends StatelessWidget {
|
|||
"Copy",
|
||||
),
|
||||
useIntrinsicWidth: true,
|
||||
onTap: () => Clipboard.setData(
|
||||
ClipboardData(text: stackTrace),
|
||||
onTap: () => getIt<ClipboardService>().setData(
|
||||
ClipboardServiceData(plainText: stackTrace),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -252,18 +258,14 @@ class GitHubRedirectButton extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
return FlowyButton(
|
||||
leftIconSize: const Size.square(_height),
|
||||
text: const FlowyText(
|
||||
"AppFlowy",
|
||||
),
|
||||
text: FlowyText(LocaleKeys.appName.tr()),
|
||||
useIntrinsicWidth: true,
|
||||
leftIcon: const Padding(
|
||||
padding: EdgeInsets.all(4.0),
|
||||
child: FlowySvg(FlowySvgData('login/github-mark')),
|
||||
),
|
||||
onTap: () async {
|
||||
if (await canLaunchUrl(_gitHubNewBugUri)) {
|
||||
await launchUrl(_gitHubNewBugUri);
|
||||
}
|
||||
await afLaunchUri(_gitHubNewBugUri);
|
||||
},
|
||||
);
|
||||
}
|
|
@ -99,6 +99,7 @@ class InitAppWidgetTask extends LaunchTask {
|
|||
Locale('zh', 'TW'),
|
||||
Locale('fa'),
|
||||
Locale('hin'),
|
||||
Locale('mr','IN'),
|
||||
],
|
||||
path: 'assets/translations',
|
||||
fallbackLocale: const Locale('en'),
|
||||
|
|
|
@ -73,10 +73,10 @@ Future<Directory> appFlowyApplicationDataDirectory() async {
|
|||
case IntegrationMode.develop:
|
||||
final Directory documentsDir = await getApplicationSupportDirectory()
|
||||
.then((directory) => directory.create());
|
||||
return Directory(path.join(documentsDir.path, 'data_dev')).create();
|
||||
return Directory(path.join(documentsDir.path, 'data_dev'));
|
||||
case IntegrationMode.release:
|
||||
final Directory documentsDir = await getApplicationSupportDirectory();
|
||||
return Directory(path.join(documentsDir.path, 'data')).create();
|
||||
return Directory(path.join(documentsDir.path, 'data'));
|
||||
case IntegrationMode.unitTest:
|
||||
case IntegrationMode.integrationTest:
|
||||
return Directory(path.join(Directory.current.path, '.sandbox'));
|
||||
|
|
|
@ -8,7 +8,6 @@ import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
|
|||
show SignInPayloadPB, SignUpPayloadPB, UserProfilePB;
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/uuid.dart';
|
||||
|
||||
import '../../../generated/locale_keys.g.dart';
|
||||
import 'device_id.dart';
|
||||
|
@ -65,8 +64,7 @@ class BackendAuthService implements AuthService {
|
|||
Map<String, String> params = const {},
|
||||
}) async {
|
||||
const password = "Guest!@123456";
|
||||
final uid = uuid();
|
||||
final userEmail = "$uid@appflowy.io";
|
||||
final userEmail = "anon@appflowy.io";
|
||||
|
||||
final request = SignUpPayloadPB.create()
|
||||
..name = LocaleKeys.defaultUsername.tr()
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:local_notifier/local_notifier.dart';
|
||||
|
||||
const _appName = "AppFlowy";
|
||||
|
||||
/// Manages Local Notifications
|
||||
///
|
||||
/// Currently supports:
|
||||
|
@ -13,7 +12,7 @@ const _appName = "AppFlowy";
|
|||
///
|
||||
class NotificationService {
|
||||
static Future<void> initialize() async {
|
||||
await localNotifier.setup(appName: _appName);
|
||||
await localNotifier.setup(appName: LocaleKeys.appName.tr());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -632,7 +632,11 @@ class _SingleInnerViewItemState extends State<SingleInnerViewItem> {
|
|||
Widget _buildViewIconButton() {
|
||||
final iconData = widget.view.icon.toEmojiIconData();
|
||||
final icon = iconData.isNotEmpty
|
||||
? RawEmojiIconWidget(emoji: iconData, emojiSize: 16.0)
|
||||
? RawEmojiIconWidget(
|
||||
emoji: iconData,
|
||||
emojiSize: 16.0,
|
||||
lineHeight: 18.0 / 16.0,
|
||||
)
|
||||
: Opacity(opacity: 0.6, child: widget.view.defaultIcon());
|
||||
|
||||
final Widget child = AppFlowyPopover(
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_p
|
|||
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/custom_paste_command.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/math_equation/math_equation_shortcut.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/toggle/toggle_block_shortcuts.dart';
|
||||
import 'package:appflowy/shared/error_page/error_page.dart';
|
||||
import 'package:appflowy/workspace/application/settings/shortcuts/settings_shortcuts_cubit.dart';
|
||||
import 'package:appflowy/workspace/application/settings/shortcuts/settings_shortcuts_service.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/shared_widget.dart';
|
||||
|
@ -21,7 +22,6 @@ import 'package:flowy_infra/size.dart';
|
|||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
|
|
@ -54,7 +54,7 @@ class SettingsPageSitesEvent {
|
|||
getIt<ClipboardService>().setData(ClipboardServiceData(plainText: url));
|
||||
showToastNotification(
|
||||
context,
|
||||
message: LocaleKeys.grid_url_copy.tr(),
|
||||
message: LocaleKeys.message_copy_success.tr(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:appflowy/env/cloud_env.dart';
|
|||
import 'package:appflowy/env/env.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/shared/share/constants.dart';
|
||||
import 'package:appflowy/shared/error_page/error_page.dart';
|
||||
import 'package:appflowy/workspace/application/settings/appflowy_cloud_setting_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/settings/appflowy_cloud_urls_bloc.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/_restart_app_button.dart';
|
||||
|
@ -19,7 +20,6 @@ import 'package:easy_localization/easy_localization.dart';
|
|||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -130,7 +130,7 @@ class CustomAppFlowyCloudView extends StatelessWidget {
|
|||
final List<Widget> children = [];
|
||||
children.addAll([
|
||||
const AppFlowyCloudEnableSync(),
|
||||
const AppFlowyCloudSyncLogEnabled(),
|
||||
// const AppFlowyCloudSyncLogEnabled(),
|
||||
const VSpace(40),
|
||||
]);
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import 'package:appflowy/core/helpers/url_launcher.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/shared/error_page/error_page.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/theme_upload/theme_upload_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/widget/buttons/secondary_button.dart';
|
||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ThemeUploadLearnMoreButton extends StatelessWidget {
|
||||
|
|
|
@ -48,6 +48,8 @@ String languageFromLocale(Locale locale) {
|
|||
default:
|
||||
return locale.languageCode;
|
||||
}
|
||||
case "mr":
|
||||
return "मराठी";
|
||||
case "he":
|
||||
return "עברית";
|
||||
case "hu":
|
||||
|
@ -79,7 +81,7 @@ String languageFromLocale(Locale locale) {
|
|||
case "ur":
|
||||
return "اردو";
|
||||
case "hin":
|
||||
return "हिन्दी";
|
||||
return "हिन्दी";
|
||||
}
|
||||
// If not found then the language code will be displayed
|
||||
return locale.languageCode;
|
||||
|
|
|
@ -311,14 +311,16 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "الاختصارات",
|
||||
"whatsNew": "ما هو الجديد؟",
|
||||
"help": "المساعدة والدعم",
|
||||
"helpAndDocumentation": "المساعدة والتوثيق",
|
||||
"getSupport": "احصل على الدعم",
|
||||
"markdown": "Markdown",
|
||||
"debug": {
|
||||
"name": "معلومات التصحيح",
|
||||
"success": "تم نسخ معلومات التصحيح إلى الحافظة!",
|
||||
"fail": "تعذر نسخ معلومات التصحيح إلى الحافظة"
|
||||
},
|
||||
"feedback": "تعليق"
|
||||
"feedback": "تعليق",
|
||||
"help": "المساعدة والدعم"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "إزالة وإعادة تسمية والمزيد...",
|
||||
|
@ -513,6 +515,8 @@
|
|||
"settings": "إعدادات",
|
||||
"members": "الأعضاء",
|
||||
"trash": "سلة المحذوفات",
|
||||
"helpAndDocumentation": "المساعدة والتوثيق",
|
||||
"getSupport": "احصل على الدعم",
|
||||
"helpAndSupport": "المساعدة والدعم"
|
||||
},
|
||||
"sites": {
|
||||
|
@ -1672,8 +1676,7 @@
|
|||
"url": {
|
||||
"launch": "فتح في المتصفح",
|
||||
"copy": "إنسخ الرابط",
|
||||
"textFieldHint": "أدخل عنوان URL",
|
||||
"copiedNotification": "تمت نسخها إلى الحافظة!"
|
||||
"textFieldHint": "أدخل عنوان URL"
|
||||
},
|
||||
"relation": {
|
||||
"relatedDatabasePlaceLabel": "قاعدة البيانات ذات الصلة",
|
||||
|
|
|
@ -133,14 +133,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "Dreceres",
|
||||
"whatsNew": "Què hi ha de nou?",
|
||||
"help": "Ajuda i Suport",
|
||||
"markdown": "Reducció",
|
||||
"debug": {
|
||||
"name": "Informació de depuració",
|
||||
"success": "S'ha copiat la informació de depuració!",
|
||||
"fail": "No es pot copiar la informació de depuració"
|
||||
},
|
||||
"feedback": "Feedback"
|
||||
"feedback": "Feedback",
|
||||
"help": "Ajuda i Suport"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "Suprimeix, canvia el nom i més...",
|
||||
|
|
|
@ -170,14 +170,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "کورتە ڕێگاکان",
|
||||
"whatsNew": "نوێترین",
|
||||
"help": "پشتیوانی و یارمەتی",
|
||||
"markdown": "Markdown",
|
||||
"debug": {
|
||||
"name": "زانیاری دیباگ",
|
||||
"success": "زانیارییەکانی دیباگ کۆپی کراون بۆ کلیپبۆرد!",
|
||||
"fail": "ناتوانرێت زانیارییەکانی دیباگ کۆپی بکات بۆ کلیپبۆرد"
|
||||
},
|
||||
"feedback": "فیدباک"
|
||||
"feedback": "فیدباک",
|
||||
"help": "پشتیوانی و یارمەتی"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "سڕینەوە، گۆڕینی ناو، و زۆر شتی تر...",
|
||||
|
|
|
@ -134,14 +134,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "Klávesové zkratky",
|
||||
"whatsNew": "Co je nového?",
|
||||
"help": "Pomoc a podpora",
|
||||
"markdown": "Markdown",
|
||||
"debug": {
|
||||
"name": "Debug informace",
|
||||
"success": "Debug informace zkopírovány do schránky!",
|
||||
"fail": "Nepodařilo se zkopáí"
|
||||
},
|
||||
"feedback": "Zpětná vazba"
|
||||
"feedback": "Zpětná vazba",
|
||||
"help": "Pomoc a podpora"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "Smazat, přejmenovat, a další...",
|
||||
|
|
|
@ -252,14 +252,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "Tastenkürzel",
|
||||
"whatsNew": "Was gibt es Neues?",
|
||||
"help": "Hilfe & Support",
|
||||
"markdown": "Markdown",
|
||||
"debug": {
|
||||
"name": "Debug-Informationen",
|
||||
"success": "Debug-Informationen in die Zwischenablage kopiert!",
|
||||
"fail": "Debug-Informationen konnten nicht in die Zwischenablage kopiert werden"
|
||||
},
|
||||
"feedback": "Feedback"
|
||||
"feedback": "Feedback",
|
||||
"help": "Hilfe & Support"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "Entfernen, umbenennen und mehr...",
|
||||
|
@ -1625,8 +1625,7 @@
|
|||
"url": {
|
||||
"launch": "Im Browser öffnen",
|
||||
"copy": "Webadresse kopieren",
|
||||
"textFieldHint": "Gebe eine URL ein",
|
||||
"copiedNotification": "In die Zwischenablage kopiert!"
|
||||
"textFieldHint": "Gebe eine URL ein"
|
||||
},
|
||||
"relation": {
|
||||
"relatedDatabasePlaceLabel": "Verwandte Datenbank",
|
||||
|
|
|
@ -723,9 +723,8 @@
|
|||
},
|
||||
"url": {
|
||||
"launch": "Άνοιγμα συνδέσμου στο πρόγραμμα περιήγησης",
|
||||
"copy": "Copied link to clipboard",
|
||||
"textFieldHint": "Enter a URL",
|
||||
"copiedNotification": "Copied to clipboard!"
|
||||
"copy": "Copy link to clipboard",
|
||||
"textFieldHint": "Enter a URL"
|
||||
},
|
||||
"relation": {
|
||||
"relatedDatabasePlaceLabel": "Related Database",
|
||||
|
|
|
@ -1647,9 +1647,8 @@
|
|||
},
|
||||
"url": {
|
||||
"launch": "Open link in browser",
|
||||
"copy": "Copied link to clipboard",
|
||||
"textFieldHint": "Enter a URL",
|
||||
"copiedNotification": "Copied to clipboard!"
|
||||
"copy": "Copy link to clipboard",
|
||||
"textFieldHint": "Enter a URL"
|
||||
},
|
||||
"relation": {
|
||||
"relatedDatabasePlaceLabel": "Related Database",
|
||||
|
@ -2294,7 +2293,7 @@
|
|||
},
|
||||
"message": {
|
||||
"copy": {
|
||||
"success": "Copied!",
|
||||
"success": "Copied to clipboard",
|
||||
"fail": "Unable to copy"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -218,14 +218,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "Atajos",
|
||||
"whatsNew": "¿Qué hay de nuevo?",
|
||||
"help": "Ayuda y Soporte",
|
||||
"markdown": "Reducción",
|
||||
"debug": {
|
||||
"name": "Información de depuración",
|
||||
"success": "¡Información copiada!",
|
||||
"fail": "No fue posible copiar la información"
|
||||
},
|
||||
"feedback": "Comentario"
|
||||
"feedback": "Comentario",
|
||||
"help": "Ayuda y Soporte"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "Eliminar, renombrar y más...",
|
||||
|
@ -866,8 +866,7 @@
|
|||
"url": {
|
||||
"launch": "Abrir en el navegador",
|
||||
"copy": "Copiar URL",
|
||||
"textFieldHint": "Introduce una URL",
|
||||
"copiedNotification": "¡Copiado al portapapeles!"
|
||||
"textFieldHint": "Introduce una URL"
|
||||
},
|
||||
"relation": {
|
||||
"relatedDatabasePlaceLabel": "Base de datos relacionada",
|
||||
|
|
|
@ -99,14 +99,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "Lasterbideak",
|
||||
"whatsNew": "Ze berri?",
|
||||
"help": "Laguntza",
|
||||
"markdown": "Markdown",
|
||||
"debug": {
|
||||
"name": "Debug informazioa",
|
||||
"success": "Debug informazioa kopiatu da!",
|
||||
"fail": "Ezin izan da debug informazioa kopiatu"
|
||||
},
|
||||
"feedback": "Iritzia"
|
||||
"feedback": "Iritzia",
|
||||
"help": "Laguntza"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"addPageTooltip": "Gehitu orri bat",
|
||||
|
|
|
@ -139,14 +139,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "میانبرها",
|
||||
"whatsNew": "تازهترینها",
|
||||
"help": "پشتیبانی و مستندات",
|
||||
"markdown": "Markdown",
|
||||
"debug": {
|
||||
"name": "اطلاعات اشکالزدایی",
|
||||
"success": "طلاعات اشکال زدایی در کلیپ بورد کپی شد!",
|
||||
"fail": "نمی توان اطلاعات اشکال زدایی را در کلیپ بورد کپی کرد"
|
||||
},
|
||||
"feedback": "بازخورد"
|
||||
"feedback": "بازخورد",
|
||||
"help": "پشتیبانی و مستندات"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "حذف، تغییر نام، و موارد دیگر...",
|
||||
|
|
|
@ -196,14 +196,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "Raccourcis",
|
||||
"whatsNew": "Nouveautés",
|
||||
"help": "Aide et Support Technique",
|
||||
"markdown": "Réduction",
|
||||
"debug": {
|
||||
"name": "Infos du système",
|
||||
"success": "Informations de débogage copiées dans le presse-papiers !",
|
||||
"fail": "Impossible de copier les informations de débogage dans le presse-papiers"
|
||||
},
|
||||
"feedback": "Retour"
|
||||
"feedback": "Retour",
|
||||
"help": "Aide et Support Technique"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "Supprimer, renommer et plus...",
|
||||
|
|
|
@ -269,14 +269,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "Raccourcis",
|
||||
"whatsNew": "Nouveautés",
|
||||
"help": "Aide et Support",
|
||||
"markdown": "Rédaction",
|
||||
"debug": {
|
||||
"name": "Informations de Débogage",
|
||||
"success": "Informations de débogage copiées dans le presse-papiers !",
|
||||
"fail": "Impossible de copier les informations de débogage dans le presse-papiers"
|
||||
},
|
||||
"feedback": "Retour"
|
||||
"feedback": "Retour",
|
||||
"help": "Aide et Support"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "Supprimer, renommer et plus...",
|
||||
|
@ -1612,8 +1612,7 @@
|
|||
"url": {
|
||||
"launch": "Ouvrir dans le navigateur",
|
||||
"copy": "Copier l'URL",
|
||||
"textFieldHint": "Entrez une URL",
|
||||
"copiedNotification": "Copié dans le presse-papier!"
|
||||
"textFieldHint": "Entrez une URL"
|
||||
},
|
||||
"relation": {
|
||||
"relatedDatabasePlaceLabel": "Base de données associée",
|
||||
|
|
|
@ -206,14 +206,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "מקשי קיצור",
|
||||
"whatsNew": "מה חדש?",
|
||||
"help": "עזרה ותמיכה",
|
||||
"markdown": "Markdown",
|
||||
"debug": {
|
||||
"name": "פרטי ניפוי שגיאות",
|
||||
"success": "פרטי ניפוי השגיאות הועתקו ללוח הגזירים!",
|
||||
"fail": "לא ניתן להעתיק את פרטי ניפוי השגיאות ללוח הגזירים"
|
||||
},
|
||||
"feedback": "משוב"
|
||||
"feedback": "משוב",
|
||||
"help": "עזרה ותמיכה"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "הסרה, שינוי שם ועוד…",
|
||||
|
@ -1243,8 +1243,7 @@
|
|||
"url": {
|
||||
"launch": "פתיחת קישור בדפדפן",
|
||||
"copy": "העתקת קישור ללוח הגזירים",
|
||||
"textFieldHint": "נא למלא כתובת",
|
||||
"copiedNotification": "הועתק ללוח הגזירים!"
|
||||
"textFieldHint": "נא למלא כתובת"
|
||||
},
|
||||
"relation": {
|
||||
"relatedDatabasePlaceLabel": "מסד נתונים קשור",
|
||||
|
|
|
@ -103,14 +103,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "Parancsikonok",
|
||||
"whatsNew": "Újdonságok",
|
||||
"help": "Segítség & Támogatás",
|
||||
"markdown": "Markdown",
|
||||
"debug": {
|
||||
"name": "Debug Információ",
|
||||
"success": "Debug információ a vágólapra másolva",
|
||||
"fail": "A Debug információ nem másolható a vágólapra"
|
||||
},
|
||||
"feedback": "Visszacsatolás"
|
||||
"feedback": "Visszacsatolás",
|
||||
"help": "Segítség & Támogatás"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"addPageTooltip": "Belső oldal hozzáadása",
|
||||
|
|
|
@ -160,14 +160,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "Pintasan",
|
||||
"whatsNew": "Apa yang baru?",
|
||||
"help": "Bantuan & Dukungan",
|
||||
"markdown": "Penurunan harga",
|
||||
"debug": {
|
||||
"name": "Info debug",
|
||||
"success": "Info debug disalin ke papan klip!",
|
||||
"fail": "Tidak dapat menyalin info debug ke papan klip"
|
||||
},
|
||||
"feedback": "Masukan"
|
||||
"feedback": "Masukan",
|
||||
"help": "Bantuan & Dukungan"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "Menghapus, merubah nama, dan banyak lagi...",
|
||||
|
|
|
@ -221,14 +221,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "Scorciatoie",
|
||||
"whatsNew": "Cosa c'è di nuovo?",
|
||||
"help": "Aiuto & Supporto",
|
||||
"markdown": "Markdown",
|
||||
"debug": {
|
||||
"name": "Informazioni di debug",
|
||||
"success": "Informazioni di debug copiate negli appunti!",
|
||||
"fail": "Impossibile copiare le informazioni di debug negli appunti"
|
||||
},
|
||||
"feedback": "Feedback"
|
||||
"feedback": "Feedback",
|
||||
"help": "Aiuto & Supporto"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "Rimuovi, rinomina e altro...",
|
||||
|
|
|
@ -263,14 +263,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "ショートカット",
|
||||
"whatsNew": "新着情報",
|
||||
"help": "ヘルプ & サポート",
|
||||
"markdown": "Markdown",
|
||||
"debug": {
|
||||
"name": "デバッグ情報",
|
||||
"success": "デバッグ情報をクリップボードにコピーしました!",
|
||||
"fail": "デバッグ情報をクリップボードにコピーできませんでした"
|
||||
},
|
||||
"feedback": "フィードバック"
|
||||
"feedback": "フィードバック",
|
||||
"help": "ヘルプ & サポート"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "削除、名前の変更、その他...",
|
||||
|
@ -1577,8 +1577,7 @@
|
|||
"url": {
|
||||
"launch": "リンクをブラウザで開く",
|
||||
"copy": "リンクをクリップボードにコピー",
|
||||
"textFieldHint": "URLを入力",
|
||||
"copiedNotification": "クリップボードにコピーされました!"
|
||||
"textFieldHint": "URLを入力"
|
||||
},
|
||||
"relation": {
|
||||
"relatedDatabasePlaceLabel": "関連データベース",
|
||||
|
|
|
@ -301,14 +301,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "단축키",
|
||||
"whatsNew": "새로운 기능",
|
||||
"help": "도움말 및 지원",
|
||||
"markdown": "Markdown",
|
||||
"debug": {
|
||||
"name": "디버그 정보",
|
||||
"success": "디버그 정보를 클립보드에 복사했습니다!",
|
||||
"fail": "디버그 정보를 클립보드에 복사할 수 없습니다"
|
||||
},
|
||||
"feedback": "피드백"
|
||||
"feedback": "피드백",
|
||||
"help": "도움말 및 지원"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "제거, 이름 변경 등...",
|
||||
|
@ -1634,8 +1634,7 @@
|
|||
"url": {
|
||||
"launch": "브라우저에서 링크 열기",
|
||||
"copy": "링크를 클립보드에 복사",
|
||||
"textFieldHint": "URL 입력",
|
||||
"copiedNotification": "클립보드에 복사되었습니다!"
|
||||
"textFieldHint": "URL 입력"
|
||||
},
|
||||
"relation": {
|
||||
"relatedDatabasePlaceLabel": "관련 데이터베이스",
|
||||
|
|
3210
frontend/resources/translations/mr-IN.json
Normal file
3210
frontend/resources/translations/mr-IN.json
Normal file
File diff suppressed because it is too large
Load diff
|
@ -164,14 +164,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "Skróty",
|
||||
"whatsNew": "Co nowego?",
|
||||
"help": "Pomoc & Wsparcie",
|
||||
"markdown": "Markdown",
|
||||
"debug": {
|
||||
"name": "Informacje Debugowania",
|
||||
"success": "Skopiowano informacje debugowania do schowka!",
|
||||
"fail": "Nie mozna skopiować informacji debugowania do schowka"
|
||||
},
|
||||
"feedback": "Feedback"
|
||||
"feedback": "Feedback",
|
||||
"help": "Pomoc & Wsparcie"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "Usuń, zmień nazwę i więcej...",
|
||||
|
|
|
@ -225,14 +225,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "Atalhos",
|
||||
"whatsNew": "O que há de novo?",
|
||||
"help": "Ajuda e Suporte",
|
||||
"markdown": "Remarcação",
|
||||
"debug": {
|
||||
"name": "Informação de depuração",
|
||||
"success": "Informação de depuração copiada para a área de transferência!",
|
||||
"fail": "Falha ao copiar a informação de depuração para a área de transferência"
|
||||
},
|
||||
"feedback": "Opinião"
|
||||
"feedback": "Opinião",
|
||||
"help": "Ajuda e Suporte"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "Remover, renomear e muito mais...",
|
||||
|
|
|
@ -128,14 +128,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "Atalhos",
|
||||
"whatsNew": "O que há de novo?",
|
||||
"help": "Ajuda & Suporte",
|
||||
"markdown": "Remarcação",
|
||||
"debug": {
|
||||
"name": "Informação de depuração",
|
||||
"success": "Copiar informação de depuração para o clipboard!",
|
||||
"fail": "Falha em copiar a informação de depuração para o clipboard"
|
||||
},
|
||||
"feedback": "Opinião"
|
||||
"feedback": "Opinião",
|
||||
"help": "Ajuda & Suporte"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "Remover, renomear e muito mais...",
|
||||
|
|
|
@ -251,14 +251,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "Горячие клавиши",
|
||||
"whatsNew": "Что нового?",
|
||||
"help": "Помощь и поддержка",
|
||||
"markdown": "Markdown",
|
||||
"debug": {
|
||||
"name": "Отладочная информация",
|
||||
"success": "Отладочная информация скопирована в буфер обмена!",
|
||||
"fail": "Не удалось скопировать отладочную информацию в буфер обмена"
|
||||
},
|
||||
"feedback": "Обратная связь"
|
||||
"feedback": "Обратная связь",
|
||||
"help": "Помощь и поддержка"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "Удалить, переименовать и другие действия...",
|
||||
|
@ -1420,8 +1420,7 @@
|
|||
"url": {
|
||||
"launch": "Открыть в браузере",
|
||||
"copy": "Скопировать URL",
|
||||
"textFieldHint": "Введите URL-адрес",
|
||||
"copiedNotification": "Скопировано в буфер обмена!"
|
||||
"textFieldHint": "Введите URL-адрес"
|
||||
},
|
||||
"relation": {
|
||||
"relatedDatabasePlaceLabel": "Связанная база данных",
|
||||
|
|
|
@ -107,14 +107,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "Genvägar",
|
||||
"whatsNew": "Vad nytt?",
|
||||
"help": "Hjälp & Support",
|
||||
"markdown": "Prissänkning",
|
||||
"debug": {
|
||||
"name": "Felsökningsinfo",
|
||||
"success": "Kopierade felsökningsinfo till urklipp!",
|
||||
"fail": "Kunde inte kopiera felsökningsinfo till urklipp"
|
||||
},
|
||||
"feedback": "Återkoppling"
|
||||
"feedback": "Återkoppling",
|
||||
"help": "Hjälp & Support"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"addPageTooltip": "Lägg till en underliggande sida",
|
||||
|
|
|
@ -250,14 +250,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "ทางลัด",
|
||||
"whatsNew": "มีอะไรใหม่?",
|
||||
"help": "ช่วยเหลือและสนับสนุน",
|
||||
"markdown": "Markdown",
|
||||
"debug": {
|
||||
"name": "ข้อมูลดีบัก",
|
||||
"success": "คัดลอกข้อมูลดีบักไปยังคลิปบอร์ดแล้ว!",
|
||||
"fail": "ไม่สามารถคัดลอกข้อมูลดีบักไปยังคลิปบอร์ด"
|
||||
},
|
||||
"feedback": "ข้อเสนอแนะ"
|
||||
"feedback": "ข้อเสนอแนะ",
|
||||
"help": "ช่วยเหลือและสนับสนุน"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "ลบ เปลี่ยนชื่อ และอื่นๆ...",
|
||||
|
@ -1570,8 +1570,7 @@
|
|||
"url": {
|
||||
"launch": "เปิดในเบราว์เซอร์",
|
||||
"copy": "คัดลอก URL",
|
||||
"textFieldHint": "ป้อน URL",
|
||||
"copiedNotification": "คัดลอกไปยังคลิปบอร์ดแล้ว!"
|
||||
"textFieldHint": "ป้อน URL"
|
||||
},
|
||||
"relation": {
|
||||
"relatedDatabasePlaceLabel": "ฐานข้อมูลที่เกี่ยวข้อง",
|
||||
|
|
|
@ -288,14 +288,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "Kısayollar",
|
||||
"whatsNew": "Yenilikler",
|
||||
"help": "Yardım ve Destek",
|
||||
"markdown": "Markdown",
|
||||
"debug": {
|
||||
"name": "Hata Ayıklama Bilgisi",
|
||||
"success": "Hata ayıklama bilgisi panoya kopyalandı!",
|
||||
"fail": "Hata ayıklama bilgisi panoya kopyalanamadı"
|
||||
},
|
||||
"feedback": "Geri Bildirim"
|
||||
"feedback": "Geri Bildirim",
|
||||
"help": "Yardım ve Destek"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "Kaldır, yeniden adlandır ve daha fazlası...",
|
||||
|
@ -1608,8 +1608,7 @@
|
|||
"url": {
|
||||
"launch": "Bağlantıyı tarayıcıda aç",
|
||||
"copy": "Bağlantıyı panoya kopyala",
|
||||
"textFieldHint": "Bir URL girin",
|
||||
"copiedNotification": "Panoya kopyalandı!"
|
||||
"textFieldHint": "Bir URL girin"
|
||||
},
|
||||
"relation": {
|
||||
"relatedDatabasePlaceLabel": "İlişkili Veritabanı",
|
||||
|
|
|
@ -225,14 +225,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "Комбінації клавіш",
|
||||
"whatsNew": "Що нового?",
|
||||
"help": "Довідка та підтримка",
|
||||
"markdown": "Markdown",
|
||||
"debug": {
|
||||
"name": "Інформація для налагодження",
|
||||
"success": "Інформацію для налагодження скопійовано в буфер обміну!",
|
||||
"fail": "Не вдалося скопіювати інформацію для налагодження в буфер обміну"
|
||||
},
|
||||
"feedback": "Зворотний зв'язок"
|
||||
"feedback": "Зворотний зв'язок",
|
||||
"help": "Довідка та підтримка"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "Видалити, перейменувати та інше...",
|
||||
|
@ -1445,8 +1445,7 @@
|
|||
"url": {
|
||||
"launch": "Відкрити посилання в браузері",
|
||||
"copy": "Копіювати посилання в буфер обміну",
|
||||
"textFieldHint": "Введіть URL",
|
||||
"copiedNotification": "Скопійовано в буфер обміну!"
|
||||
"textFieldHint": "Введіть URL"
|
||||
},
|
||||
"relation": {
|
||||
"relatedDatabasePlaceLabel": "Пов'язана база даних",
|
||||
|
|
|
@ -226,14 +226,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "Phím tắt",
|
||||
"whatsNew": "Có gì mới?",
|
||||
"help": "Trợ giúp & Hỗ trợ",
|
||||
"markdown": "Markdown",
|
||||
"debug": {
|
||||
"name": "Thông tin gỡ lỗi",
|
||||
"success": "Đã sao chép thông tin gỡ lỗi vào khay nhớ tạm!",
|
||||
"fail": "Không thể sao chép thông tin gỡ lỗi vào khay nhớ tạm"
|
||||
},
|
||||
"feedback": "Nhận xét"
|
||||
"feedback": "Nhận xét",
|
||||
"help": "Trợ giúp & Hỗ trợ"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "Xóa, đổi tên và hơn thế nữa...",
|
||||
|
@ -1439,8 +1439,7 @@
|
|||
"url": {
|
||||
"launch": "Mở liên kết trong trình duyệt",
|
||||
"copy": "Sao chép URL",
|
||||
"textFieldHint": "Nhập một URL",
|
||||
"copiedNotification": "Đã sao chép vào bảng tạm!"
|
||||
"textFieldHint": "Nhập một URL"
|
||||
},
|
||||
"relation": {
|
||||
"relatedDatabasePlaceLabel": "Cơ sở dữ liệu liên quan",
|
||||
|
|
|
@ -270,14 +270,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "快捷键",
|
||||
"whatsNew": "新功能",
|
||||
"help": "帮助和支持",
|
||||
"markdown": "Markdown",
|
||||
"debug": {
|
||||
"name": "调试信息",
|
||||
"success": "将调试信息复制到剪贴板!",
|
||||
"fail": "无法将调试信息复制到剪贴板"
|
||||
},
|
||||
"feedback": "反馈"
|
||||
"feedback": "反馈",
|
||||
"help": "帮助和支持"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "删除、重命名等等...",
|
||||
|
@ -1270,8 +1270,7 @@
|
|||
"url": {
|
||||
"launch": "在浏览器中打开链接",
|
||||
"copy": "将链接复制到剪贴板",
|
||||
"textFieldHint": "输入 URL",
|
||||
"copiedNotification": "已复制到剪贴板!"
|
||||
"textFieldHint": "输入 URL"
|
||||
},
|
||||
"relation": {
|
||||
"rowSearchTextFieldPlaceholder": "搜索"
|
||||
|
|
|
@ -216,14 +216,14 @@
|
|||
"questionBubble": {
|
||||
"shortcuts": "快捷鍵",
|
||||
"whatsNew": "有什麼新功能?",
|
||||
"help": "幫助 & 支援",
|
||||
"markdown": "Markdown",
|
||||
"debug": {
|
||||
"name": "除錯資訊",
|
||||
"success": "已將除錯資訊複製到剪貼簿!",
|
||||
"fail": "無法將除錯資訊複製到剪貼簿"
|
||||
},
|
||||
"feedback": "意見回饋"
|
||||
"feedback": "意見回饋",
|
||||
"help": "幫助 & 支援"
|
||||
},
|
||||
"menuAppHeader": {
|
||||
"moreButtonToolTip": "移除、重新命名等等...",
|
||||
|
@ -838,8 +838,7 @@
|
|||
"url": {
|
||||
"launch": "在瀏覽器中開啟",
|
||||
"copy": "複製網址",
|
||||
"textFieldHint": "輸入網址",
|
||||
"copiedNotification": "已複製到剪貼簿"
|
||||
"textFieldHint": "輸入網址"
|
||||
},
|
||||
"menuName": "網格",
|
||||
"referencedGridPrefix": "檢視",
|
||||
|
|
92
frontend/rust-lib/Cargo.lock
generated
92
frontend/rust-lib/Cargo.lock
generated
|
@ -496,7 +496,7 @@ checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
|
|||
[[package]]
|
||||
name = "app-error"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
@ -516,16 +516,16 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "appflowy-ai-client"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"futures",
|
||||
"pin-project",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"thiserror 1.0.64",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1137,7 +1137,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "client-api"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
|
||||
dependencies = [
|
||||
"again",
|
||||
"anyhow",
|
||||
|
@ -1192,7 +1192,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "client-api-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
|
||||
dependencies = [
|
||||
"collab-entity",
|
||||
"collab-rt-entity",
|
||||
|
@ -1205,7 +1205,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "client-websocket"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
|
@ -1248,7 +1248,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "collab"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=80d1c6147d1139289c2eaadab40557cc86c0f4b6#80d1c6147d1139289c2eaadab40557cc86c0f4b6"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4e717c2c6a15c42feda9e1ff1b122c7b0baf1821#4e717c2c6a15c42feda9e1ff1b122c7b0baf1821"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
|
@ -1273,7 +1273,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "collab-database"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=80d1c6147d1139289c2eaadab40557cc86c0f4b6#80d1c6147d1139289c2eaadab40557cc86c0f4b6"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4e717c2c6a15c42feda9e1ff1b122c7b0baf1821#4e717c2c6a15c42feda9e1ff1b122c7b0baf1821"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
@ -1313,7 +1313,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "collab-document"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=80d1c6147d1139289c2eaadab40557cc86c0f4b6#80d1c6147d1139289c2eaadab40557cc86c0f4b6"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4e717c2c6a15c42feda9e1ff1b122c7b0baf1821#4e717c2c6a15c42feda9e1ff1b122c7b0baf1821"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
|
@ -1334,7 +1334,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "collab-entity"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=80d1c6147d1139289c2eaadab40557cc86c0f4b6#80d1c6147d1139289c2eaadab40557cc86c0f4b6"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4e717c2c6a15c42feda9e1ff1b122c7b0baf1821#4e717c2c6a15c42feda9e1ff1b122c7b0baf1821"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
|
@ -1354,7 +1354,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "collab-folder"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=80d1c6147d1139289c2eaadab40557cc86c0f4b6#80d1c6147d1139289c2eaadab40557cc86c0f4b6"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4e717c2c6a15c42feda9e1ff1b122c7b0baf1821#4e717c2c6a15c42feda9e1ff1b122c7b0baf1821"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
|
@ -1376,7 +1376,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "collab-importer"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=80d1c6147d1139289c2eaadab40557cc86c0f4b6#80d1c6147d1139289c2eaadab40557cc86c0f4b6"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4e717c2c6a15c42feda9e1ff1b122c7b0baf1821#4e717c2c6a15c42feda9e1ff1b122c7b0baf1821"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
|
@ -1418,7 +1418,6 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
"collab",
|
||||
"collab-database",
|
||||
"collab-document",
|
||||
|
@ -1429,18 +1428,18 @@ dependencies = [
|
|||
"diesel",
|
||||
"flowy-error",
|
||||
"flowy-sqlite",
|
||||
"futures",
|
||||
"lib-infra",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "collab-plugins"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=80d1c6147d1139289c2eaadab40557cc86c0f4b6#80d1c6147d1139289c2eaadab40557cc86c0f4b6"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4e717c2c6a15c42feda9e1ff1b122c7b0baf1821#4e717c2c6a15c42feda9e1ff1b122c7b0baf1821"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
|
@ -1478,7 +1477,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "collab-rt-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
@ -1500,7 +1499,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "collab-rt-protocol"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
@ -1511,13 +1510,14 @@ dependencies = [
|
|||
"thiserror 1.0.64",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"uuid",
|
||||
"yrs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "collab-user"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=80d1c6147d1139289c2eaadab40557cc86c0f4b6#80d1c6147d1139289c2eaadab40557cc86c0f4b6"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=4e717c2c6a15c42feda9e1ff1b122c7b0baf1821#4e717c2c6a15c42feda9e1ff1b122c7b0baf1821"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
|
@ -1764,7 +1764,7 @@ dependencies = [
|
|||
"cssparser-macros",
|
||||
"dtoa-short",
|
||||
"itoa",
|
||||
"phf 0.11.2",
|
||||
"phf 0.8.0",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
|
@ -1947,7 +1947,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
|||
[[package]]
|
||||
name = "database-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bytes",
|
||||
|
@ -2513,13 +2513,13 @@ dependencies = [
|
|||
name = "flowy-ai-pub"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"client-api",
|
||||
"flowy-error",
|
||||
"futures",
|
||||
"lib-infra",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2592,7 +2592,6 @@ dependencies = [
|
|||
"flowy-storage-pub",
|
||||
"flowy-user",
|
||||
"flowy-user-pub",
|
||||
"futures",
|
||||
"futures-core",
|
||||
"lib-dispatch",
|
||||
"lib-infra",
|
||||
|
@ -2605,20 +2604,20 @@ dependencies = [
|
|||
"tokio",
|
||||
"tokio-stream",
|
||||
"tracing",
|
||||
"url",
|
||||
"uuid",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flowy-database-pub"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"client-api",
|
||||
"collab",
|
||||
"collab-entity",
|
||||
"flowy-error",
|
||||
"lib-infra",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2667,6 +2666,7 @@ dependencies = [
|
|||
"tokio-util",
|
||||
"tracing",
|
||||
"url",
|
||||
"uuid",
|
||||
"validator 0.18.1",
|
||||
]
|
||||
|
||||
|
@ -2744,11 +2744,11 @@ dependencies = [
|
|||
name = "flowy-document-pub"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
"collab-document",
|
||||
"flowy-error",
|
||||
"lib-infra",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2778,6 +2778,7 @@ dependencies = [
|
|||
"thiserror 1.0.64",
|
||||
"tokio",
|
||||
"url",
|
||||
"uuid",
|
||||
"validator 0.18.1",
|
||||
]
|
||||
|
||||
|
@ -2864,16 +2865,12 @@ dependencies = [
|
|||
"bytes",
|
||||
"collab",
|
||||
"collab-folder",
|
||||
"diesel",
|
||||
"diesel_derives",
|
||||
"diesel_migrations",
|
||||
"flowy-codegen",
|
||||
"flowy-derive",
|
||||
"flowy-error",
|
||||
"flowy-folder",
|
||||
"flowy-notification",
|
||||
"flowy-search-pub",
|
||||
"flowy-sqlite",
|
||||
"flowy-user",
|
||||
"futures",
|
||||
"lib-dispatch",
|
||||
|
@ -2887,7 +2884,7 @@ dependencies = [
|
|||
"tempfile",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"validator 0.18.1",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2898,8 +2895,8 @@ dependencies = [
|
|||
"collab",
|
||||
"collab-folder",
|
||||
"flowy-error",
|
||||
"futures",
|
||||
"lib-infra",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2991,7 +2988,6 @@ name = "flowy-storage"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"allo-isolate",
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"chrono",
|
||||
|
@ -3003,8 +2999,6 @@ dependencies = [
|
|||
"flowy-notification",
|
||||
"flowy-sqlite",
|
||||
"flowy-storage-pub",
|
||||
"futures-util",
|
||||
"fxhash",
|
||||
"lib-dispatch",
|
||||
"lib-infra",
|
||||
"mime_guess",
|
||||
|
@ -3032,9 +3026,8 @@ dependencies = [
|
|||
"mime",
|
||||
"mime_guess",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3057,7 +3050,6 @@ dependencies = [
|
|||
"collab-user",
|
||||
"dashmap 6.0.1",
|
||||
"diesel",
|
||||
"diesel_derives",
|
||||
"fake",
|
||||
"fancy-regex 0.11.0",
|
||||
"flowy-codegen",
|
||||
|
@ -3072,7 +3064,6 @@ dependencies = [
|
|||
"lib-dispatch",
|
||||
"lib-infra",
|
||||
"nanoid",
|
||||
"once_cell",
|
||||
"protobuf",
|
||||
"quickcheck",
|
||||
"quickcheck_macros",
|
||||
|
@ -3082,7 +3073,6 @@ dependencies = [
|
|||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"strum",
|
||||
"strum_macros 0.25.2",
|
||||
"tokio",
|
||||
|
@ -3432,7 +3422,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "gotrue"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"getrandom 0.2.10",
|
||||
|
@ -3447,7 +3437,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "gotrue-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
|
||||
dependencies = [
|
||||
"app-error",
|
||||
"jsonwebtoken",
|
||||
|
@ -4068,7 +4058,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "infra"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
|
@ -5181,7 +5171,7 @@ version = "0.8.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
||||
dependencies = [
|
||||
"phf_macros 0.8.0",
|
||||
"phf_macros",
|
||||
"phf_shared 0.8.0",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
@ -5201,7 +5191,6 @@ version = "0.11.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||
dependencies = [
|
||||
"phf_macros 0.11.3",
|
||||
"phf_shared 0.11.2",
|
||||
]
|
||||
|
||||
|
@ -5269,19 +5258,6 @@ dependencies = [
|
|||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_macros"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216"
|
||||
dependencies = [
|
||||
"phf_generator 0.11.2",
|
||||
"phf_shared 0.11.2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.94",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
|
@ -6782,7 +6758,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "shared-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=f7288f46c27dc8e3c7829cda1b70b61118e88336#f7288f46c27dc8e3c7829cda1b70b61118e88336"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e#2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
|
|
|
@ -103,8 +103,8 @@ dashmap = "6.0.1"
|
|||
# Run the script.add_workspace_members:
|
||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||
# ⚠️⚠️⚠️️
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "f7288f46c27dc8e3c7829cda1b70b61118e88336" }
|
||||
client-api-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "f7288f46c27dc8e3c7829cda1b70b61118e88336" }
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e" }
|
||||
client-api-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "2922db6801ca23c5fd6fe8b4958f03bc54dbcb7e" }
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 0
|
||||
|
@ -139,14 +139,14 @@ rocksdb = { git = "https://github.com/rust-rocksdb/rust-rocksdb", rev = "1710120
|
|||
# To switch to the local path, run:
|
||||
# scripts/tool/update_collab_source.sh
|
||||
# ⚠️⚠️⚠️️
|
||||
collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "80d1c6147d1139289c2eaadab40557cc86c0f4b6" }
|
||||
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "80d1c6147d1139289c2eaadab40557cc86c0f4b6" }
|
||||
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "80d1c6147d1139289c2eaadab40557cc86c0f4b6" }
|
||||
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "80d1c6147d1139289c2eaadab40557cc86c0f4b6" }
|
||||
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "80d1c6147d1139289c2eaadab40557cc86c0f4b6" }
|
||||
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "80d1c6147d1139289c2eaadab40557cc86c0f4b6" }
|
||||
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "80d1c6147d1139289c2eaadab40557cc86c0f4b6" }
|
||||
collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "80d1c6147d1139289c2eaadab40557cc86c0f4b6" }
|
||||
collab = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4e717c2c6a15c42feda9e1ff1b122c7b0baf1821" }
|
||||
collab-entity = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4e717c2c6a15c42feda9e1ff1b122c7b0baf1821" }
|
||||
collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4e717c2c6a15c42feda9e1ff1b122c7b0baf1821" }
|
||||
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4e717c2c6a15c42feda9e1ff1b122c7b0baf1821" }
|
||||
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4e717c2c6a15c42feda9e1ff1b122c7b0baf1821" }
|
||||
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4e717c2c6a15c42feda9e1ff1b122c7b0baf1821" }
|
||||
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4e717c2c6a15c42feda9e1ff1b122c7b0baf1821" }
|
||||
collab-importer = { version = "0.1", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "4e717c2c6a15c42feda9e1ff1b122c7b0baf1821" }
|
||||
|
||||
# Working directory: frontend
|
||||
# To update the commit ID, run:
|
||||
|
|
|
@ -19,14 +19,13 @@ serde.workspace = true
|
|||
serde_json.workspace = true
|
||||
anyhow.workspace = true
|
||||
tracing.workspace = true
|
||||
async-trait.workspace = true
|
||||
tokio = { workspace = true, features = ["sync"] }
|
||||
lib-infra = { workspace = true }
|
||||
futures = "0.3.31"
|
||||
arc-swap = "1.7"
|
||||
flowy-sqlite = { workspace = true }
|
||||
diesel.workspace = true
|
||||
flowy-error.workspace = true
|
||||
uuid.workspace = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
|
|
@ -33,8 +33,10 @@ use collab_plugins::local_storage::kv::KVTransactionDB;
|
|||
use collab_plugins::local_storage::CollabPersistenceConfig;
|
||||
use collab_user::core::{UserAwareness, UserAwarenessNotifier};
|
||||
|
||||
use flowy_error::FlowyError;
|
||||
use lib_infra::{if_native, if_wasm};
|
||||
use tracing::{error, instrument, trace, warn};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum CollabPluginProviderType {
|
||||
|
@ -66,8 +68,8 @@ impl Display for CollabPluginProviderContext {
|
|||
}
|
||||
|
||||
pub trait WorkspaceCollabIntegrate: Send + Sync {
|
||||
fn workspace_id(&self) -> Result<String, Error>;
|
||||
fn device_id(&self) -> Result<String, Error>;
|
||||
fn workspace_id(&self) -> Result<Uuid, FlowyError>;
|
||||
fn device_id(&self) -> Result<String, FlowyError>;
|
||||
}
|
||||
|
||||
pub struct AppFlowyCollabBuilder {
|
||||
|
@ -119,15 +121,15 @@ impl AppFlowyCollabBuilder {
|
|||
|
||||
pub fn collab_object(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
uid: i64,
|
||||
object_id: &str,
|
||||
object_id: &Uuid,
|
||||
collab_type: CollabType,
|
||||
) -> Result<CollabObject, Error> {
|
||||
// Compare the workspace_id with the currently opened workspace_id. Return an error if they do not match.
|
||||
// This check is crucial in asynchronous code contexts where the workspace_id might change during operation.
|
||||
let actual_workspace_id = self.workspace_integrate.workspace_id()?;
|
||||
if workspace_id != actual_workspace_id {
|
||||
if workspace_id != &actual_workspace_id {
|
||||
return Err(anyhow::anyhow!(
|
||||
"workspace_id not match when build collab. expect workspace_id: {}, actual workspace_id: {}",
|
||||
workspace_id,
|
||||
|
@ -135,12 +137,11 @@ impl AppFlowyCollabBuilder {
|
|||
));
|
||||
}
|
||||
let device_id = self.workspace_integrate.device_id()?;
|
||||
let workspace_id = self.workspace_integrate.workspace_id()?;
|
||||
Ok(CollabObject::new(
|
||||
uid,
|
||||
object_id.to_string(),
|
||||
collab_type,
|
||||
workspace_id,
|
||||
workspace_id.to_string(),
|
||||
device_id,
|
||||
))
|
||||
}
|
||||
|
@ -399,11 +400,11 @@ impl CollabBuilderConfig {
|
|||
pub struct CollabPersistenceImpl {
|
||||
pub db: Weak<CollabKVDB>,
|
||||
pub uid: i64,
|
||||
pub workspace_id: String,
|
||||
pub workspace_id: Uuid,
|
||||
}
|
||||
|
||||
impl CollabPersistenceImpl {
|
||||
pub fn new(db: Weak<CollabKVDB>, uid: i64, workspace_id: String) -> Self {
|
||||
pub fn new(db: Weak<CollabKVDB>, uid: i64, workspace_id: Uuid) -> Self {
|
||||
Self {
|
||||
db,
|
||||
uid,
|
||||
|
@ -425,10 +426,11 @@ impl CollabPersistence for CollabPersistenceImpl {
|
|||
|
||||
let object_id = collab.object_id().to_string();
|
||||
let rocksdb_read = collab_db.read_txn();
|
||||
let workspace_id = self.workspace_id.to_string();
|
||||
|
||||
if rocksdb_read.is_exist(self.uid, &self.workspace_id, &object_id) {
|
||||
if rocksdb_read.is_exist(self.uid, &workspace_id, &object_id) {
|
||||
let mut txn = collab.transact_mut();
|
||||
match rocksdb_read.load_doc_with_txn(self.uid, &self.workspace_id, &object_id, &mut txn) {
|
||||
match rocksdb_read.load_doc_with_txn(self.uid, &workspace_id, &object_id, &mut txn) {
|
||||
Ok(update_count) => {
|
||||
trace!(
|
||||
"did load collab:{}-{} from disk, update_count:{}",
|
||||
|
@ -453,6 +455,7 @@ impl CollabPersistence for CollabPersistenceImpl {
|
|||
object_id: &str,
|
||||
encoded_collab: EncodedCollab,
|
||||
) -> Result<(), CollabError> {
|
||||
let workspace_id = self.workspace_id.to_string();
|
||||
let collab_db = self
|
||||
.db
|
||||
.upgrade()
|
||||
|
@ -461,7 +464,7 @@ impl CollabPersistence for CollabPersistenceImpl {
|
|||
write_txn
|
||||
.flush_doc(
|
||||
self.uid,
|
||||
self.workspace_id.as_str(),
|
||||
workspace_id.as_str(),
|
||||
object_id,
|
||||
encoded_collab.state_vector.to_vec(),
|
||||
encoded_collab.doc_state.to_vec(),
|
||||
|
|
|
@ -7,6 +7,8 @@ use flowy_sqlite::{
|
|||
DBConnection, ExpressionMethods, Identifiable, Insertable, Queryable,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Queryable, Insertable, Identifiable)]
|
||||
#[diesel(table_name = af_collab_metadata)]
|
||||
|
@ -43,13 +45,18 @@ pub fn batch_insert_collab_metadata(
|
|||
|
||||
pub fn batch_select_collab_metadata(
|
||||
mut conn: DBConnection,
|
||||
object_ids: &[String],
|
||||
) -> FlowyResult<HashMap<String, AFCollabMetadata>> {
|
||||
object_ids: &[Uuid],
|
||||
) -> FlowyResult<HashMap<Uuid, AFCollabMetadata>> {
|
||||
let object_ids = object_ids
|
||||
.iter()
|
||||
.map(|id| id.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
let metadata = dsl::af_collab_metadata
|
||||
.filter(af_collab_metadata::object_id.eq_any(object_ids))
|
||||
.filter(af_collab_metadata::object_id.eq_any(&object_ids))
|
||||
.load::<AFCollabMetadata>(&mut conn)?
|
||||
.into_iter()
|
||||
.map(|m| (m.object_id.clone(), m))
|
||||
.flat_map(|m| Uuid::from_str(&m.object_id).map(|v| (v, m)))
|
||||
.collect();
|
||||
Ok(metadata)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
use collab::entity::EncodedCollab;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
use flowy_document::entities::*;
|
||||
use flowy_document::event_map::DocumentEvent;
|
||||
use flowy_document::parser::parser_entities::{
|
||||
|
@ -11,6 +9,8 @@ use flowy_document::parser::parser_entities::{
|
|||
};
|
||||
use flowy_folder::entities::{CreateViewPayloadPB, ViewLayoutPB, ViewPB};
|
||||
use flowy_folder::event_map::FolderEvent;
|
||||
use serde_json::Value;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::document::utils::{gen_delta_str, gen_id, gen_text_block_data};
|
||||
use crate::event_builder::EventBuilder;
|
||||
|
@ -37,7 +37,7 @@ impl DocumentEventTest {
|
|||
Self { event_test: core }
|
||||
}
|
||||
|
||||
pub async fn get_encoded_v1(&self, doc_id: &str) -> EncodedCollab {
|
||||
pub async fn get_encoded_v1(&self, doc_id: &Uuid) -> EncodedCollab {
|
||||
let doc = self
|
||||
.event_test
|
||||
.appflowy_core
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use flowy_folder::view_operation::{GatherEncodedCollab, ViewData};
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use collab_folder::{FolderData, View};
|
||||
|
@ -16,6 +17,7 @@ use flowy_user::entities::{
|
|||
use flowy_user::errors::FlowyError;
|
||||
use flowy_user::event_map::UserEvent;
|
||||
use flowy_user_pub::entities::Role;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::event_builder::EventBuilder;
|
||||
use crate::EventIntegrationTest;
|
||||
|
@ -123,10 +125,10 @@ impl EventIntegrationTest {
|
|||
let create_view_params = views
|
||||
.into_iter()
|
||||
.map(|view| CreateViewParams {
|
||||
parent_view_id: view.parent_view_id,
|
||||
parent_view_id: Uuid::from_str(&view.parent_view_id).unwrap(),
|
||||
name: view.name,
|
||||
layout: view.layout.into(),
|
||||
view_id: view.id,
|
||||
view_id: Uuid::from_str(&view.id).unwrap(),
|
||||
initial_data: ViewData::Empty,
|
||||
meta: Default::default(),
|
||||
set_as_current: false,
|
||||
|
@ -195,9 +197,10 @@ impl EventIntegrationTest {
|
|||
view_id: &str,
|
||||
layout: ViewLayout,
|
||||
) -> GatherEncodedCollab {
|
||||
let view_id = Uuid::from_str(view_id).unwrap();
|
||||
self
|
||||
.folder_manager
|
||||
.gather_publish_encode_collab(view_id, &layout)
|
||||
.gather_publish_encode_collab(&view_id, &layout)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::user_event::TestNotificationSender;
|
||||
use collab::core::collab::DataSource;
|
||||
use collab::core::origin::CollabOrigin;
|
||||
use collab::preclude::Collab;
|
||||
|
@ -15,14 +16,14 @@ use nanoid::nanoid;
|
|||
use semver::Version;
|
||||
use std::env::temp_dir;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::select;
|
||||
use tokio::task::LocalSet;
|
||||
use tokio::time::sleep;
|
||||
|
||||
use crate::user_event::TestNotificationSender;
|
||||
use uuid::Uuid;
|
||||
|
||||
mod chat_event;
|
||||
pub mod database_event;
|
||||
|
@ -145,10 +146,16 @@ impl EventIntegrationTest {
|
|||
) -> Result<Vec<u8>, FlowyError> {
|
||||
let server = self.server_provider.get_server().unwrap();
|
||||
let workspace_id = self.get_current_workspace().await.id;
|
||||
let oid = Uuid::from_str(oid).unwrap();
|
||||
let uid = self.get_user_profile().await?.id;
|
||||
let doc_state = server
|
||||
.folder_service()
|
||||
.get_folder_doc_state(&workspace_id, uid, collab_type, oid)
|
||||
.get_folder_doc_state(
|
||||
&Uuid::from_str(&workspace_id).unwrap(),
|
||||
uid,
|
||||
collab_type,
|
||||
&oid,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(doc_state)
|
||||
|
|
|
@ -3,10 +3,12 @@ use event_integration_test::user_event::use_localhost_af_cloud;
|
|||
use event_integration_test::EventIntegrationTest;
|
||||
use flowy_ai::entities::ChatMessageListPB;
|
||||
use flowy_ai::notification::ChatNotification;
|
||||
use std::str::FromStr;
|
||||
|
||||
use flowy_ai_pub::cloud::ChatMessageType;
|
||||
|
||||
use std::time::Duration;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[tokio::test]
|
||||
async fn af_cloud_create_chat_message_test() {
|
||||
|
@ -21,8 +23,8 @@ async fn af_cloud_create_chat_message_test() {
|
|||
for i in 0..10 {
|
||||
let _ = chat_service
|
||||
.create_question(
|
||||
¤t_workspace.id,
|
||||
&chat_id,
|
||||
&Uuid::from_str(¤t_workspace.id).unwrap(),
|
||||
&Uuid::from_str(&chat_id).unwrap(),
|
||||
&format!("hello world {}", i),
|
||||
ChatMessageType::System,
|
||||
&[],
|
||||
|
@ -77,8 +79,8 @@ async fn af_cloud_load_remote_system_message_test() {
|
|||
for i in 0..10 {
|
||||
let _ = chat_service
|
||||
.create_question(
|
||||
¤t_workspace.id,
|
||||
&chat_id,
|
||||
&Uuid::from_str(¤t_workspace.id).unwrap(),
|
||||
&Uuid::from_str(&chat_id).unwrap(),
|
||||
&format!("hello server {}", i),
|
||||
ChatMessageType::System,
|
||||
&[],
|
||||
|
|
|
@ -8,6 +8,8 @@ use flowy_document::parser::parser_entities::{
|
|||
};
|
||||
use serde_json::{json, Value};
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_document_event_test() {
|
||||
|
@ -101,8 +103,8 @@ async fn document_size_test() {
|
|||
let s = generate_random_string(string_size);
|
||||
test.insert_index(&view.id, &s, 1, None).await;
|
||||
}
|
||||
|
||||
let encoded_v1 = test.get_encoded_v1(&view.id).await;
|
||||
let view_id = Uuid::from_str(&view.id).unwrap();
|
||||
let encoded_v1 = test.get_encoded_v1(&view_id).await;
|
||||
if encoded_v1.doc_state.len() > max_size {
|
||||
panic!(
|
||||
"The document size is too large. {}",
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use collab_folder::ViewLayout;
|
||||
|
||||
use event_integration_test::EventIntegrationTest;
|
||||
use flowy_folder::entities::icon::{ViewIconPB, ViewIconTypePB};
|
||||
use flowy_folder::entities::ViewLayoutPB;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::folder::local_test::script::FolderScript::*;
|
||||
use crate::folder::local_test::script::FolderTest;
|
||||
|
@ -338,11 +338,11 @@ async fn move_view_event_test() {
|
|||
async fn create_orphan_child_view_and_get_its_ancestors_test() {
|
||||
let test = EventIntegrationTest::new_anon().await;
|
||||
let name = "Orphan View";
|
||||
let view_id = "20240521";
|
||||
let view_id = Uuid::new_v4().to_string();
|
||||
test
|
||||
.create_orphan_view(name, view_id, ViewLayoutPB::Grid)
|
||||
.create_orphan_view(name, &view_id, ViewLayoutPB::Grid)
|
||||
.await;
|
||||
let ancestors = test.get_view_ancestors(view_id).await;
|
||||
let ancestors = test.get_view_ancestors(&view_id).await;
|
||||
assert_eq!(ancestors.len(), 1);
|
||||
assert_eq!(ancestors[0].name, "Orphan View");
|
||||
assert_eq!(ancestors[0].id, view_id);
|
||||
|
|
|
@ -6,6 +6,7 @@ use flowy_folder::view_operation::GatherEncodedCollab;
|
|||
use flowy_folder_pub::entities::{
|
||||
PublishDocumentPayload, PublishPayload, PublishViewInfo, PublishViewMeta, PublishViewMetaData,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
async fn mock_single_document_view_publish_payload(
|
||||
test: &EventIntegrationTest,
|
||||
|
@ -140,11 +141,11 @@ async fn create_nested_document(test: &EventIntegrationTest, view_id: &str, name
|
|||
#[tokio::test]
|
||||
async fn single_document_get_publish_view_payload_test() {
|
||||
let test = EventIntegrationTest::new_anon().await;
|
||||
let view_id = "20240521";
|
||||
let view_id = Uuid::new_v4().to_string();
|
||||
let name = "Orphan View";
|
||||
create_single_document(&test, view_id, name).await;
|
||||
let view = test.get_view(view_id).await;
|
||||
let payload = test.get_publish_payload(view_id, true).await;
|
||||
create_single_document(&test, &view_id, name).await;
|
||||
let view = test.get_view(&view_id).await;
|
||||
let payload = test.get_publish_payload(&view_id, true).await;
|
||||
|
||||
let expect_payload = mock_single_document_view_publish_payload(
|
||||
&test,
|
||||
|
@ -160,10 +161,10 @@ async fn single_document_get_publish_view_payload_test() {
|
|||
async fn nested_document_get_publish_view_payload_test() {
|
||||
let test = EventIntegrationTest::new_anon().await;
|
||||
let name = "Orphan View";
|
||||
let view_id = "20240521";
|
||||
create_nested_document(&test, view_id, name).await;
|
||||
let view = test.get_view(view_id).await;
|
||||
let payload = test.get_publish_payload(view_id, true).await;
|
||||
let view_id = Uuid::new_v4().to_string();
|
||||
create_nested_document(&test, &view_id, name).await;
|
||||
let view = test.get_view(&view_id).await;
|
||||
let payload = test.get_publish_payload(&view_id, true).await;
|
||||
|
||||
let expect_payload = mock_nested_document_view_publish_payload(
|
||||
&test,
|
||||
|
@ -180,10 +181,10 @@ async fn nested_document_get_publish_view_payload_test() {
|
|||
async fn no_children_publish_view_payload_test() {
|
||||
let test = EventIntegrationTest::new_anon().await;
|
||||
let name = "Orphan View";
|
||||
let view_id = "20240521";
|
||||
create_nested_document(&test, view_id, name).await;
|
||||
let view = test.get_view(view_id).await;
|
||||
let payload = test.get_publish_payload(view_id, false).await;
|
||||
let view_id = Uuid::new_v4().to_string();
|
||||
create_nested_document(&test, &view_id, name).await;
|
||||
let view = test.get_view(&view_id).await;
|
||||
let payload = test.get_publish_payload(&view_id, false).await;
|
||||
|
||||
let data = mock_single_document_view_publish_payload(
|
||||
&test,
|
||||
|
|
|
@ -1,44 +1,9 @@
|
|||
use event_integration_test::EventIntegrationTest;
|
||||
use flowy_core::DEFAULT_NAME;
|
||||
use flowy_folder::entities::ViewLayoutPB;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::util::unzip;
|
||||
|
||||
#[tokio::test]
|
||||
async fn migrate_020_historical_empty_document_test() {
|
||||
let user_db_path = unzip(
|
||||
"./tests/user/migration_test/history_user_db",
|
||||
"020_historical_user_data",
|
||||
)
|
||||
.unwrap();
|
||||
let test =
|
||||
EventIntegrationTest::new_with_user_data_path(user_db_path, DEFAULT_NAME.to_string()).await;
|
||||
|
||||
let mut views = test.get_all_workspace_views().await;
|
||||
assert_eq!(views.len(), 1);
|
||||
|
||||
// Check the parent view
|
||||
let parent_view = views.pop().unwrap();
|
||||
assert_eq!(parent_view.layout, ViewLayoutPB::Document);
|
||||
let data = test.open_document(parent_view.id.clone()).await.data;
|
||||
assert!(!data.page_id.is_empty());
|
||||
assert_eq!(data.blocks.len(), 2);
|
||||
assert!(!data.meta.children_map.is_empty());
|
||||
|
||||
// Check the child views of the parent view
|
||||
let child_views = test.get_view(&parent_view.id).await.child_views;
|
||||
assert_eq!(child_views.len(), 4);
|
||||
assert_eq!(child_views[0].layout, ViewLayoutPB::Document);
|
||||
assert_eq!(child_views[1].layout, ViewLayoutPB::Grid);
|
||||
assert_eq!(child_views[2].layout, ViewLayoutPB::Calendar);
|
||||
assert_eq!(child_views[3].layout, ViewLayoutPB::Board);
|
||||
|
||||
let database = test.get_database(&child_views[1].id).await;
|
||||
assert_eq!(database.fields.len(), 8);
|
||||
assert_eq!(database.rows.len(), 3);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn migrate_036_fav_v1_workspace_array_test() {
|
||||
// Used to test migration: FavoriteV1AndWorkspaceArrayMigration
|
||||
|
|
|
@ -9,7 +9,7 @@ edition = "2021"
|
|||
lib-infra = { workspace = true }
|
||||
flowy-error = { workspace = true }
|
||||
client-api = { workspace = true }
|
||||
bytes.workspace = true
|
||||
futures.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde.workspace = true
|
||||
serde.workspace = true
|
||||
uuid.workspace = true
|
|
@ -19,6 +19,7 @@ use serde::{Deserialize, Serialize};
|
|||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub type ChatMessageStream = BoxStream<'static, Result<ChatMessage, AppResponseError>>;
|
||||
pub type StreamAnswer = BoxStream<'static, Result<QuestionStreamValue, FlowyError>>;
|
||||
|
@ -81,15 +82,15 @@ pub trait ChatCloudService: Send + Sync + 'static {
|
|||
async fn create_chat(
|
||||
&self,
|
||||
uid: &i64,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
rag_ids: Vec<String>,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
rag_ids: Vec<Uuid>,
|
||||
) -> Result<(), FlowyError>;
|
||||
|
||||
async fn create_question(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
message: &str,
|
||||
message_type: ChatMessageType,
|
||||
metadata: &[ChatMessageMetadata],
|
||||
|
@ -97,8 +98,8 @@ pub trait ChatCloudService: Send + Sync + 'static {
|
|||
|
||||
async fn create_answer(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
message: &str,
|
||||
question_id: i64,
|
||||
metadata: Option<serde_json::Value>,
|
||||
|
@ -106,8 +107,8 @@ pub trait ChatCloudService: Send + Sync + 'static {
|
|||
|
||||
async fn stream_answer(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
message_id: i64,
|
||||
format: ResponseFormat,
|
||||
ai_model: Option<AIModel>,
|
||||
|
@ -115,68 +116,68 @@ pub trait ChatCloudService: Send + Sync + 'static {
|
|||
|
||||
async fn get_answer(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
question_message_id: i64,
|
||||
) -> Result<ChatMessage, FlowyError>;
|
||||
|
||||
async fn get_chat_messages(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
offset: MessageCursor,
|
||||
limit: u64,
|
||||
) -> Result<RepeatedChatMessage, FlowyError>;
|
||||
|
||||
async fn get_question_from_answer_id(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
answer_message_id: i64,
|
||||
) -> Result<ChatMessage, FlowyError>;
|
||||
|
||||
async fn get_related_message(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
message_id: i64,
|
||||
) -> Result<RepeatedRelatedQuestion, FlowyError>;
|
||||
|
||||
async fn stream_complete(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
params: CompleteTextParams,
|
||||
ai_model: Option<AIModel>,
|
||||
) -> Result<StreamComplete, FlowyError>;
|
||||
|
||||
async fn embed_file(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
file_path: &Path,
|
||||
chat_id: &str,
|
||||
chat_id: &Uuid,
|
||||
metadata: Option<HashMap<String, Value>>,
|
||||
) -> Result<(), FlowyError>;
|
||||
|
||||
async fn get_local_ai_config(&self, workspace_id: &str) -> Result<LocalAIConfig, FlowyError>;
|
||||
async fn get_local_ai_config(&self, workspace_id: &Uuid) -> Result<LocalAIConfig, FlowyError>;
|
||||
|
||||
async fn get_workspace_plan(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
) -> Result<Vec<SubscriptionPlan>, FlowyError>;
|
||||
|
||||
async fn get_chat_settings(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
) -> Result<ChatSettings, FlowyError>;
|
||||
|
||||
async fn update_chat_settings(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
params: UpdateChatParams,
|
||||
) -> Result<(), FlowyError>;
|
||||
|
||||
async fn get_available_models(&self, workspace_id: &str) -> Result<ModelList, FlowyError>;
|
||||
async fn get_workspace_default_model(&self, workspace_id: &str) -> Result<String, FlowyError>;
|
||||
async fn get_available_models(&self, workspace_id: &Uuid) -> Result<ModelList, FlowyError>;
|
||||
async fn get_workspace_default_model(&self, workspace_id: &Uuid) -> Result<String, FlowyError>;
|
||||
}
|
||||
|
|
|
@ -27,14 +27,16 @@ use flowy_storage_pub::storage::StorageService;
|
|||
use lib_infra::async_trait::async_trait;
|
||||
use lib_infra::util::timestamp;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Weak};
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::{error, info, instrument, trace};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub trait AIUserService: Send + Sync + 'static {
|
||||
fn user_id(&self) -> Result<i64, FlowyError>;
|
||||
fn device_id(&self) -> Result<String, FlowyError>;
|
||||
fn workspace_id(&self) -> Result<String, FlowyError>;
|
||||
fn workspace_id(&self) -> Result<Uuid, FlowyError>;
|
||||
fn sqlite_connection(&self, uid: i64) -> Result<DBConnection, FlowyError>;
|
||||
fn application_root_dir(&self) -> Result<PathBuf, FlowyError>;
|
||||
}
|
||||
|
@ -44,18 +46,18 @@ pub trait AIUserService: Send + Sync + 'static {
|
|||
pub trait AIExternalService: Send + Sync + 'static {
|
||||
async fn query_chat_rag_ids(
|
||||
&self,
|
||||
parent_view_id: &str,
|
||||
chat_id: &str,
|
||||
) -> Result<Vec<String>, FlowyError>;
|
||||
parent_view_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
) -> Result<Vec<Uuid>, FlowyError>;
|
||||
|
||||
async fn sync_rag_documents(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
rag_ids: Vec<String>,
|
||||
rag_metadata_map: HashMap<String, AFCollabMetadata>,
|
||||
workspace_id: &Uuid,
|
||||
rag_ids: Vec<Uuid>,
|
||||
rag_metadata_map: HashMap<Uuid, AFCollabMetadata>,
|
||||
) -> Result<Vec<AFCollabMetadata>, FlowyError>;
|
||||
|
||||
async fn notify_did_send_message(&self, chat_id: &str, message: &str) -> Result<(), FlowyError>;
|
||||
async fn notify_did_send_message(&self, chat_id: &Uuid, message: &str) -> Result<(), FlowyError>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
@ -70,7 +72,7 @@ pub struct AIManager {
|
|||
pub cloud_service_wm: Arc<AICloudServiceMiddleware>,
|
||||
pub user_service: Arc<dyn AIUserService>,
|
||||
pub external_service: Arc<dyn AIExternalService>,
|
||||
chats: Arc<DashMap<String, Arc<Chat>>>,
|
||||
chats: Arc<DashMap<Uuid, Arc<Chat>>>,
|
||||
pub local_ai: Arc<LocalAIController>,
|
||||
pub store_preferences: Arc<KVStorePreferences>,
|
||||
server_models: Arc<RwLock<ServerModelsCache>>,
|
||||
|
@ -132,11 +134,11 @@ impl AIManager {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn open_chat(&self, chat_id: &str) -> Result<(), FlowyError> {
|
||||
self.chats.entry(chat_id.to_string()).or_insert_with(|| {
|
||||
pub async fn open_chat(&self, chat_id: &Uuid) -> Result<(), FlowyError> {
|
||||
self.chats.entry(*chat_id).or_insert_with(|| {
|
||||
Arc::new(Chat::new(
|
||||
self.user_service.user_id().unwrap(),
|
||||
chat_id.to_string(),
|
||||
*chat_id,
|
||||
self.user_service.clone(),
|
||||
self.cloud_service_wm.clone(),
|
||||
))
|
||||
|
@ -150,7 +152,7 @@ impl AIManager {
|
|||
let cloud_service_wm = self.cloud_service_wm.clone();
|
||||
let store_preferences = self.store_preferences.clone();
|
||||
let external_service = self.external_service.clone();
|
||||
let chat_id = chat_id.to_string();
|
||||
let chat_id = *chat_id;
|
||||
tokio::spawn(async move {
|
||||
match refresh_chat_setting(
|
||||
&user_service,
|
||||
|
@ -161,7 +163,12 @@ impl AIManager {
|
|||
.await
|
||||
{
|
||||
Ok(settings) => {
|
||||
let _ = sync_chat_documents(user_service, external_service, settings.rag_ids).await;
|
||||
let rag_ids = settings
|
||||
.rag_ids
|
||||
.into_iter()
|
||||
.flat_map(|r| Uuid::from_str(&r).ok())
|
||||
.collect();
|
||||
let _ = sync_chat_documents(user_service, external_service, rag_ids).await;
|
||||
},
|
||||
Err(err) => {
|
||||
error!("failed to refresh chat settings: {}", err);
|
||||
|
@ -172,13 +179,13 @@ impl AIManager {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn close_chat(&self, chat_id: &str) -> Result<(), FlowyError> {
|
||||
pub async fn close_chat(&self, chat_id: &Uuid) -> Result<(), FlowyError> {
|
||||
trace!("close chat: {}", chat_id);
|
||||
self.local_ai.close_chat(chat_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_chat(&self, chat_id: &str) -> Result<(), FlowyError> {
|
||||
pub async fn delete_chat(&self, chat_id: &Uuid) -> Result<(), FlowyError> {
|
||||
if let Some((_, chat)) = self.chats.remove(chat_id) {
|
||||
chat.close();
|
||||
|
||||
|
@ -212,8 +219,8 @@ impl AIManager {
|
|||
pub async fn create_chat(
|
||||
&self,
|
||||
uid: &i64,
|
||||
parent_view_id: &str,
|
||||
chat_id: &str,
|
||||
parent_view_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
) -> Result<Arc<Chat>, FlowyError> {
|
||||
let workspace_id = self.user_service.workspace_id()?;
|
||||
let rag_ids = self
|
||||
|
@ -231,11 +238,11 @@ impl AIManager {
|
|||
|
||||
let chat = Arc::new(Chat::new(
|
||||
self.user_service.user_id()?,
|
||||
chat_id.to_string(),
|
||||
*chat_id,
|
||||
self.user_service.clone(),
|
||||
self.cloud_service_wm.clone(),
|
||||
));
|
||||
self.chats.insert(chat_id.to_string(), chat.clone());
|
||||
self.chats.insert(*chat_id, chat.clone());
|
||||
Ok(chat)
|
||||
}
|
||||
|
||||
|
@ -244,7 +251,7 @@ impl AIManager {
|
|||
params: StreamMessageParams,
|
||||
) -> Result<ChatMessagePB, FlowyError> {
|
||||
let chat = self.get_or_create_chat_instance(¶ms.chat_id).await?;
|
||||
let ai_model = self.get_active_model(¶ms.chat_id).await;
|
||||
let ai_model = self.get_active_model(¶ms.chat_id.to_string()).await;
|
||||
let question = chat.stream_chat_message(¶ms, ai_model).await?;
|
||||
let _ = self
|
||||
.external_service
|
||||
|
@ -255,7 +262,7 @@ impl AIManager {
|
|||
|
||||
pub async fn stream_regenerate_response(
|
||||
&self,
|
||||
chat_id: &str,
|
||||
chat_id: &Uuid,
|
||||
answer_message_id: i64,
|
||||
answer_stream_port: i64,
|
||||
format: Option<PredefinedFormatPB>,
|
||||
|
@ -270,7 +277,7 @@ impl AIManager {
|
|||
|| {
|
||||
self
|
||||
.store_preferences
|
||||
.get_object::<AIModel>(&ai_available_models_key(chat_id))
|
||||
.get_object::<AIModel>(&ai_available_models_key(&chat_id.to_string()))
|
||||
},
|
||||
|model| Some(model.into()),
|
||||
);
|
||||
|
@ -520,17 +527,17 @@ impl AIManager {
|
|||
})
|
||||
}
|
||||
|
||||
pub async fn get_or_create_chat_instance(&self, chat_id: &str) -> Result<Arc<Chat>, FlowyError> {
|
||||
pub async fn get_or_create_chat_instance(&self, chat_id: &Uuid) -> Result<Arc<Chat>, FlowyError> {
|
||||
let chat = self.chats.get(chat_id).as_deref().cloned();
|
||||
match chat {
|
||||
None => {
|
||||
let chat = Arc::new(Chat::new(
|
||||
self.user_service.user_id()?,
|
||||
chat_id.to_string(),
|
||||
*chat_id,
|
||||
self.user_service.clone(),
|
||||
self.cloud_service_wm.clone(),
|
||||
));
|
||||
self.chats.insert(chat_id.to_string(), chat.clone());
|
||||
self.chats.insert(*chat_id, chat.clone());
|
||||
Ok(chat)
|
||||
},
|
||||
Some(chat) => Ok(chat),
|
||||
|
@ -554,7 +561,7 @@ impl AIManager {
|
|||
|
||||
pub async fn load_prev_chat_messages(
|
||||
&self,
|
||||
chat_id: &str,
|
||||
chat_id: &Uuid,
|
||||
limit: i64,
|
||||
before_message_id: Option<i64>,
|
||||
) -> Result<ChatMessageListPB, FlowyError> {
|
||||
|
@ -567,7 +574,7 @@ impl AIManager {
|
|||
|
||||
pub async fn load_latest_chat_messages(
|
||||
&self,
|
||||
chat_id: &str,
|
||||
chat_id: &Uuid,
|
||||
limit: i64,
|
||||
after_message_id: Option<i64>,
|
||||
) -> Result<ChatMessageListPB, FlowyError> {
|
||||
|
@ -580,7 +587,7 @@ impl AIManager {
|
|||
|
||||
pub async fn get_related_questions(
|
||||
&self,
|
||||
chat_id: &str,
|
||||
chat_id: &Uuid,
|
||||
message_id: i64,
|
||||
) -> Result<RepeatedRelatedQuestionPB, FlowyError> {
|
||||
let chat = self.get_or_create_chat_instance(chat_id).await?;
|
||||
|
@ -590,7 +597,7 @@ impl AIManager {
|
|||
|
||||
pub async fn generate_answer(
|
||||
&self,
|
||||
chat_id: &str,
|
||||
chat_id: &Uuid,
|
||||
question_message_id: i64,
|
||||
) -> Result<ChatMessagePB, FlowyError> {
|
||||
let chat = self.get_or_create_chat_instance(chat_id).await?;
|
||||
|
@ -598,19 +605,19 @@ impl AIManager {
|
|||
Ok(resp)
|
||||
}
|
||||
|
||||
pub async fn stop_stream(&self, chat_id: &str) -> Result<(), FlowyError> {
|
||||
pub async fn stop_stream(&self, chat_id: &Uuid) -> Result<(), FlowyError> {
|
||||
let chat = self.get_or_create_chat_instance(chat_id).await?;
|
||||
chat.stop_stream_message().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn chat_with_file(&self, chat_id: &str, file_path: PathBuf) -> FlowyResult<()> {
|
||||
pub async fn chat_with_file(&self, chat_id: &Uuid, file_path: PathBuf) -> FlowyResult<()> {
|
||||
let chat = self.get_or_create_chat_instance(chat_id).await?;
|
||||
chat.index_file(file_path).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_rag_ids(&self, chat_id: &str) -> FlowyResult<Vec<String>> {
|
||||
pub async fn get_rag_ids(&self, chat_id: &Uuid) -> FlowyResult<Vec<String>> {
|
||||
if let Some(settings) = self
|
||||
.store_preferences
|
||||
.get_object::<ChatSettings>(&setting_store_key(chat_id))
|
||||
|
@ -628,7 +635,7 @@ impl AIManager {
|
|||
Ok(settings.rag_ids)
|
||||
}
|
||||
|
||||
pub async fn update_rag_ids(&self, chat_id: &str, rag_ids: Vec<String>) -> FlowyResult<()> {
|
||||
pub async fn update_rag_ids(&self, chat_id: &Uuid, rag_ids: Vec<String>) -> FlowyResult<()> {
|
||||
info!("[Chat] update chat:{} rag ids: {:?}", chat_id, rag_ids);
|
||||
let workspace_id = self.user_service.workspace_id()?;
|
||||
let update_setting = UpdateChatParams {
|
||||
|
@ -659,6 +666,10 @@ impl AIManager {
|
|||
|
||||
let user_service = self.user_service.clone();
|
||||
let external_service = self.external_service.clone();
|
||||
let rag_ids = rag_ids
|
||||
.into_iter()
|
||||
.flat_map(|r| Uuid::from_str(&r).ok())
|
||||
.collect();
|
||||
sync_chat_documents(user_service, external_service, rag_ids).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -667,7 +678,7 @@ impl AIManager {
|
|||
async fn sync_chat_documents(
|
||||
user_service: Arc<dyn AIUserService>,
|
||||
external_service: Arc<dyn AIExternalService>,
|
||||
rag_ids: Vec<String>,
|
||||
rag_ids: Vec<Uuid>,
|
||||
) -> FlowyResult<()> {
|
||||
if rag_ids.is_empty() {
|
||||
return Ok(());
|
||||
|
@ -697,7 +708,7 @@ async fn sync_chat_documents(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn save_chat(conn: DBConnection, chat_id: &str) -> FlowyResult<()> {
|
||||
fn save_chat(conn: DBConnection, chat_id: &Uuid) -> FlowyResult<()> {
|
||||
let row = ChatTable {
|
||||
chat_id: chat_id.to_string(),
|
||||
created_at: timestamp(),
|
||||
|
@ -716,7 +727,7 @@ async fn refresh_chat_setting(
|
|||
user_service: &Arc<dyn AIUserService>,
|
||||
cloud_service: &Arc<AICloudServiceMiddleware>,
|
||||
store_preferences: &Arc<KVStorePreferences>,
|
||||
chat_id: &str,
|
||||
chat_id: &Uuid,
|
||||
) -> FlowyResult<ChatSettings> {
|
||||
info!("[Chat] refresh chat:{} setting", chat_id);
|
||||
let workspace_id = user_service.workspace_id()?;
|
||||
|
@ -728,7 +739,7 @@ async fn refresh_chat_setting(
|
|||
error!("failed to set chat settings: {}", err);
|
||||
}
|
||||
|
||||
chat_notification_builder(chat_id, ChatNotification::DidUpdateChatSettings)
|
||||
chat_notification_builder(chat_id.to_string(), ChatNotification::DidUpdateChatSettings)
|
||||
.payload(ChatSettingsPB {
|
||||
rag_ids: settings.rag_ids.clone(),
|
||||
})
|
||||
|
@ -737,6 +748,6 @@ async fn refresh_chat_setting(
|
|||
Ok(settings)
|
||||
}
|
||||
|
||||
fn setting_store_key(chat_id: &str) -> String {
|
||||
fn setting_store_key(chat_id: &Uuid) -> String {
|
||||
format!("chat_settings_{}", chat_id)
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ use std::sync::atomic::{AtomicBool, AtomicI64};
|
|||
use std::sync::Arc;
|
||||
use tokio::sync::{Mutex, RwLock};
|
||||
use tracing::{error, instrument, trace};
|
||||
use uuid::Uuid;
|
||||
|
||||
enum PrevMessageState {
|
||||
HasMore,
|
||||
|
@ -31,7 +32,7 @@ enum PrevMessageState {
|
|||
}
|
||||
|
||||
pub struct Chat {
|
||||
chat_id: String,
|
||||
chat_id: Uuid,
|
||||
uid: i64,
|
||||
user_service: Arc<dyn AIUserService>,
|
||||
chat_service: Arc<AICloudServiceMiddleware>,
|
||||
|
@ -44,7 +45,7 @@ pub struct Chat {
|
|||
impl Chat {
|
||||
pub fn new(
|
||||
uid: i64,
|
||||
chat_id: String,
|
||||
chat_id: Uuid,
|
||||
user_service: Arc<dyn AIUserService>,
|
||||
chat_service: Arc<AICloudServiceMiddleware>,
|
||||
) -> Chat {
|
||||
|
@ -197,13 +198,13 @@ impl Chat {
|
|||
answer_stream_port: i64,
|
||||
answer_stream_buffer: Arc<Mutex<StringBuffer>>,
|
||||
uid: i64,
|
||||
workspace_id: String,
|
||||
workspace_id: Uuid,
|
||||
question_id: i64,
|
||||
format: ResponseFormat,
|
||||
ai_model: Option<AIModel>,
|
||||
) {
|
||||
let stop_stream = self.stop_stream.clone();
|
||||
let chat_id = self.chat_id.clone();
|
||||
let chat_id = self.chat_id;
|
||||
let cloud_service = self.chat_service.clone();
|
||||
let user_service = self.user_service.clone();
|
||||
tokio::spawn(async move {
|
||||
|
@ -254,10 +255,10 @@ impl Chat {
|
|||
.send(StreamMessage::OnError(err.msg.clone()).to_string())
|
||||
.await;
|
||||
let pb = ChatMessageErrorPB {
|
||||
chat_id: chat_id.clone(),
|
||||
chat_id: chat_id.to_string(),
|
||||
error_message: err.to_string(),
|
||||
};
|
||||
chat_notification_builder(&chat_id, ChatNotification::StreamChatMessageError)
|
||||
chat_notification_builder(chat_id, ChatNotification::StreamChatMessageError)
|
||||
.payload(pb)
|
||||
.send();
|
||||
return Err(err);
|
||||
|
@ -293,17 +294,17 @@ impl Chat {
|
|||
}
|
||||
|
||||
let pb = ChatMessageErrorPB {
|
||||
chat_id: chat_id.clone(),
|
||||
chat_id: chat_id.to_string(),
|
||||
error_message: err.to_string(),
|
||||
};
|
||||
chat_notification_builder(&chat_id, ChatNotification::StreamChatMessageError)
|
||||
chat_notification_builder(chat_id, ChatNotification::StreamChatMessageError)
|
||||
.payload(pb)
|
||||
.send();
|
||||
return Err(err);
|
||||
},
|
||||
}
|
||||
|
||||
chat_notification_builder(&chat_id, ChatNotification::FinishStreaming).send();
|
||||
chat_notification_builder(chat_id, ChatNotification::FinishStreaming).send();
|
||||
trace!("[Chat] finish streaming");
|
||||
|
||||
if answer_stream_buffer.lock().await.is_empty() {
|
||||
|
@ -359,7 +360,7 @@ impl Chat {
|
|||
has_more: true,
|
||||
total: 0,
|
||||
};
|
||||
chat_notification_builder(&self.chat_id, ChatNotification::DidLoadPrevChatMessage)
|
||||
chat_notification_builder(self.chat_id, ChatNotification::DidLoadPrevChatMessage)
|
||||
.payload(pb.clone())
|
||||
.send();
|
||||
return Ok(pb);
|
||||
|
@ -432,7 +433,7 @@ impl Chat {
|
|||
after_message_id
|
||||
);
|
||||
let workspace_id = self.user_service.workspace_id()?;
|
||||
let chat_id = self.chat_id.clone();
|
||||
let chat_id = self.chat_id;
|
||||
let cloud_service = self.chat_service.clone();
|
||||
let user_service = self.user_service.clone();
|
||||
let uid = self.uid;
|
||||
|
@ -480,11 +481,11 @@ impl Chat {
|
|||
} else {
|
||||
*prev_message_state.write().await = PrevMessageState::NoMore;
|
||||
}
|
||||
chat_notification_builder(&chat_id, ChatNotification::DidLoadPrevChatMessage)
|
||||
chat_notification_builder(chat_id, ChatNotification::DidLoadPrevChatMessage)
|
||||
.payload(pb)
|
||||
.send();
|
||||
} else {
|
||||
chat_notification_builder(&chat_id, ChatNotification::DidLoadLatestChatMessage)
|
||||
chat_notification_builder(chat_id, ChatNotification::DidLoadLatestChatMessage)
|
||||
.payload(pb)
|
||||
.send();
|
||||
}
|
||||
|
@ -510,7 +511,7 @@ impl Chat {
|
|||
}
|
||||
|
||||
let workspace_id = self.user_service.workspace_id()?;
|
||||
let chat_id = self.chat_id.clone();
|
||||
let chat_id = self.chat_id;
|
||||
let cloud_service = self.chat_service.clone();
|
||||
|
||||
let question = cloud_service
|
||||
|
@ -566,7 +567,7 @@ impl Chat {
|
|||
let conn = self.user_service.sqlite_connection(self.uid)?;
|
||||
let records = select_chat_messages(
|
||||
conn,
|
||||
&self.chat_id,
|
||||
&self.chat_id.to_string(),
|
||||
limit,
|
||||
after_message_id,
|
||||
before_message_id,
|
||||
|
@ -628,7 +629,7 @@ impl Chat {
|
|||
|
||||
fn save_chat_message_disk(
|
||||
conn: DBConnection,
|
||||
chat_id: &str,
|
||||
chat_id: &Uuid,
|
||||
messages: Vec<ChatMessage>,
|
||||
) -> FlowyResult<()> {
|
||||
let records = messages
|
||||
|
@ -683,7 +684,7 @@ impl StringBuffer {
|
|||
|
||||
pub(crate) fn save_and_notify_message(
|
||||
uid: i64,
|
||||
chat_id: &str,
|
||||
chat_id: &Uuid,
|
||||
user_service: &Arc<dyn AIUserService>,
|
||||
message: ChatMessage,
|
||||
) -> Result<(), FlowyError> {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::ai_manager::AIUserService;
|
||||
use crate::entities::{CompleteTextPB, CompleteTextTaskPB, CompletionTypePB};
|
||||
use allo_isolate::Isolate;
|
||||
use std::str::FromStr;
|
||||
|
||||
use dashmap::DashMap;
|
||||
use flowy_ai_pub::cloud::{
|
||||
|
@ -15,7 +16,8 @@ use lib_infra::isolate_stream::IsolateSink;
|
|||
use crate::stream_message::StreamMessage;
|
||||
use std::sync::{Arc, Weak};
|
||||
use tokio::select;
|
||||
use tracing::info;
|
||||
use tracing::{error, info};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub struct AICompletion {
|
||||
tasks: Arc<DashMap<String, tokio::sync::mpsc::Sender<()>>>,
|
||||
|
@ -77,7 +79,7 @@ impl AICompletion {
|
|||
}
|
||||
|
||||
pub struct CompletionTask {
|
||||
workspace_id: String,
|
||||
workspace_id: Uuid,
|
||||
task_id: String,
|
||||
stop_rx: tokio::sync::mpsc::Receiver<()>,
|
||||
context: CompleteTextPB,
|
||||
|
@ -87,7 +89,7 @@ pub struct CompletionTask {
|
|||
|
||||
impl CompletionTask {
|
||||
pub fn new(
|
||||
workspace_id: String,
|
||||
workspace_id: Uuid,
|
||||
context: CompleteTextPB,
|
||||
preferred_model: Option<AIModel>,
|
||||
cloud_service: Weak<dyn ChatCloudService>,
|
||||
|
@ -122,59 +124,63 @@ impl CompletionTask {
|
|||
let _ = sink.send("start:".to_string()).await;
|
||||
let completion_history = Some(self.context.history.iter().map(Into::into).collect());
|
||||
let format = self.context.format.map(Into::into).unwrap_or_default();
|
||||
let params = CompleteTextParams {
|
||||
text: self.context.text,
|
||||
completion_type: Some(complete_type),
|
||||
metadata: Some(CompletionMetadata {
|
||||
object_id: self.context.object_id,
|
||||
workspace_id: Some(self.workspace_id.clone()),
|
||||
rag_ids: Some(self.context.rag_ids),
|
||||
completion_history,
|
||||
custom_prompt: self
|
||||
.context
|
||||
.custom_prompt
|
||||
.map(|v| CustomPrompt { system: v }),
|
||||
}),
|
||||
format,
|
||||
};
|
||||
if let Ok(object_id) = Uuid::from_str(&self.context.object_id) {
|
||||
let params = CompleteTextParams {
|
||||
text: self.context.text,
|
||||
completion_type: Some(complete_type),
|
||||
metadata: Some(CompletionMetadata {
|
||||
object_id,
|
||||
workspace_id: Some(self.workspace_id),
|
||||
rag_ids: Some(self.context.rag_ids),
|
||||
completion_history,
|
||||
custom_prompt: self
|
||||
.context
|
||||
.custom_prompt
|
||||
.map(|v| CustomPrompt { system: v }),
|
||||
}),
|
||||
format,
|
||||
};
|
||||
|
||||
info!("start completion: {:?}", params);
|
||||
match cloud_service
|
||||
.stream_complete(&self.workspace_id, params, self.preferred_model)
|
||||
.await
|
||||
{
|
||||
Ok(mut stream) => loop {
|
||||
select! {
|
||||
_ = self.stop_rx.recv() => {
|
||||
return;
|
||||
},
|
||||
result = stream.next() => {
|
||||
match result {
|
||||
Some(Ok(data)) => {
|
||||
match data {
|
||||
CompletionStreamValue::Answer{ value } => {
|
||||
let _ = sink.send(format!("data:{}", value)).await;
|
||||
info!("start completion: {:?}", params);
|
||||
match cloud_service
|
||||
.stream_complete(&self.workspace_id, params, self.preferred_model)
|
||||
.await
|
||||
{
|
||||
Ok(mut stream) => loop {
|
||||
select! {
|
||||
_ = self.stop_rx.recv() => {
|
||||
return;
|
||||
},
|
||||
result = stream.next() => {
|
||||
match result {
|
||||
Some(Ok(data)) => {
|
||||
match data {
|
||||
CompletionStreamValue::Answer{ value } => {
|
||||
let _ = sink.send(format!("data:{}", value)).await;
|
||||
}
|
||||
CompletionStreamValue::Comment{ value } => {
|
||||
let _ = sink.send(format!("comment:{}", value)).await;
|
||||
}
|
||||
}
|
||||
CompletionStreamValue::Comment{ value } => {
|
||||
let _ = sink.send(format!("comment:{}", value)).await;
|
||||
}
|
||||
}
|
||||
},
|
||||
Some(Err(error)) => {
|
||||
handle_error(&mut sink, error).await;
|
||||
return;
|
||||
},
|
||||
None => {
|
||||
let _ = sink.send(format!("finish:{}", self.task_id)).await;
|
||||
return;
|
||||
},
|
||||
},
|
||||
Some(Err(error)) => {
|
||||
handle_error(&mut sink, error).await;
|
||||
return;
|
||||
},
|
||||
None => {
|
||||
let _ = sink.send(format!("finish:{}", self.task_id)).await;
|
||||
return;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(error) => {
|
||||
handle_error(&mut sink, error).await;
|
||||
},
|
||||
}
|
||||
},
|
||||
Err(error) => {
|
||||
handle_error(&mut sink, error).await;
|
||||
},
|
||||
}
|
||||
} else {
|
||||
error!("Invalid uuid: {}", self.context.object_id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
use af_plugin::core::plugin::RunningState;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::local_ai::controller::LocalAISetting;
|
||||
use crate::local_ai::resource::PendingResource;
|
||||
use af_plugin::core::plugin::RunningState;
|
||||
use flowy_ai_pub::cloud::{
|
||||
AIModel, ChatMessage, ChatMessageMetadata, ChatMessageType, CompletionMessage, LLMModel,
|
||||
OutputContent, OutputLayout, RelatedQuestion, RepeatedChatMessage, RepeatedRelatedQuestion,
|
||||
|
@ -10,6 +8,8 @@ use flowy_ai_pub::cloud::{
|
|||
};
|
||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
use lib_infra::validator_fn::required_not_empty_str;
|
||||
use std::collections::HashMap;
|
||||
use uuid::Uuid;
|
||||
use validator::Validate;
|
||||
|
||||
#[derive(Default, ProtoBuf, Validate, Clone, Debug)]
|
||||
|
@ -78,7 +78,7 @@ pub struct StreamChatPayloadPB {
|
|||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct StreamMessageParams {
|
||||
pub chat_id: String,
|
||||
pub chat_id: Uuid,
|
||||
pub message: String,
|
||||
pub message_type: ChatMessageType,
|
||||
pub answer_stream_port: i64,
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::ai_manager::{AIManager, GLOBAL_ACTIVE_MODEL_KEY};
|
||||
use crate::completion::AICompletion;
|
||||
use crate::entities::*;
|
||||
|
@ -10,8 +7,12 @@ use flowy_ai_pub::cloud::{
|
|||
};
|
||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
use lib_dispatch::prelude::{data_result_ok, AFPluginData, AFPluginState, DataResult};
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Weak};
|
||||
use tracing::trace;
|
||||
use uuid::Uuid;
|
||||
use validator::Validate;
|
||||
|
||||
fn upgrade_ai_manager(ai_manager: AFPluginState<Weak<AIManager>>) -> FlowyResult<Arc<AIManager>> {
|
||||
|
@ -70,6 +71,7 @@ pub(crate) async fn stream_chat_message_handler(
|
|||
|
||||
trace!("Stream chat message with metadata: {:?}", metadata);
|
||||
|
||||
let chat_id = Uuid::from_str(&chat_id)?;
|
||||
let params = StreamMessageParams {
|
||||
chat_id,
|
||||
message,
|
||||
|
@ -91,11 +93,12 @@ pub(crate) async fn regenerate_response_handler(
|
|||
ai_manager: AFPluginState<Weak<AIManager>>,
|
||||
) -> FlowyResult<()> {
|
||||
let data = data.try_into_inner()?;
|
||||
let chat_id = Uuid::from_str(&data.chat_id)?;
|
||||
|
||||
let ai_manager = upgrade_ai_manager(ai_manager)?;
|
||||
ai_manager
|
||||
.stream_regenerate_response(
|
||||
&data.chat_id,
|
||||
&chat_id,
|
||||
data.answer_message_id,
|
||||
data.answer_stream_port,
|
||||
data.format,
|
||||
|
@ -147,8 +150,9 @@ pub(crate) async fn load_prev_message_handler(
|
|||
let data = data.into_inner();
|
||||
data.validate()?;
|
||||
|
||||
let chat_id = Uuid::from_str(&data.chat_id)?;
|
||||
let messages = ai_manager
|
||||
.load_prev_chat_messages(&data.chat_id, data.limit, data.before_message_id)
|
||||
.load_prev_chat_messages(&chat_id, data.limit, data.before_message_id)
|
||||
.await?;
|
||||
data_result_ok(messages)
|
||||
}
|
||||
|
@ -162,8 +166,9 @@ pub(crate) async fn load_next_message_handler(
|
|||
let data = data.into_inner();
|
||||
data.validate()?;
|
||||
|
||||
let chat_id = Uuid::from_str(&data.chat_id)?;
|
||||
let messages = ai_manager
|
||||
.load_latest_chat_messages(&data.chat_id, data.limit, data.after_message_id)
|
||||
.load_latest_chat_messages(&chat_id, data.limit, data.after_message_id)
|
||||
.await?;
|
||||
data_result_ok(messages)
|
||||
}
|
||||
|
@ -175,8 +180,9 @@ pub(crate) async fn get_related_question_handler(
|
|||
) -> DataResult<RepeatedRelatedQuestionPB, FlowyError> {
|
||||
let ai_manager = upgrade_ai_manager(ai_manager)?;
|
||||
let data = data.into_inner();
|
||||
let chat_id = Uuid::from_str(&data.chat_id)?;
|
||||
let messages = ai_manager
|
||||
.get_related_questions(&data.chat_id, data.message_id)
|
||||
.get_related_questions(&chat_id, data.message_id)
|
||||
.await?;
|
||||
data_result_ok(messages)
|
||||
}
|
||||
|
@ -188,8 +194,9 @@ pub(crate) async fn get_answer_handler(
|
|||
) -> DataResult<ChatMessagePB, FlowyError> {
|
||||
let ai_manager = upgrade_ai_manager(ai_manager)?;
|
||||
let data = data.into_inner();
|
||||
let chat_id = Uuid::from_str(&data.chat_id)?;
|
||||
let message = ai_manager
|
||||
.generate_answer(&data.chat_id, data.message_id)
|
||||
.generate_answer(&chat_id, data.message_id)
|
||||
.await?;
|
||||
data_result_ok(message)
|
||||
}
|
||||
|
@ -203,7 +210,8 @@ pub(crate) async fn stop_stream_handler(
|
|||
data.validate()?;
|
||||
|
||||
let ai_manager = upgrade_ai_manager(ai_manager)?;
|
||||
ai_manager.stop_stream(&data.chat_id).await?;
|
||||
let chat_id = Uuid::from_str(&data.chat_id)?;
|
||||
ai_manager.stop_stream(&chat_id).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -273,7 +281,8 @@ pub(crate) async fn chat_file_handler(
|
|||
|
||||
tracing::debug!("File size: {} bytes", file_size);
|
||||
let ai_manager = upgrade_ai_manager(ai_manager)?;
|
||||
ai_manager.chat_with_file(&data.chat_id, file_path).await?;
|
||||
let chat_id = Uuid::from_str(&data.chat_id)?;
|
||||
ai_manager.chat_with_file(&chat_id, file_path).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -332,6 +341,7 @@ pub(crate) async fn get_chat_settings_handler(
|
|||
ai_manager: AFPluginState<Weak<AIManager>>,
|
||||
) -> DataResult<ChatSettingsPB, FlowyError> {
|
||||
let chat_id = data.try_into_inner()?.value;
|
||||
let chat_id = Uuid::from_str(&chat_id)?;
|
||||
let ai_manager = upgrade_ai_manager(ai_manager)?;
|
||||
let rag_ids = ai_manager.get_rag_ids(&chat_id).await?;
|
||||
let pb = ChatSettingsPB { rag_ids };
|
||||
|
@ -345,9 +355,8 @@ pub(crate) async fn update_chat_settings_handler(
|
|||
) -> FlowyResult<()> {
|
||||
let params = data.try_into_inner()?;
|
||||
let ai_manager = upgrade_ai_manager(ai_manager)?;
|
||||
ai_manager
|
||||
.update_rag_ids(¶ms.chat_id.value, params.rag_ids)
|
||||
.await?;
|
||||
let chat_id = Uuid::from_str(¶ms.chat_id.value)?;
|
||||
ai_manager.update_rag_ids(&chat_id, params.rag_ids).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ use std::sync::Arc;
|
|||
use tokio::select;
|
||||
use tokio_stream::StreamExt;
|
||||
use tracing::{debug, error, info, instrument};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct LocalAISetting {
|
||||
|
@ -51,7 +52,7 @@ const LOCAL_AI_SETTING_KEY: &str = "appflowy_local_ai_setting:v1";
|
|||
pub struct LocalAIController {
|
||||
ai_plugin: Arc<OllamaAIPlugin>,
|
||||
resource: Arc<LocalAIResourceController>,
|
||||
current_chat_id: ArcSwapOption<String>,
|
||||
current_chat_id: ArcSwapOption<Uuid>,
|
||||
store_preferences: Arc<KVStorePreferences>,
|
||||
user_service: Arc<dyn AIUserService>,
|
||||
#[allow(dead_code)]
|
||||
|
@ -240,7 +241,7 @@ impl LocalAIController {
|
|||
Some(self.resource.get_llm_setting().chat_model_name)
|
||||
}
|
||||
|
||||
pub fn open_chat(&self, chat_id: &str) {
|
||||
pub fn open_chat(&self, chat_id: &Uuid) {
|
||||
if !self.is_enabled() {
|
||||
return;
|
||||
}
|
||||
|
@ -252,9 +253,7 @@ impl LocalAIController {
|
|||
self.close_chat(current_chat_id);
|
||||
}
|
||||
|
||||
self
|
||||
.current_chat_id
|
||||
.store(Some(Arc::new(chat_id.to_string())));
|
||||
self.current_chat_id.store(Some(Arc::new(*chat_id)));
|
||||
let chat_id = chat_id.to_string();
|
||||
let weak_ctrl = Arc::downgrade(&self.ai_plugin);
|
||||
tokio::spawn(async move {
|
||||
|
@ -266,7 +265,7 @@ impl LocalAIController {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn close_chat(&self, chat_id: &str) {
|
||||
pub fn close_chat(&self, chat_id: &Uuid) {
|
||||
if !self.is_running() {
|
||||
return;
|
||||
}
|
||||
|
@ -383,7 +382,7 @@ impl LocalAIController {
|
|||
#[instrument(level = "debug", skip_all)]
|
||||
pub async fn index_message_metadata(
|
||||
&self,
|
||||
chat_id: &str,
|
||||
chat_id: &Uuid,
|
||||
metadata_list: &[ChatMessageMetadata],
|
||||
index_process_sink: &mut (impl Sink<String> + Unpin),
|
||||
) -> FlowyResult<()> {
|
||||
|
@ -434,7 +433,7 @@ impl LocalAIController {
|
|||
|
||||
async fn process_index_file(
|
||||
&self,
|
||||
chat_id: &str,
|
||||
chat_id: &Uuid,
|
||||
file_path: PathBuf,
|
||||
index_metadata: &HashMap<String, serde_json::Value>,
|
||||
index_process_sink: &mut (impl Sink<String> + Unpin),
|
||||
|
@ -456,7 +455,11 @@ impl LocalAIController {
|
|||
|
||||
let result = self
|
||||
.ai_plugin
|
||||
.embed_file(chat_id, file_path, Some(index_metadata.clone()))
|
||||
.embed_file(
|
||||
&chat_id.to_string(),
|
||||
file_path,
|
||||
Some(index_metadata.clone()),
|
||||
)
|
||||
.await;
|
||||
match result {
|
||||
Ok(_) => {
|
||||
|
@ -616,6 +619,6 @@ impl LLMResourceService for LLMResourceServiceImpl {
|
|||
}
|
||||
|
||||
const APPFLOWY_LOCAL_AI_ENABLED: &str = "appflowy_local_ai_enabled";
|
||||
fn local_ai_enabled_key(workspace_id: &str) -> String {
|
||||
fn local_ai_enabled_key(workspace_id: &Uuid) -> String {
|
||||
format!("{}:{}", APPFLOWY_LOCAL_AI_ENABLED, workspace_id)
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ use serde_json::{json, Value};
|
|||
use std::path::Path;
|
||||
use std::sync::{Arc, Weak};
|
||||
use tracing::{info, trace};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub struct AICloudServiceMiddleware {
|
||||
cloud_service: Arc<dyn ChatCloudService>,
|
||||
|
@ -55,7 +56,7 @@ impl AICloudServiceMiddleware {
|
|||
|
||||
pub async fn index_message_metadata(
|
||||
&self,
|
||||
chat_id: &str,
|
||||
chat_id: &Uuid,
|
||||
metadata_list: &[ChatMessageMetadata],
|
||||
index_process_sink: &mut (impl Sink<String> + Unpin),
|
||||
) -> Result<(), FlowyError> {
|
||||
|
@ -114,9 +115,9 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
|||
async fn create_chat(
|
||||
&self,
|
||||
uid: &i64,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
rag_ids: Vec<String>,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
rag_ids: Vec<Uuid>,
|
||||
) -> Result<(), FlowyError> {
|
||||
self
|
||||
.cloud_service
|
||||
|
@ -126,8 +127,8 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
|||
|
||||
async fn create_question(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
message: &str,
|
||||
message_type: ChatMessageType,
|
||||
metadata: &[ChatMessageMetadata],
|
||||
|
@ -140,8 +141,8 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
|||
|
||||
async fn create_answer(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
message: &str,
|
||||
question_id: i64,
|
||||
metadata: Option<serde_json::Value>,
|
||||
|
@ -154,8 +155,8 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
|||
|
||||
async fn stream_answer(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
message_id: i64,
|
||||
format: ResponseFormat,
|
||||
ai_model: Option<AIModel>,
|
||||
|
@ -171,7 +172,12 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
|||
let row = self.get_message_record(message_id)?;
|
||||
match self
|
||||
.local_ai
|
||||
.stream_question(chat_id, &row.content, Some(json!(format)), json!({}))
|
||||
.stream_question(
|
||||
&chat_id.to_string(),
|
||||
&row.content,
|
||||
Some(json!(format)),
|
||||
json!({}),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(stream) => Ok(QuestionStream::new(stream).boxed()),
|
||||
|
@ -195,13 +201,17 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
|||
|
||||
async fn get_answer(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
question_message_id: i64,
|
||||
) -> Result<ChatMessage, FlowyError> {
|
||||
if self.local_ai.is_running() {
|
||||
let content = self.get_message_record(question_message_id)?.content;
|
||||
match self.local_ai.ask_question(chat_id, &content).await {
|
||||
match self
|
||||
.local_ai
|
||||
.ask_question(&chat_id.to_string(), &content)
|
||||
.await
|
||||
{
|
||||
Ok(answer) => {
|
||||
let message = self
|
||||
.cloud_service
|
||||
|
@ -224,8 +234,8 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
|||
|
||||
async fn get_chat_messages(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
offset: MessageCursor,
|
||||
limit: u64,
|
||||
) -> Result<RepeatedChatMessage, FlowyError> {
|
||||
|
@ -237,26 +247,26 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
|||
|
||||
async fn get_question_from_answer_id(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
answer_id: i64,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
answer_message_id: i64,
|
||||
) -> Result<ChatMessage, FlowyError> {
|
||||
self
|
||||
.cloud_service
|
||||
.get_question_from_answer_id(workspace_id, chat_id, answer_id)
|
||||
.get_question_from_answer_id(workspace_id, chat_id, answer_message_id)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_related_message(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
message_id: i64,
|
||||
) -> Result<RepeatedRelatedQuestion, FlowyError> {
|
||||
if self.local_ai.is_running() {
|
||||
let questions = self
|
||||
.local_ai
|
||||
.get_related_question(chat_id)
|
||||
.get_related_question(&chat_id.to_string())
|
||||
.await
|
||||
.map_err(|err| FlowyError::local_ai().with_context(err))?;
|
||||
trace!("LocalAI related questions: {:?}", questions);
|
||||
|
@ -280,7 +290,7 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
|||
|
||||
async fn stream_complete(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
params: CompleteTextParams,
|
||||
ai_model: Option<AIModel>,
|
||||
) -> Result<StreamComplete, FlowyError> {
|
||||
|
@ -329,15 +339,15 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
|||
|
||||
async fn embed_file(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
file_path: &Path,
|
||||
chat_id: &str,
|
||||
chat_id: &Uuid,
|
||||
metadata: Option<HashMap<String, Value>>,
|
||||
) -> Result<(), FlowyError> {
|
||||
if self.local_ai.is_running() {
|
||||
self
|
||||
.local_ai
|
||||
.embed_file(chat_id, file_path.to_path_buf(), metadata)
|
||||
.embed_file(&chat_id.to_string(), file_path.to_path_buf(), metadata)
|
||||
.await
|
||||
.map_err(|err| FlowyError::local_ai().with_context(err))?;
|
||||
Ok(())
|
||||
|
@ -349,21 +359,21 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
async fn get_local_ai_config(&self, workspace_id: &str) -> Result<LocalAIConfig, FlowyError> {
|
||||
async fn get_local_ai_config(&self, workspace_id: &Uuid) -> Result<LocalAIConfig, FlowyError> {
|
||||
self.cloud_service.get_local_ai_config(workspace_id).await
|
||||
}
|
||||
|
||||
async fn get_workspace_plan(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
) -> Result<Vec<SubscriptionPlan>, FlowyError> {
|
||||
self.cloud_service.get_workspace_plan(workspace_id).await
|
||||
}
|
||||
|
||||
async fn get_chat_settings(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
) -> Result<ChatSettings, FlowyError> {
|
||||
self
|
||||
.cloud_service
|
||||
|
@ -373,8 +383,8 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
|||
|
||||
async fn update_chat_settings(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
params: UpdateChatParams,
|
||||
) -> Result<(), FlowyError> {
|
||||
self
|
||||
|
@ -383,11 +393,11 @@ impl ChatCloudService for AICloudServiceMiddleware {
|
|||
.await
|
||||
}
|
||||
|
||||
async fn get_available_models(&self, workspace_id: &str) -> Result<ModelList, FlowyError> {
|
||||
async fn get_available_models(&self, workspace_id: &Uuid) -> Result<ModelList, FlowyError> {
|
||||
self.cloud_service.get_available_models(workspace_id).await
|
||||
}
|
||||
|
||||
async fn get_workspace_default_model(&self, workspace_id: &str) -> Result<String, FlowyError> {
|
||||
async fn get_workspace_default_model(&self, workspace_id: &Uuid) -> Result<String, FlowyError> {
|
||||
self
|
||||
.cloud_service
|
||||
.get_workspace_default_model(workspace_id)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use flowy_derive::ProtoBuf_Enum;
|
||||
use flowy_notification::NotificationBuilder;
|
||||
use tracing::trace;
|
||||
|
||||
const CHAT_OBSERVABLE_SOURCE: &str = "Chat";
|
||||
pub const APPFLOWY_AI_NOTIFICATION_KEY: &str = "appflowy_ai_plugin";
|
||||
|
@ -39,7 +40,12 @@ impl std::convert::From<i32> for ChatNotification {
|
|||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace")]
|
||||
pub(crate) fn chat_notification_builder(id: &str, ty: ChatNotification) -> NotificationBuilder {
|
||||
NotificationBuilder::new(id, ty, CHAT_OBSERVABLE_SOURCE)
|
||||
#[tracing::instrument(level = "trace", skip_all)]
|
||||
pub(crate) fn chat_notification_builder<T: ToString>(
|
||||
id: T,
|
||||
ty: ChatNotification,
|
||||
) -> NotificationBuilder {
|
||||
let id = id.to_string();
|
||||
trace!("chat_notification_builder: id = {id}, ty = {ty:?}");
|
||||
NotificationBuilder::new(&id, ty, CHAT_OBSERVABLE_SOURCE)
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ collab = { workspace = true }
|
|||
#collab = { workspace = true, features = ["verbose_log"] }
|
||||
|
||||
diesel.workspace = true
|
||||
uuid.workspace = true
|
||||
flowy-storage = { workspace = true }
|
||||
flowy-storage-pub = { workspace = true }
|
||||
client-api.workspace = true
|
||||
|
@ -56,10 +55,10 @@ lib-infra = { workspace = true }
|
|||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde_repr.workspace = true
|
||||
futures.workspace = true
|
||||
walkdir = "2.4.0"
|
||||
uuid.workspace = true
|
||||
sysinfo = "0.30.5"
|
||||
semver = { version = "1.0.22", features = ["serde"] }
|
||||
url = "2.5.0"
|
||||
|
||||
[features]
|
||||
profiling = ["console-subscriber", "tokio/tracing"]
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use std::fmt;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use base64::Engine;
|
||||
use semver::Version;
|
||||
use tracing::{error, info};
|
||||
use url::Url;
|
||||
|
||||
use crate::log_filter::create_log_filter;
|
||||
use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
|
||||
|
@ -28,7 +29,25 @@ pub struct AppFlowyCoreConfig {
|
|||
pub(crate) log_filter: String,
|
||||
pub cloud_config: Option<AFCloudConfiguration>,
|
||||
}
|
||||
impl AppFlowyCoreConfig {
|
||||
pub fn ensure_path(&self) {
|
||||
let create_if_needed = |path_str: &str, label: &str| {
|
||||
let dir = std::path::Path::new(path_str);
|
||||
if !dir.exists() {
|
||||
match std::fs::create_dir_all(dir) {
|
||||
Ok(_) => info!("Created {} path: {}", label, path_str),
|
||||
Err(err) => error!(
|
||||
"Failed to create {} path: {}. Error: {}",
|
||||
label, path_str, err
|
||||
),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
create_if_needed(&self.storage_path, "storage");
|
||||
create_if_needed(&self.application_path, "application");
|
||||
}
|
||||
}
|
||||
impl fmt::Debug for AppFlowyCoreConfig {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut debug = f.debug_struct("AppFlowy Configuration");
|
||||
|
@ -46,30 +65,60 @@ impl fmt::Debug for AppFlowyCoreConfig {
|
|||
}
|
||||
|
||||
fn make_user_data_folder(root: &str, url: &str) -> String {
|
||||
// Isolate the user data folder by using the base url of AppFlowy cloud. This is to avoid
|
||||
// the user data folder being shared by different AppFlowy cloud.
|
||||
let storage_path = if !url.is_empty() {
|
||||
let server_base64 = URL_SAFE_ENGINE.encode(url);
|
||||
format!("{}_{}", root, server_base64)
|
||||
// If a URL is provided, try to parse it and extract the domain name.
|
||||
// This isolates the user data folder by the domain, which prevents data sharing
|
||||
// between different AppFlowy cloud instances.
|
||||
print!("Creating user data folder for URL: {}, root:{}", url, root);
|
||||
let mut storage_path = if url.is_empty() {
|
||||
PathBuf::from(root)
|
||||
} else {
|
||||
root.to_string()
|
||||
let server_base64 = URL_SAFE_ENGINE.encode(url);
|
||||
PathBuf::from(format!("{}_{}", root, server_base64))
|
||||
};
|
||||
|
||||
// Only use new storage path if the old one doesn't exist
|
||||
if !storage_path.exists() {
|
||||
let anon_path = format!("{}_anonymous", root);
|
||||
// We use domain name as suffix to isolate the user data folder since version 0.8.9
|
||||
let new_storage_path = if url.is_empty() {
|
||||
// if the url is empty, then it's anonymous mode
|
||||
anon_path
|
||||
} else {
|
||||
match Url::parse(url) {
|
||||
Ok(parsed_url) => {
|
||||
if let Some(domain) = parsed_url.host_str() {
|
||||
format!("{}_{}", root, domain)
|
||||
} else {
|
||||
anon_path
|
||||
}
|
||||
},
|
||||
Err(_) => anon_path,
|
||||
}
|
||||
};
|
||||
|
||||
storage_path = PathBuf::from(new_storage_path);
|
||||
}
|
||||
|
||||
// Copy the user data folder from the root path to the isolated path
|
||||
// The root path without any suffix is the created by the local version AppFlowy
|
||||
if !Path::new(&storage_path).exists() && Path::new(root).exists() {
|
||||
info!("Copy dir from {} to {}", root, storage_path);
|
||||
if !storage_path.exists() && Path::new(root).exists() {
|
||||
info!("Copy dir from {} to {:?}", root, storage_path);
|
||||
let src = Path::new(root);
|
||||
match copy_dir_recursive(src, Path::new(&storage_path)) {
|
||||
Ok(_) => storage_path,
|
||||
match copy_dir_recursive(src, &storage_path) {
|
||||
Ok(_) => storage_path
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.unwrap_or_else(|_| root.to_string()),
|
||||
Err(err) => {
|
||||
// when the copy dir failed, use the root path as the storage path
|
||||
error!("Copy dir failed: {}", err);
|
||||
root.to_string()
|
||||
},
|
||||
}
|
||||
} else {
|
||||
storage_path
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.unwrap_or_else(|_| root.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,15 +132,11 @@ impl AppFlowyCoreConfig {
|
|||
name: String,
|
||||
) -> Self {
|
||||
let cloud_config = AFCloudConfiguration::from_env().ok();
|
||||
let mut log_crates = vec![];
|
||||
// By default enable sync trace log
|
||||
let log_crates = vec!["sync_trace_log".to_string()];
|
||||
let storage_path = match &cloud_config {
|
||||
None => custom_application_path,
|
||||
Some(config) => {
|
||||
if config.enable_sync_trace {
|
||||
log_crates.push("sync_trace_log".to_string());
|
||||
}
|
||||
make_user_data_folder(&custom_application_path, &config.base_url)
|
||||
},
|
||||
Some(config) => make_user_data_folder(&custom_application_path, &config.base_url),
|
||||
};
|
||||
|
||||
let log_filter = create_log_filter(
|
||||
|
|
|
@ -21,6 +21,7 @@ use std::collections::HashMap;
|
|||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Weak};
|
||||
use tracing::{error, info};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub struct ChatDepsResolver;
|
||||
|
||||
|
@ -56,9 +57,9 @@ struct ChatQueryServiceImpl {
|
|||
impl AIExternalService for ChatQueryServiceImpl {
|
||||
async fn query_chat_rag_ids(
|
||||
&self,
|
||||
parent_view_id: &str,
|
||||
chat_id: &str,
|
||||
) -> Result<Vec<String>, FlowyError> {
|
||||
parent_view_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
) -> Result<Vec<Uuid>, FlowyError> {
|
||||
let mut ids = self
|
||||
.folder_service
|
||||
.get_surrounding_view_ids_with_view_layout(parent_view_id, ViewLayout::Document)
|
||||
|
@ -72,9 +73,9 @@ impl AIExternalService for ChatQueryServiceImpl {
|
|||
}
|
||||
async fn sync_rag_documents(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
rag_ids: Vec<String>,
|
||||
mut rag_metadata_map: HashMap<String, AFCollabMetadata>,
|
||||
workspace_id: &Uuid,
|
||||
rag_ids: Vec<Uuid>,
|
||||
mut rag_metadata_map: HashMap<Uuid, AFCollabMetadata>,
|
||||
) -> Result<Vec<AFCollabMetadata>, FlowyError> {
|
||||
let mut result = Vec::new();
|
||||
|
||||
|
@ -96,7 +97,7 @@ impl AIExternalService for ChatQueryServiceImpl {
|
|||
if let Ok(prev_sv) = StateVector::decode_v1(&metadata.prev_sync_state_vector) {
|
||||
let collab = Collab::new_with_source(
|
||||
CollabOrigin::Empty,
|
||||
&rag_id,
|
||||
&rag_id.to_string(),
|
||||
DataSource::DocStateV1(query_collab.encoded_collab.doc_state.to_vec()),
|
||||
vec![],
|
||||
false,
|
||||
|
@ -111,7 +112,7 @@ impl AIExternalService for ChatQueryServiceImpl {
|
|||
|
||||
// Perform full sync if changes are detected or no state vector is found
|
||||
let params = FullSyncCollabParams {
|
||||
object_id: rag_id.clone(),
|
||||
object_id: rag_id,
|
||||
collab_type: CollabType::Document,
|
||||
encoded_collab: query_collab.encoded_collab.clone(),
|
||||
};
|
||||
|
@ -125,7 +126,7 @@ impl AIExternalService for ChatQueryServiceImpl {
|
|||
} else {
|
||||
info!("[Chat] full sync rag document: {}", rag_id);
|
||||
result.push(AFCollabMetadata {
|
||||
object_id: rag_id,
|
||||
object_id: rag_id.to_string(),
|
||||
updated_at: timestamp(),
|
||||
prev_sync_state_vector: query_collab.encoded_collab.state_vector.to_vec(),
|
||||
collab_type: CollabType::Document as i32,
|
||||
|
@ -136,7 +137,7 @@ impl AIExternalService for ChatQueryServiceImpl {
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
async fn notify_did_send_message(&self, chat_id: &str, message: &str) -> Result<(), FlowyError> {
|
||||
async fn notify_did_send_message(&self, chat_id: &Uuid, message: &str) -> Result<(), FlowyError> {
|
||||
info!(
|
||||
"notify_did_send_message: chat_id: {}, message: {}",
|
||||
chat_id, message
|
||||
|
@ -169,7 +170,7 @@ impl AIUserService for ChatUserServiceImpl {
|
|||
self.upgrade_user()?.device_id()
|
||||
}
|
||||
|
||||
fn workspace_id(&self) -> Result<String, FlowyError> {
|
||||
fn workspace_id(&self) -> Result<Uuid, FlowyError> {
|
||||
self.upgrade_user()?.workspace_id()
|
||||
}
|
||||
|
||||
|
|
|
@ -7,16 +7,6 @@ use collab::core::origin::{CollabClient, CollabOrigin};
|
|||
use collab::entity::EncodedCollab;
|
||||
use collab::preclude::CollabPlugin;
|
||||
use collab_entity::CollabType;
|
||||
use flowy_search_pub::cloud::SearchCloudService;
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio_stream::wrappers::WatchStream;
|
||||
use tracing::{debug, info};
|
||||
|
||||
use collab_integrate::collab_builder::{
|
||||
CollabCloudPluginProvider, CollabPluginProviderContext, CollabPluginProviderType,
|
||||
};
|
||||
|
@ -37,12 +27,24 @@ use flowy_folder_pub::cloud::{
|
|||
Workspace, WorkspaceRecord,
|
||||
};
|
||||
use flowy_folder_pub::entities::PublishPayload;
|
||||
use flowy_search_pub::cloud::SearchCloudService;
|
||||
use flowy_server_pub::af_cloud_config::AFCloudConfiguration;
|
||||
use flowy_storage_pub::cloud::{ObjectIdentity, ObjectValue, StorageCloudService};
|
||||
use flowy_storage_pub::storage::{CompletedPartRequest, CreateUploadResponse, UploadPartResponse};
|
||||
use flowy_user_pub::cloud::{UserCloudService, UserCloudServiceProvider};
|
||||
use flowy_user_pub::entities::{Authenticator, UserTokenState};
|
||||
use lib_infra::async_trait::async_trait;
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio_stream::wrappers::WatchStream;
|
||||
use tracing::log::error;
|
||||
use tracing::{debug, info};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::server_layer::{Server, ServerProvider};
|
||||
|
||||
|
@ -82,7 +84,7 @@ impl StorageCloudService for ServerProvider {
|
|||
|
||||
async fn get_object_url_v1(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
parent_dir: &str,
|
||||
file_id: &str,
|
||||
) -> FlowyResult<String> {
|
||||
|
@ -93,7 +95,7 @@ impl StorageCloudService for ServerProvider {
|
|||
.await
|
||||
}
|
||||
|
||||
async fn parse_object_url_v1(&self, url: &str) -> Option<(String, String, String)> {
|
||||
async fn parse_object_url_v1(&self, url: &str) -> Option<(Uuid, String, String)> {
|
||||
self
|
||||
.get_server()
|
||||
.ok()?
|
||||
|
@ -104,7 +106,7 @@ impl StorageCloudService for ServerProvider {
|
|||
|
||||
async fn create_upload(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
parent_dir: &str,
|
||||
file_id: &str,
|
||||
content_type: &str,
|
||||
|
@ -119,7 +121,7 @@ impl StorageCloudService for ServerProvider {
|
|||
|
||||
async fn upload_part(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
parent_dir: &str,
|
||||
upload_id: &str,
|
||||
file_id: &str,
|
||||
|
@ -142,7 +144,7 @@ impl StorageCloudService for ServerProvider {
|
|||
|
||||
async fn complete_upload(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
parent_dir: &str,
|
||||
upload_id: &str,
|
||||
file_id: &str,
|
||||
|
@ -233,10 +235,9 @@ impl FolderCloudService for ServerProvider {
|
|||
server.folder_service().create_workspace(uid, &name).await
|
||||
}
|
||||
|
||||
async fn open_workspace(&self, workspace_id: &str) -> Result<(), FlowyError> {
|
||||
let workspace_id = workspace_id.to_string();
|
||||
async fn open_workspace(&self, workspace_id: &Uuid) -> Result<(), FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
server.folder_service().open_workspace(&workspace_id).await
|
||||
server.folder_service().open_workspace(workspace_id).await
|
||||
}
|
||||
|
||||
async fn get_all_workspace(&self) -> Result<Vec<WorkspaceRecord>, FlowyError> {
|
||||
|
@ -246,7 +247,7 @@ impl FolderCloudService for ServerProvider {
|
|||
|
||||
async fn get_folder_data(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
uid: &i64,
|
||||
) -> Result<Option<FolderData>, FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
|
@ -272,10 +273,10 @@ impl FolderCloudService for ServerProvider {
|
|||
|
||||
async fn get_folder_doc_state(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
uid: i64,
|
||||
collab_type: CollabType,
|
||||
object_id: &str,
|
||||
object_id: &Uuid,
|
||||
) -> Result<Vec<u8>, FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
|
||||
|
@ -287,7 +288,7 @@ impl FolderCloudService for ServerProvider {
|
|||
|
||||
async fn batch_create_folder_collab_objects(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
objects: Vec<FolderCollabParams>,
|
||||
) -> Result<(), FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
|
@ -307,7 +308,7 @@ impl FolderCloudService for ServerProvider {
|
|||
|
||||
async fn publish_view(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
payload: Vec<PublishPayload>,
|
||||
) -> Result<(), FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
|
@ -320,8 +321,8 @@ impl FolderCloudService for ServerProvider {
|
|||
|
||||
async fn unpublish_views(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
view_ids: Vec<String>,
|
||||
workspace_id: &Uuid,
|
||||
view_ids: Vec<Uuid>,
|
||||
) -> Result<(), FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
server
|
||||
|
@ -330,15 +331,15 @@ impl FolderCloudService for ServerProvider {
|
|||
.await
|
||||
}
|
||||
|
||||
async fn get_publish_info(&self, view_id: &str) -> Result<PublishInfo, FlowyError> {
|
||||
async fn get_publish_info(&self, view_id: &Uuid) -> Result<PublishInfo, FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
server.folder_service().get_publish_info(view_id).await
|
||||
}
|
||||
|
||||
async fn set_publish_name(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
view_id: String,
|
||||
workspace_id: &Uuid,
|
||||
view_id: Uuid,
|
||||
new_name: String,
|
||||
) -> Result<(), FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
|
@ -350,7 +351,7 @@ impl FolderCloudService for ServerProvider {
|
|||
|
||||
async fn set_publish_namespace(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
new_namespace: String,
|
||||
) -> Result<(), FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
|
@ -360,7 +361,7 @@ impl FolderCloudService for ServerProvider {
|
|||
.await
|
||||
}
|
||||
|
||||
async fn get_publish_namespace(&self, workspace_id: &str) -> Result<String, FlowyError> {
|
||||
async fn get_publish_namespace(&self, workspace_id: &Uuid) -> Result<String, FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
server
|
||||
.folder_service()
|
||||
|
@ -371,7 +372,7 @@ impl FolderCloudService for ServerProvider {
|
|||
/// List all published views of the current workspace.
|
||||
async fn list_published_views(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
) -> Result<Vec<PublishInfoView>, FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
server
|
||||
|
@ -382,7 +383,7 @@ impl FolderCloudService for ServerProvider {
|
|||
|
||||
async fn get_default_published_view_info(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
) -> Result<PublishInfo, FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
server
|
||||
|
@ -393,7 +394,7 @@ impl FolderCloudService for ServerProvider {
|
|||
|
||||
async fn set_default_published_view(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
view_id: uuid::Uuid,
|
||||
) -> Result<(), FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
|
@ -403,7 +404,7 @@ impl FolderCloudService for ServerProvider {
|
|||
.await
|
||||
}
|
||||
|
||||
async fn remove_default_published_view(&self, workspace_id: &str) -> Result<(), FlowyError> {
|
||||
async fn remove_default_published_view(&self, workspace_id: &Uuid) -> Result<(), FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
server
|
||||
.folder_service()
|
||||
|
@ -421,7 +422,7 @@ impl FolderCloudService for ServerProvider {
|
|||
|
||||
async fn full_sync_collab_object(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
params: FullSyncCollabParams,
|
||||
) -> Result<(), FlowyError> {
|
||||
self
|
||||
|
@ -436,24 +437,22 @@ impl FolderCloudService for ServerProvider {
|
|||
impl DatabaseCloudService for ServerProvider {
|
||||
async fn get_database_encode_collab(
|
||||
&self,
|
||||
object_id: &str,
|
||||
object_id: &Uuid,
|
||||
collab_type: CollabType,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
) -> Result<Option<EncodedCollab>, FlowyError> {
|
||||
let workspace_id = workspace_id.to_string();
|
||||
let server = self.get_server()?;
|
||||
let database_id = object_id.to_string();
|
||||
server
|
||||
.database_service()
|
||||
.get_database_encode_collab(&database_id, collab_type, &workspace_id)
|
||||
.get_database_encode_collab(object_id, collab_type, workspace_id)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn create_database_encode_collab(
|
||||
&self,
|
||||
object_id: &str,
|
||||
object_id: &Uuid,
|
||||
collab_type: CollabType,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
encoded_collab: EncodedCollab,
|
||||
) -> Result<(), FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
|
@ -465,30 +464,28 @@ impl DatabaseCloudService for ServerProvider {
|
|||
|
||||
async fn batch_get_database_encode_collab(
|
||||
&self,
|
||||
object_ids: Vec<String>,
|
||||
object_ids: Vec<Uuid>,
|
||||
object_ty: CollabType,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
) -> Result<EncodeCollabByOid, FlowyError> {
|
||||
let workspace_id = workspace_id.to_string();
|
||||
let server = self.get_server()?;
|
||||
|
||||
server
|
||||
.database_service()
|
||||
.batch_get_database_encode_collab(object_ids, object_ty, &workspace_id)
|
||||
.batch_get_database_encode_collab(object_ids, object_ty, workspace_id)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_database_collab_object_snapshots(
|
||||
&self,
|
||||
object_id: &str,
|
||||
object_id: &Uuid,
|
||||
limit: usize,
|
||||
) -> Result<Vec<DatabaseSnapshot>, FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
let database_id = object_id.to_string();
|
||||
|
||||
server
|
||||
.database_service()
|
||||
.get_database_collab_object_snapshots(&database_id, limit)
|
||||
.get_database_collab_object_snapshots(object_id, limit)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
@ -497,29 +494,29 @@ impl DatabaseCloudService for ServerProvider {
|
|||
impl DatabaseAIService for ServerProvider {
|
||||
async fn summary_database_row(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
object_id: &str,
|
||||
summary_row: SummaryRowContent,
|
||||
_workspace_id: &Uuid,
|
||||
_object_id: &Uuid,
|
||||
_summary_row: SummaryRowContent,
|
||||
) -> Result<String, FlowyError> {
|
||||
self
|
||||
.get_server()?
|
||||
.database_ai_service()
|
||||
.ok_or_else(FlowyError::not_support)?
|
||||
.summary_database_row(workspace_id, object_id, summary_row)
|
||||
.summary_database_row(_workspace_id, _object_id, _summary_row)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn translate_database_row(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
translate_row: TranslateRowContent,
|
||||
language: &str,
|
||||
_workspace_id: &Uuid,
|
||||
_translate_row: TranslateRowContent,
|
||||
_language: &str,
|
||||
) -> Result<TranslateRowResponse, FlowyError> {
|
||||
self
|
||||
.get_server()?
|
||||
.database_ai_service()
|
||||
.ok_or_else(FlowyError::not_support)?
|
||||
.translate_database_row(workspace_id, translate_row, language)
|
||||
.translate_database_row(_workspace_id, _translate_row, _language)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
@ -528,8 +525,8 @@ impl DatabaseAIService for ServerProvider {
|
|||
impl DocumentCloudService for ServerProvider {
|
||||
async fn get_document_doc_state(
|
||||
&self,
|
||||
document_id: &str,
|
||||
workspace_id: &str,
|
||||
document_id: &Uuid,
|
||||
workspace_id: &Uuid,
|
||||
) -> Result<Vec<u8>, FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
server
|
||||
|
@ -540,7 +537,7 @@ impl DocumentCloudService for ServerProvider {
|
|||
|
||||
async fn get_document_snapshots(
|
||||
&self,
|
||||
document_id: &str,
|
||||
document_id: &Uuid,
|
||||
limit: usize,
|
||||
workspace_id: &str,
|
||||
) -> Result<Vec<DocumentSnapshot>, FlowyError> {
|
||||
|
@ -554,8 +551,8 @@ impl DocumentCloudService for ServerProvider {
|
|||
|
||||
async fn get_document_data(
|
||||
&self,
|
||||
document_id: &str,
|
||||
workspace_id: &str,
|
||||
document_id: &Uuid,
|
||||
workspace_id: &Uuid,
|
||||
) -> Result<Option<DocumentData>, FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
server
|
||||
|
@ -566,8 +563,8 @@ impl DocumentCloudService for ServerProvider {
|
|||
|
||||
async fn create_document_collab(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
document_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
document_id: &Uuid,
|
||||
encoded_collab: EncodedCollab,
|
||||
) -> Result<(), FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
|
@ -611,26 +608,37 @@ impl CollabCloudPluginProvider for ServerProvider {
|
|||
collab_object.uid,
|
||||
collab_object.device_id.clone(),
|
||||
));
|
||||
let sync_object = SyncObject::new(
|
||||
&collab_object.object_id,
|
||||
&collab_object.workspace_id,
|
||||
collab_object.collab_type,
|
||||
&collab_object.device_id,
|
||||
);
|
||||
let (sink, stream) = (channel.sink(), channel.stream());
|
||||
let sink_config = SinkConfig::new().send_timeout(8);
|
||||
let sync_plugin = SyncPlugin::new(
|
||||
origin,
|
||||
sync_object,
|
||||
local_collab,
|
||||
sink,
|
||||
sink_config,
|
||||
stream,
|
||||
Some(channel),
|
||||
ws_connect_state,
|
||||
Some(Duration::from_secs(60)),
|
||||
);
|
||||
plugins.push(Box::new(sync_plugin));
|
||||
|
||||
if let (Ok(object_id), Ok(workspace_id)) = (
|
||||
Uuid::from_str(&collab_object.object_id),
|
||||
Uuid::from_str(&collab_object.workspace_id),
|
||||
) {
|
||||
let sync_object = SyncObject::new(
|
||||
object_id,
|
||||
workspace_id,
|
||||
collab_object.collab_type,
|
||||
&collab_object.device_id,
|
||||
);
|
||||
let (sink, stream) = (channel.sink(), channel.stream());
|
||||
let sink_config = SinkConfig::new().send_timeout(8);
|
||||
let sync_plugin = SyncPlugin::new(
|
||||
origin,
|
||||
sync_object,
|
||||
local_collab,
|
||||
sink,
|
||||
sink_config,
|
||||
stream,
|
||||
Some(channel),
|
||||
ws_connect_state,
|
||||
Some(Duration::from_secs(60)),
|
||||
);
|
||||
plugins.push(Box::new(sync_plugin));
|
||||
} else {
|
||||
error!(
|
||||
"Failed to parse collab object id: {}",
|
||||
collab_object.object_id
|
||||
);
|
||||
}
|
||||
},
|
||||
Ok(None) => {
|
||||
tracing::error!("🔴Failed to get collab ws channel: channel is none");
|
||||
|
@ -655,9 +663,9 @@ impl ChatCloudService for ServerProvider {
|
|||
async fn create_chat(
|
||||
&self,
|
||||
uid: &i64,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
rag_ids: Vec<String>,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
rag_ids: Vec<Uuid>,
|
||||
) -> Result<(), FlowyError> {
|
||||
let server = self.get_server();
|
||||
server?
|
||||
|
@ -668,26 +676,24 @@ impl ChatCloudService for ServerProvider {
|
|||
|
||||
async fn create_question(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
message: &str,
|
||||
message_type: ChatMessageType,
|
||||
metadata: &[ChatMessageMetadata],
|
||||
) -> Result<ChatMessage, FlowyError> {
|
||||
let workspace_id = workspace_id.to_string();
|
||||
let chat_id = chat_id.to_string();
|
||||
let message = message.to_string();
|
||||
self
|
||||
.get_server()?
|
||||
.chat_service()
|
||||
.create_question(&workspace_id, &chat_id, &message, message_type, metadata)
|
||||
.create_question(workspace_id, chat_id, &message, message_type, metadata)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn create_answer(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
message: &str,
|
||||
question_id: i64,
|
||||
metadata: Option<serde_json::Value>,
|
||||
|
@ -701,25 +707,23 @@ impl ChatCloudService for ServerProvider {
|
|||
|
||||
async fn stream_answer(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
message_id: i64,
|
||||
format: ResponseFormat,
|
||||
ai_model: Option<AIModel>,
|
||||
) -> Result<StreamAnswer, FlowyError> {
|
||||
let workspace_id = workspace_id.to_string();
|
||||
let chat_id = chat_id.to_string();
|
||||
let server = self.get_server()?;
|
||||
server
|
||||
.chat_service()
|
||||
.stream_answer(&workspace_id, &chat_id, message_id, format, ai_model)
|
||||
.stream_answer(workspace_id, chat_id, message_id, format, ai_model)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_chat_messages(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
offset: MessageCursor,
|
||||
limit: u64,
|
||||
) -> Result<RepeatedChatMessage, FlowyError> {
|
||||
|
@ -732,8 +736,8 @@ impl ChatCloudService for ServerProvider {
|
|||
|
||||
async fn get_question_from_answer_id(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
answer_message_id: i64,
|
||||
) -> Result<ChatMessage, FlowyError> {
|
||||
self
|
||||
|
@ -745,8 +749,8 @@ impl ChatCloudService for ServerProvider {
|
|||
|
||||
async fn get_related_message(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
message_id: i64,
|
||||
) -> Result<RepeatedRelatedQuestion, FlowyError> {
|
||||
self
|
||||
|
@ -758,8 +762,8 @@ impl ChatCloudService for ServerProvider {
|
|||
|
||||
async fn get_answer(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
question_message_id: i64,
|
||||
) -> Result<ChatMessage, FlowyError> {
|
||||
let server = self.get_server();
|
||||
|
@ -771,23 +775,22 @@ impl ChatCloudService for ServerProvider {
|
|||
|
||||
async fn stream_complete(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
params: CompleteTextParams,
|
||||
ai_model: Option<AIModel>,
|
||||
) -> Result<StreamComplete, FlowyError> {
|
||||
let workspace_id = workspace_id.to_string();
|
||||
let server = self.get_server()?;
|
||||
server
|
||||
.chat_service()
|
||||
.stream_complete(&workspace_id, params, ai_model)
|
||||
.stream_complete(workspace_id, params, ai_model)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn embed_file(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
file_path: &Path,
|
||||
chat_id: &str,
|
||||
chat_id: &Uuid,
|
||||
metadata: Option<HashMap<String, Value>>,
|
||||
) -> Result<(), FlowyError> {
|
||||
self
|
||||
|
@ -797,7 +800,7 @@ impl ChatCloudService for ServerProvider {
|
|||
.await
|
||||
}
|
||||
|
||||
async fn get_local_ai_config(&self, workspace_id: &str) -> Result<LocalAIConfig, FlowyError> {
|
||||
async fn get_local_ai_config(&self, workspace_id: &Uuid) -> Result<LocalAIConfig, FlowyError> {
|
||||
self
|
||||
.get_server()?
|
||||
.chat_service()
|
||||
|
@ -807,7 +810,7 @@ impl ChatCloudService for ServerProvider {
|
|||
|
||||
async fn get_workspace_plan(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
) -> Result<Vec<SubscriptionPlan>, FlowyError> {
|
||||
self
|
||||
.get_server()?
|
||||
|
@ -818,8 +821,8 @@ impl ChatCloudService for ServerProvider {
|
|||
|
||||
async fn get_chat_settings(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
) -> Result<ChatSettings, FlowyError> {
|
||||
self
|
||||
.get_server()?
|
||||
|
@ -830,8 +833,8 @@ impl ChatCloudService for ServerProvider {
|
|||
|
||||
async fn update_chat_settings(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
chat_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
chat_id: &Uuid,
|
||||
params: UpdateChatParams,
|
||||
) -> Result<(), FlowyError> {
|
||||
self
|
||||
|
@ -841,7 +844,7 @@ impl ChatCloudService for ServerProvider {
|
|||
.await
|
||||
}
|
||||
|
||||
async fn get_available_models(&self, workspace_id: &str) -> Result<ModelList, FlowyError> {
|
||||
async fn get_available_models(&self, workspace_id: &Uuid) -> Result<ModelList, FlowyError> {
|
||||
self
|
||||
.get_server()?
|
||||
.chat_service()
|
||||
|
@ -849,7 +852,7 @@ impl ChatCloudService for ServerProvider {
|
|||
.await
|
||||
}
|
||||
|
||||
async fn get_workspace_default_model(&self, workspace_id: &str) -> Result<String, FlowyError> {
|
||||
async fn get_workspace_default_model(&self, workspace_id: &Uuid) -> Result<String, FlowyError> {
|
||||
self
|
||||
.get_server()?
|
||||
.chat_service()
|
||||
|
@ -862,7 +865,7 @@ impl ChatCloudService for ServerProvider {
|
|||
impl SearchCloudService for ServerProvider {
|
||||
async fn document_search(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
workspace_id: &Uuid,
|
||||
query: String,
|
||||
) -> Result<Vec<SearchDocumentResponseItem>, FlowyError> {
|
||||
let server = self.get_server()?;
|
||||
|
|
|
@ -13,6 +13,7 @@ use collab_integrate::collab_builder::WorkspaceCollabIntegrate;
|
|||
use lib_infra::util::timestamp;
|
||||
use std::sync::{Arc, Weak};
|
||||
use tracing::debug;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub struct SnapshotDBImpl(pub Weak<AuthenticateUser>);
|
||||
|
||||
|
@ -222,12 +223,12 @@ impl WorkspaceCollabIntegrateImpl {
|
|||
}
|
||||
|
||||
impl WorkspaceCollabIntegrate for WorkspaceCollabIntegrateImpl {
|
||||
fn workspace_id(&self) -> Result<String, anyhow::Error> {
|
||||
fn workspace_id(&self) -> Result<Uuid, FlowyError> {
|
||||
let workspace_id = self.upgrade_user()?.workspace_id()?;
|
||||
Ok(workspace_id)
|
||||
}
|
||||
|
||||
fn device_id(&self) -> Result<String, anyhow::Error> {
|
||||
fn device_id(&self) -> Result<String, FlowyError> {
|
||||
Ok(self.upgrade_user()?.user_config.device_id.clone())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ use lib_infra::async_trait::async_trait;
|
|||
use lib_infra::priority_task::TaskDispatcher;
|
||||
use std::sync::{Arc, Weak};
|
||||
use tokio::sync::RwLock;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub struct DatabaseDepsResolver();
|
||||
|
||||
|
@ -47,41 +48,41 @@ struct DatabaseAIServiceMiddleware {
|
|||
impl DatabaseAIService for DatabaseAIServiceMiddleware {
|
||||
async fn summary_database_row(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
object_id: &str,
|
||||
summary_row: SummaryRowContent,
|
||||
workspace_id: &Uuid,
|
||||
object_id: &Uuid,
|
||||
_summary_row: SummaryRowContent,
|
||||
) -> Result<String, FlowyError> {
|
||||
if self.ai_manager.local_ai.is_running() {
|
||||
self
|
||||
.ai_manager
|
||||
.local_ai
|
||||
.summary_database_row(summary_row)
|
||||
.summary_database_row(_summary_row)
|
||||
.await
|
||||
.map_err(|err| FlowyError::local_ai().with_context(err))
|
||||
} else {
|
||||
self
|
||||
.ai_service
|
||||
.summary_database_row(workspace_id, object_id, summary_row)
|
||||
.summary_database_row(workspace_id, object_id, _summary_row)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
async fn translate_database_row(
|
||||
&self,
|
||||
workspace_id: &str,
|
||||
translate_row: TranslateRowContent,
|
||||
language: &str,
|
||||
_workspace_id: &Uuid,
|
||||
_translate_row: TranslateRowContent,
|
||||
_language: &str,
|
||||
) -> Result<TranslateRowResponse, FlowyError> {
|
||||
if self.ai_manager.local_ai.is_running() {
|
||||
let data = LocalAITranslateRowData {
|
||||
cells: translate_row
|
||||
cells: _translate_row
|
||||
.into_iter()
|
||||
.map(|row| LocalAITranslateItem {
|
||||
title: row.title,
|
||||
content: row.content,
|
||||
})
|
||||
.collect(),
|
||||
language: language.to_string(),
|
||||
language: _language.to_string(),
|
||||
include_header: false,
|
||||
};
|
||||
let resp = self
|
||||
|
@ -95,7 +96,7 @@ impl DatabaseAIService for DatabaseAIServiceMiddleware {
|
|||
} else {
|
||||
self
|
||||
.ai_service
|
||||
.translate_database_row(workspace_id, translate_row, language)
|
||||
.translate_database_row(_workspace_id, _translate_row, _language)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
@ -121,11 +122,11 @@ impl DatabaseUser for DatabaseUserImpl {
|
|||
self.upgrade_user()?.get_collab_db(uid)
|
||||
}
|
||||
|
||||
fn workspace_id(&self) -> Result<String, FlowyError> {
|
||||
fn workspace_id(&self) -> Result<Uuid, FlowyError> {
|
||||
self.upgrade_user()?.workspace_id()
|
||||
}
|
||||
|
||||
fn workspace_database_object_id(&self) -> Result<String, FlowyError> {
|
||||
fn workspace_database_object_id(&self) -> Result<Uuid, FlowyError> {
|
||||
self.upgrade_user()?.workspace_database_object_id()
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue