From c4cdcbff73414543b3a0028a002508e204047056 Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Wed, 28 Aug 2024 09:15:42 +0800 Subject: [PATCH 1/5] chore: disable old import type (#6089) --- .../presentation/home/menu/sidebar/import/import_type.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/import/import_type.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/import/import_type.dart index 3728cbee7b..5465bb0533 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/import/import_type.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/import/import_type.dart @@ -48,6 +48,8 @@ enum ImportType { bool get enableOnRelease { switch (this) { + case ImportType.historyDatabase: + case ImportType.historyDocument: case ImportType.databaseRawData: return kDebugMode; default: From 7541dff00ec824759fae06436532fbc3762eb284 Mon Sep 17 00:00:00 2001 From: "Kilu.He" <108015703+qinluhe@users.noreply.github.com> Date: Wed, 28 Aug 2024 10:30:08 +0800 Subject: [PATCH 2/5] fix: some file block issues (#6085) --- .../components/blocks/file/FileBlock.tsx | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/frontend/appflowy_web_app/src/components/editor/components/blocks/file/FileBlock.tsx b/frontend/appflowy_web_app/src/components/editor/components/blocks/file/FileBlock.tsx index ecb5a49b4e..ed6c652462 100644 --- a/frontend/appflowy_web_app/src/components/editor/components/blocks/file/FileBlock.tsx +++ b/frontend/appflowy_web_app/src/components/editor/components/blocks/file/FileBlock.tsx @@ -1,17 +1,15 @@ -import { FieldURLType } from '@/application/collab.type'; import { notify } from '@/components/_shared/notify'; import RightTopActionsToolbar from '@/components/editor/components/block-actions/RightTopActionsToolbar'; import { EditorElementProps, FileNode } from '@/components/editor/editor.type'; import { copyTextToClipboard } from '@/utils/copy'; import { downloadFile } from '@/utils/download'; -import { renderDate } from '@/utils/time'; import React, { forwardRef, memo, useCallback, useMemo, useState } from 'react'; import { ReactComponent as FileIcon } from '@/assets/file_upload.svg'; import { useTranslation } from 'react-i18next'; export const FileBlock = memo( forwardRef>(({ node, children, ...attributes }, ref) => { - const { url, name, url_type, uploaded_at } = useMemo(() => node.data || {}, [node.data]); + const { url, name } = useMemo(() => node.data || {}, [node.data]); const className = useMemo(() => { const classList = ['w-full bg-bg-body py-2']; @@ -41,20 +39,6 @@ export const FileBlock = memo( } }, [url, name]); - const uploadTypePrefix = useMemo(() => { - const time = renderDate(uploaded_at, 'MMM DD, YYYY', false); - - if (url_type === FieldURLType.Upload) { - return t('web.fileBlock.uploadedAt', { - time, - }); - } else { - return t('web.fileBlock.linkedAt', { - time, - }); - } - }, [uploaded_at, url_type, t]); - return (
{name?.trim() || t('document.title.placeholder')}
-
- {uploadTypePrefix} -
:
{t('web.fileBlock.empty')} From 956d62fe82f2ce0fe04e48ddb4fb9677fd4ae4be Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Wed, 28 Aug 2024 13:22:07 +0800 Subject: [PATCH 3/5] fix: expand the icon to be the same size as the text in the heading block (#6093) * fix: hidden board group padding * fix: expand icon in heading style --- .../widgets/board_hidden_groups.dart | 86 ++++---- .../mention/mention_page_block.dart | 193 +++++++++++++----- .../document/presentation/editor_style.dart | 3 +- 3 files changed, 179 insertions(+), 103 deletions(-) diff --git a/frontend/appflowy_flutter/lib/plugins/database/board/presentation/widgets/board_hidden_groups.dart b/frontend/appflowy_flutter/lib/plugins/database/board/presentation/widgets/board_hidden_groups.dart index 98dd7a8eaf..a44cf1c55d 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/board/presentation/widgets/board_hidden_groups.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/board/presentation/widgets/board_hidden_groups.dart @@ -42,6 +42,8 @@ class HiddenGroupsColumn extends StatelessWidget { return const SizedBox.shrink(); } final isCollapsed = layoutSettings.collapseHiddenGroups; + final leftPadding = margin.left + + context.read().horizontalPadding; return AnimatedSize( alignment: AlignmentDirectional.topStart, curve: Curves.easeOut, @@ -56,35 +58,29 @@ class HiddenGroupsColumn extends StatelessWidget { ), ), ) - : SizedBox( + : Container( width: 274, + padding: EdgeInsets.only( + left: leftPadding, + right: margin.right + 4, + ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: 50, - child: Padding( - padding: EdgeInsets.only( - left: margin.left + - context - .read() - .horizontalPadding, - right: margin.right + 4, - ), - child: Row( - children: [ - Expanded( - child: FlowyText.medium( - LocaleKeys - .board_hiddenGroupSection_sectionTitle - .tr(), - overflow: TextOverflow.ellipsis, - color: Theme.of(context).hintColor, - ), + child: Row( + children: [ + Expanded( + child: FlowyText.medium( + LocaleKeys.board_hiddenGroupSection_sectionTitle + .tr(), + overflow: TextOverflow.ellipsis, + color: Theme.of(context).hintColor, ), - _collapseExpandIcon(context, isCollapsed), - ], - ), + ), + _collapseExpandIcon(context, isCollapsed), + ], ), ), Expanded( @@ -204,31 +200,27 @@ class _HiddenGroupCardState extends State { final databaseController = widget.bloc.databaseController; final primaryField = databaseController.fieldController.fieldInfos .firstWhereOrNull((element) => element.isPrimary)!; - - return Padding( - padding: const EdgeInsets.only(left: 26), - child: AppFlowyPopover( - controller: _popoverController, - direction: PopoverDirection.bottomWithCenterAligned, - triggerActions: PopoverTriggerFlags.none, - constraints: const BoxConstraints(maxWidth: 234, maxHeight: 300), - popupBuilder: (popoverContext) { - return BlocProvider.value( - value: context.read(), - child: HiddenGroupPopupItemList( - viewId: databaseController.viewId, - groupId: widget.group.groupId, - primaryFieldId: primaryField.id, - rowCache: databaseController.rowCache, - ), - ); - }, - child: HiddenGroupButtonContent( - popoverController: _popoverController, - groupId: widget.group.groupId, - index: widget.index, - bloc: widget.bloc, - ), + return AppFlowyPopover( + controller: _popoverController, + direction: PopoverDirection.bottomWithCenterAligned, + triggerActions: PopoverTriggerFlags.none, + constraints: const BoxConstraints(maxWidth: 234, maxHeight: 300), + popupBuilder: (popoverContext) { + return BlocProvider.value( + value: context.read(), + child: HiddenGroupPopupItemList( + viewId: databaseController.viewId, + groupId: widget.group.groupId, + primaryFieldId: primaryField.id, + rowCache: databaseController.rowCache, + ), + ); + }, + child: HiddenGroupButtonContent( + popoverController: _popoverController, + groupId: widget.group.groupId, + index: widget.index, + bloc: widget.bloc, ), ); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/mention_page_block.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/mention_page_block.dart index 34095e7512..39084c7fa3 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/mention_page_block.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mention/mention_page_block.dart @@ -1,7 +1,6 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/mobile/application/mobile_router.dart'; -import 'package:appflowy/plugins/base/emoji/emoji_text.dart'; import 'package:appflowy/plugins/document/application/document_bloc.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_block.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mobile_page_selector_sheet.dart'; @@ -106,70 +105,32 @@ class _MentionPageBlockState extends State { final view = state.data; // memorize the result pageMemorizer[widget.pageId] = view; + if (view == null) { - return FlowyHover( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: FlowyText( - LocaleKeys.document_mention_noAccess.tr(), - color: Theme.of(context).disabledColor, - decoration: TextDecoration.underline, - fontSize: widget.textStyle?.fontSize, - fontWeight: widget.textStyle?.fontWeight, - ), - ), + return _NoAccessMentionPageBlock( + textStyle: widget.textStyle, ); } - final iconSize = widget.textStyle?.fontSize ?? 16.0; - Widget child = Row( - mainAxisSize: MainAxisSize.min, - children: [ - const HSpace(4), - view.icon.value.isNotEmpty - ? EmojiText( - emoji: view.icon.value, - fontSize: 12, - textAlign: TextAlign.center, - lineHeight: 1.3, - ) - : FlowySvg( - view.layout.icon, - size: Size.square(iconSize + 2.0), - ), - const HSpace(2), - FlowyText( - view.name, - decoration: TextDecoration.underline, - fontSize: widget.textStyle?.fontSize, - fontWeight: widget.textStyle?.fontWeight, - ), - const HSpace(4), - ], - ); - - if (PlatformExtension.isDesktop) { - child = Padding( - padding: const EdgeInsets.symmetric(horizontal: 2), - child: FlowyHover( - cursor: SystemMouseCursors.click, - child: child, - ), + if (PlatformExtension.isMobile) { + return _MobileMentionPageBlock( + view: view, + textStyle: widget.textStyle, + handleTap: handleTap, + handleDoubleTap: handleDoubleTap, + ); + } else { + return _DesktopMentionPageBlock( + view: view, + textStyle: widget.textStyle, + handleTap: handleTap, ); } - - return GestureDetector( - onTap: handleTap, - onDoubleTap: PlatformExtension.isMobile ? handleDoubleTap : null, - behavior: HitTestBehavior.opaque, - child: child, - ); }, ); } Future handleTap() async { - debugPrint('handleTap'); final view = await fetchView(widget.pageId); if (view == null) { Log.error('Page(${widget.pageId}) not found'); @@ -246,3 +207,127 @@ class _MentionPageBlockState extends State { }); } } + +class _MentionPageBlockContent extends StatelessWidget { + const _MentionPageBlockContent({ + required this.view, + required this.textStyle, + }); + + final ViewPB view; + final TextStyle? textStyle; + + @override + Widget build(BuildContext context) { + final emojiSize = textStyle?.fontSize ?? 12.0; + final iconSize = textStyle?.fontSize ?? 16.0; + + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + const HSpace(4), + view.icon.value.isNotEmpty + ? FlowyText.emoji( + view.icon.value, + fontSize: emojiSize, + lineHeight: textStyle?.height, + optimizeEmojiAlign: true, + ) + : FlowySvg( + view.layout.icon, + size: Size.square(iconSize + 2.0), + ), + const HSpace(2), + FlowyText( + view.name, + decoration: TextDecoration.underline, + fontSize: textStyle?.fontSize, + fontWeight: textStyle?.fontWeight, + lineHeight: textStyle?.height, + ), + const HSpace(4), + ], + ); + } +} + +class _NoAccessMentionPageBlock extends StatelessWidget { + const _NoAccessMentionPageBlock({ + required this.textStyle, + }); + + final TextStyle? textStyle; + + @override + Widget build(BuildContext context) { + return FlowyHover( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: FlowyText( + LocaleKeys.document_mention_noAccess.tr(), + color: Theme.of(context).disabledColor, + decoration: TextDecoration.underline, + fontSize: textStyle?.fontSize, + fontWeight: textStyle?.fontWeight, + ), + ), + ); + } +} + +class _MobileMentionPageBlock extends StatelessWidget { + const _MobileMentionPageBlock({ + required this.view, + required this.textStyle, + required this.handleTap, + required this.handleDoubleTap, + }); + + final TextStyle? textStyle; + final ViewPB view; + final VoidCallback handleTap; + final VoidCallback handleDoubleTap; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: handleTap, + onDoubleTap: handleDoubleTap, + behavior: HitTestBehavior.opaque, + child: _MentionPageBlockContent( + view: view, + textStyle: textStyle, + ), + ); + } +} + +class _DesktopMentionPageBlock extends StatelessWidget { + const _DesktopMentionPageBlock({ + required this.view, + required this.textStyle, + required this.handleTap, + }); + + final TextStyle? textStyle; + final ViewPB view; + final VoidCallback handleTap; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: handleTap, + behavior: HitTestBehavior.opaque, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 2), + child: FlowyHover( + cursor: SystemMouseCursors.click, + child: _MentionPageBlockContent( + view: view, + textStyle: textStyle, + ), + ), + ), + ); + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart index e8c3554e73..28f82c07e6 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart @@ -137,9 +137,8 @@ class EditorStyleCustomizer { textStyle: baseTextStyle.copyWith( fontSize: fontSize, fontWeight: FontWeight.normal, - fontStyle: FontStyle.italic, color: Colors.red, - backgroundColor: Colors.grey.withOpacity(0.3), + backgroundColor: theme.colorScheme.inverseSurface.withOpacity(0.8), ), ), applyHeightToFirstAscent: true, From 9a295daf99a600d78b76bff4c8a156918b236063 Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Wed, 28 Aug 2024 16:15:40 +0800 Subject: [PATCH 4/5] chore: fix database filter (#6094) * chore: fix database filter * chore: fix test --- .../database/application/row/row_cache.dart | 16 ++- .../board/application/board_bloc.dart | 17 ++- .../board/presentation/board_page.dart | 2 - frontend/appflowy_tauri/src-tauri/Cargo.lock | 24 ++--- frontend/appflowy_tauri/src-tauri/Cargo.toml | 2 +- .../appflowy_web_app/src-tauri/Cargo.lock | 24 ++--- .../appflowy_web_app/src-tauri/Cargo.toml | 2 +- frontend/rust-lib/Cargo.lock | 24 ++--- frontend/rust-lib/Cargo.toml | 4 +- .../rust-lib/flowy-core/src/integrate/user.rs | 27 ++++- .../rust-lib/flowy-database2/src/manager.rs | 33 ++++-- .../src/services/database/database_editor.rs | 100 +++++++++--------- .../src/services/filter/controller.rs | 6 +- frontend/rust-lib/flowy-user/src/event_map.rs | 4 + .../flowy-user/src/user_manager/manager.rs | 3 + .../user_manager/manager_user_workspace.rs | 2 +- 16 files changed, 169 insertions(+), 121 deletions(-) diff --git a/frontend/appflowy_flutter/lib/plugins/database/application/row/row_cache.dart b/frontend/appflowy_flutter/lib/plugins/database/application/row/row_cache.dart index 1d11185a54..4d9f594471 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/application/row/row_cache.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/application/row/row_cache.dart @@ -55,6 +55,8 @@ class RowCache { final RowLifeCycle _rowLifeCycle; final RowFieldsDelegate _fieldDelegate; RowChangesetNotifier? _changedNotifier; + bool _isInitialRows = false; + final List _pendingVisibilityChanges = []; /// Returns a unmodifiable list of RowInfo UnmodifiableListView get rowInfos { @@ -80,7 +82,13 @@ class RowCache { final rowInfo = buildGridRow(row); _rowList.add(rowInfo); } + _isInitialRows = true; _changedNotifier?.receive(const ChangedReason.setInitialRows()); + + for (final changeset in _pendingVisibilityChanges) { + applyRowsVisibility(changeset); + } + _pendingVisibilityChanges.clear(); } void setRowMeta(RowMetaPB rowMeta) { @@ -103,8 +111,12 @@ class RowCache { } void applyRowsVisibility(RowsVisibilityChangePB changeset) { - _hideRows(changeset.invisibleRows); - _showRows(changeset.visibleRows); + if (_isInitialRows) { + _hideRows(changeset.invisibleRows); + _showRows(changeset.visibleRows); + } else { + _pendingVisibilityChanges.add(changeset); + } } void reorderAllRows(List rowIds) { diff --git a/frontend/appflowy_flutter/lib/plugins/database/board/application/board_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database/board/application/board_bloc.dart index 0fa07a69c8..24926531b7 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/board/application/board_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/board/application/board_bloc.dart @@ -202,11 +202,18 @@ class BoardBloc extends Bloc { ); }, endEditingHeader: (String groupId, String? groupName) async { - await groupBackendSvc.updateGroup( - fieldId: groupControllers.values.first.group.fieldId, - groupId: groupId, - name: groupName, - ); + final group = groupControllers[groupId]?.group; + if (group != null) { + if (generateGroupNameFromGroup(group) != groupName) { + await groupBackendSvc.updateGroup( + fieldId: groupControllers.values.first.group.fieldId, + groupId: groupId, + name: groupName, + ); + return; + } + } + state.maybeMap( ready: (state) => emit(state.copyWith(editingHeaderId: null)), orElse: () {}, diff --git a/frontend/appflowy_flutter/lib/plugins/database/board/presentation/board_page.dart b/frontend/appflowy_flutter/lib/plugins/database/board/presentation/board_page.dart index fc258acd11..49c7517372 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/board/presentation/board_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/board/presentation/board_page.dart @@ -556,10 +556,8 @@ class _BoardCardState extends State<_BoardCard> { @override Widget build(BuildContext context) { final boardBloc = context.read(); - final groupData = widget.afGroupData.customData as GroupData; final rowCache = boardBloc.rowCache; - final databaseController = boardBloc.databaseController; final rowMeta = rowCache.getRow(widget.groupItem.id)?.rowMeta ?? widget.groupItem.row; diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.lock b/frontend/appflowy_tauri/src-tauri/Cargo.lock index 4b8421d9c3..f3fdd16d6a 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.lock +++ b/frontend/appflowy_tauri/src-tauri/Cargo.lock @@ -172,7 +172,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "app-error" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "bincode", @@ -192,7 +192,7 @@ dependencies = [ [[package]] name = "appflowy-ai-client" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "bytes", @@ -837,7 +837,7 @@ dependencies = [ [[package]] name = "client-api" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "again", "anyhow", @@ -888,7 +888,7 @@ dependencies = [ [[package]] name = "client-api-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "collab-entity", "collab-rt-entity", @@ -901,7 +901,7 @@ dependencies = [ [[package]] name = "client-websocket" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "futures-channel", "futures-util", @@ -1149,7 +1149,7 @@ dependencies = [ [[package]] name = "collab-rt-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "bincode", @@ -1174,7 +1174,7 @@ dependencies = [ [[package]] name = "collab-rt-protocol" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "async-trait", @@ -1571,7 +1571,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "database-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "app-error", @@ -3117,7 +3117,7 @@ dependencies = [ [[package]] name = "gotrue" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "futures-util", @@ -3134,7 +3134,7 @@ dependencies = [ [[package]] name = "gotrue-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "app-error", @@ -3566,7 +3566,7 @@ dependencies = [ [[package]] name = "infra" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "bytes", @@ -6169,7 +6169,7 @@ dependencies = [ [[package]] name = "shared-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "app-error", diff --git a/frontend/appflowy_tauri/src-tauri/Cargo.toml b/frontend/appflowy_tauri/src-tauri/Cargo.toml index 32cf0aa188..f47bd7ae01 100644 --- a/frontend/appflowy_tauri/src-tauri/Cargo.toml +++ b/frontend/appflowy_tauri/src-tauri/Cargo.toml @@ -53,7 +53,7 @@ collab-user = { version = "0.2" } # Run the script: # scripts/tool/update_client_api_rev.sh new_rev_id # ⚠️⚠️⚠️️ -client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "5badffc97b17984d5f25a178c0c5a477338039c4" } +client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "cccdf76116f41631daa5533b56238aec323684de" } [dependencies] serde_json.workspace = true diff --git a/frontend/appflowy_web_app/src-tauri/Cargo.lock b/frontend/appflowy_web_app/src-tauri/Cargo.lock index f176748d1e..a3a4d98f60 100644 --- a/frontend/appflowy_web_app/src-tauri/Cargo.lock +++ b/frontend/appflowy_web_app/src-tauri/Cargo.lock @@ -163,7 +163,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "app-error" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "bincode", @@ -183,7 +183,7 @@ dependencies = [ [[package]] name = "appflowy-ai-client" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "bytes", @@ -811,7 +811,7 @@ dependencies = [ [[package]] name = "client-api" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "again", "anyhow", @@ -862,7 +862,7 @@ dependencies = [ [[package]] name = "client-api-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "collab-entity", "collab-rt-entity", @@ -875,7 +875,7 @@ dependencies = [ [[package]] name = "client-websocket" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "futures-channel", "futures-util", @@ -1132,7 +1132,7 @@ dependencies = [ [[package]] name = "collab-rt-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "bincode", @@ -1157,7 +1157,7 @@ dependencies = [ [[package]] name = "collab-rt-protocol" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "async-trait", @@ -1561,7 +1561,7 @@ checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "database-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "app-error", @@ -3184,7 +3184,7 @@ dependencies = [ [[package]] name = "gotrue" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "futures-util", @@ -3201,7 +3201,7 @@ dependencies = [ [[package]] name = "gotrue-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "app-error", @@ -3638,7 +3638,7 @@ dependencies = [ [[package]] name = "infra" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "bytes", @@ -6233,7 +6233,7 @@ dependencies = [ [[package]] name = "shared-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "app-error", diff --git a/frontend/appflowy_web_app/src-tauri/Cargo.toml b/frontend/appflowy_web_app/src-tauri/Cargo.toml index 588364dbfa..619395b2b2 100644 --- a/frontend/appflowy_web_app/src-tauri/Cargo.toml +++ b/frontend/appflowy_web_app/src-tauri/Cargo.toml @@ -52,7 +52,7 @@ collab-user = { version = "0.2" } # Run the script: # scripts/tool/update_client_api_rev.sh new_rev_id # ⚠️⚠️⚠️️ -client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "5badffc97b17984d5f25a178c0c5a477338039c4" } +client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "cccdf76116f41631daa5533b56238aec323684de" } [dependencies] serde_json.workspace = true diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index ea1cc43186..bbaa13656c 100644 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -163,7 +163,7 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "app-error" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "bincode", @@ -183,7 +183,7 @@ dependencies = [ [[package]] name = "appflowy-ai-client" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "bytes", @@ -729,7 +729,7 @@ dependencies = [ [[package]] name = "client-api" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "again", "anyhow", @@ -780,7 +780,7 @@ dependencies = [ [[package]] name = "client-api-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "collab-entity", "collab-rt-entity", @@ -793,7 +793,7 @@ dependencies = [ [[package]] name = "client-websocket" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "futures-channel", "futures-util", @@ -1010,7 +1010,7 @@ dependencies = [ [[package]] name = "collab-rt-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "bincode", @@ -1035,7 +1035,7 @@ dependencies = [ [[package]] name = "collab-rt-protocol" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "async-trait", @@ -1395,7 +1395,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "database-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "app-error", @@ -2795,7 +2795,7 @@ dependencies = [ [[package]] name = "gotrue" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "futures-util", @@ -2812,7 +2812,7 @@ dependencies = [ [[package]] name = "gotrue-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "app-error", @@ -3177,7 +3177,7 @@ dependencies = [ [[package]] name = "infra" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "bytes", @@ -5377,7 +5377,7 @@ dependencies = [ [[package]] name = "shared-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=5badffc97b17984d5f25a178c0c5a477338039c4#5badffc97b17984d5f25a178c0c5a477338039c4" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=cccdf76116f41631daa5533b56238aec323684de#cccdf76116f41631daa5533b56238aec323684de" dependencies = [ "anyhow", "app-error", diff --git a/frontend/rust-lib/Cargo.toml b/frontend/rust-lib/Cargo.toml index 22e4d9cc70..e91bf6a66d 100644 --- a/frontend/rust-lib/Cargo.toml +++ b/frontend/rust-lib/Cargo.toml @@ -100,8 +100,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 = "5badffc97b17984d5f25a178c0c5a477338039c4" } -client-api-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "5badffc97b17984d5f25a178c0c5a477338039c4" } +client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "cccdf76116f41631daa5533b56238aec323684de" } +client-api-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "cccdf76116f41631daa5533b56238aec323684de" } [profile.dev] opt-level = 0 diff --git a/frontend/rust-lib/flowy-core/src/integrate/user.rs b/frontend/rust-lib/flowy-core/src/integrate/user.rs index f382398e8f..a39ddb3945 100644 --- a/frontend/rust-lib/flowy-core/src/integrate/user.rs +++ b/frontend/rust-lib/flowy-core/src/integrate/user.rs @@ -38,6 +38,7 @@ impl UserStatusCallback for UserStatusCallbackImpl { cloud_config: &Option, user_workspace: &UserWorkspace, _device_id: &str, + authenticator: &Authenticator, ) -> FlowyResult<()> { self .server_provider @@ -64,7 +65,10 @@ impl UserStatusCallback for UserStatusCallbackImpl { }, ) .await?; - self.database_manager.initialize(user_id).await?; + self + .database_manager + .initialize(user_id, authenticator == &Authenticator::Local) + .await?; self.document_manager.initialize(user_id).await?; Ok(()) } @@ -74,6 +78,7 @@ impl UserStatusCallback for UserStatusCallbackImpl { user_id: i64, user_workspace: &UserWorkspace, device_id: &str, + authenticator: &Authenticator, ) -> FlowyResult<()> { event!( tracing::Level::TRACE, @@ -86,7 +91,10 @@ impl UserStatusCallback for UserStatusCallbackImpl { .folder_manager .initialize_with_workspace_id(user_id) .await?; - self.database_manager.initialize(user_id).await?; + self + .database_manager + .initialize(user_id, authenticator.is_local()) + .await?; self.document_manager.initialize(user_id).await?; Ok(()) } @@ -97,6 +105,7 @@ impl UserStatusCallback for UserStatusCallbackImpl { user_profile: &UserProfile, user_workspace: &UserWorkspace, device_id: &str, + authenticator: &Authenticator, ) -> FlowyResult<()> { self .server_provider @@ -156,7 +165,7 @@ impl UserStatusCallback for UserStatusCallbackImpl { self .database_manager - .initialize_with_new_user(user_profile.uid) + .initialize_with_new_user(user_profile.uid, authenticator.is_local()) .await .context("DatabaseManager error")?; @@ -173,12 +182,20 @@ impl UserStatusCallback for UserStatusCallbackImpl { Ok(()) } - async fn open_workspace(&self, user_id: i64, user_workspace: &UserWorkspace) -> FlowyResult<()> { + async fn open_workspace( + &self, + user_id: i64, + user_workspace: &UserWorkspace, + authenticator: &Authenticator, + ) -> FlowyResult<()> { self .folder_manager .initialize_with_workspace_id(user_id) .await?; - self.database_manager.initialize(user_id).await?; + self + .database_manager + .initialize(user_id, authenticator.is_local()) + .await?; self.document_manager.initialize(user_id).await?; self.ai_manager.initialize(&user_workspace.id).await?; self.storage_manager.initialize(&user_workspace.id).await; diff --git a/frontend/rust-lib/flowy-database2/src/manager.rs b/frontend/rust-lib/flowy-database2/src/manager.rs index db37387451..0c4b13cab8 100644 --- a/frontend/rust-lib/flowy-database2/src/manager.rs +++ b/frontend/rust-lib/flowy-database2/src/manager.rs @@ -82,7 +82,7 @@ impl DatabaseManager { } /// When initialize with new workspace, all the resources will be cleared. - pub async fn initialize(&self, uid: i64) -> FlowyResult<()> { + pub async fn initialize(&self, uid: i64, is_local_user: bool) -> FlowyResult<()> { // 1. Clear all existing tasks self.task_scheduler.write().await.clear_task(); // 2. Release all existing editors @@ -99,6 +99,7 @@ impl DatabaseManager { let collab_db = self.user.collab_db(uid)?; let collab_service = WorkspaceDatabaseCollabServiceImpl::new( + is_local_user, self.user.clone(), self.collab_builder.clone(), self.cloud_service.clone(), @@ -168,8 +169,12 @@ impl DatabaseManager { skip_all, err )] - pub async fn initialize_with_new_user(&self, user_id: i64) -> FlowyResult<()> { - self.initialize(user_id).await?; + pub async fn initialize_with_new_user( + &self, + user_id: i64, + is_local_user: bool, + ) -> FlowyResult<()> { + self.initialize(user_id, is_local_user).await?; Ok(()) } @@ -630,6 +635,7 @@ impl DatabaseManager { } struct WorkspaceDatabaseCollabServiceImpl { + is_local_user: bool, user: Arc, collab_builder: Arc, persistence: Arc, @@ -639,12 +645,14 @@ struct WorkspaceDatabaseCollabServiceImpl { impl WorkspaceDatabaseCollabServiceImpl { fn new( + is_local_user: bool, user: Arc, collab_builder: Arc, cloud_service: Arc, ) -> Self { let persistence = DatabasePersistenceImpl { user: user.clone() }; Self { + is_local_user, user, collab_builder, persistence: Arc::new(persistence), @@ -783,15 +791,18 @@ impl DatabaseCollabService for WorkspaceDatabaseCollabServiceImpl { DataSource::from(encode_collab) }, Ok(None) => { - info!( - "build collab: {}:{} with empty encode collab", - collab_type, object_id - ); - // when collab not exist, create a default collab - CollabPersistenceImpl { - persistence: Some(self.persistence.clone()), + if self.is_local_user { + CollabPersistenceImpl { + persistence: Some(self.persistence.clone()), + } + .into() + } else { + error!( + "build collab: {}:{} with empty encode collab", + collab_type, object_id + ); + return Err(DatabaseError::RecordNotFound); } - .into() }, Err(err) => { error!("build collab: failed to get encode collab: {}", err); diff --git a/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs b/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs index 0b8ae12030..043e2ceb24 100644 --- a/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs +++ b/frontend/rust-lib/flowy-database2/src/services/database/database_editor.rs @@ -5,7 +5,7 @@ use crate::services::cell::{apply_cell_changeset, get_cell_protobuf, CellCache}; use crate::services::database::database_observe::*; use crate::services::database::util::database_view_setting_pb_from_view; use crate::services::database_view::{ - DatabaseViewChanged, DatabaseViewOperation, DatabaseViews, EditorByViewId, + DatabaseViewChanged, DatabaseViewEditor, DatabaseViewOperation, DatabaseViews, EditorByViewId, }; use crate::services::field::type_option_transform::transform_type_option; use crate::services::field::{ @@ -1355,57 +1355,7 @@ impl DatabaseEditor { } let row_orders = self.database.read().await.get_row_orders_for_view(view_id); - let cloned_database = Arc::downgrade(&self.database); let cloned_row_orders = row_orders.clone(); - let opening_database_views = self.database_views.clone(); - tokio::spawn(async move { - const CHUNK_SIZE: usize = 10; - let apply_filter_and_sort = - |mut loaded_rows, opening_database_views: Arc| async move { - for database_view in opening_database_views.editors().await { - if database_view.has_filters().await { - database_view - .v_filter_rows_and_notify(&mut loaded_rows) - .await; - } - - if database_view.has_sorts().await { - database_view.v_sort_rows_and_notify(&mut loaded_rows).await; - } - } - }; - - let mut loaded_rows = vec![]; - for chunk_row_orders in cloned_row_orders.chunks(CHUNK_SIZE) { - match cloned_database.upgrade() { - None => break, - Some(database) => { - for row_order in chunk_row_orders { - if let Some(database_row) = - database.read().await.init_database_row(&row_order.id).await - { - if let Some(row) = database_row.read().await.get_row() { - loaded_rows.push(Arc::new(row)); - } - } - } - - // stop init database rows - if new_token.is_cancelled() { - return; - } - - if loaded_rows.len() % 1000 == 0 { - apply_filter_and_sort(loaded_rows.clone(), opening_database_views.clone()).await; - } - }, - } - tokio::task::yield_now().await; - } - - apply_filter_and_sort(loaded_rows.clone(), opening_database_views).await; - }); - // Collect database details in a single block holding the `read` lock let (database_id, fields, is_linked) = { let database = self.database.read().await; @@ -1431,6 +1381,54 @@ impl DatabaseEditor { fields.len(), rows.len() ); + + trace!("[Database]: start loading rows"); + let cloned_database = Arc::downgrade(&self.database); + let view_editor = self.database_views.get_view_editor(view_id).await?; + tokio::spawn(async move { + const CHUNK_SIZE: usize = 10; + let apply_filter_and_sort = + |mut loaded_rows: Vec>, view_editor: Arc| async move { + if view_editor.has_filters().await { + trace!("[Database]: filtering rows:{}", loaded_rows.len()); + view_editor.v_filter_rows_and_notify(&mut loaded_rows).await; + } + + if view_editor.has_sorts().await { + trace!("[Database]: sorting rows:{}", loaded_rows.len()); + view_editor.v_sort_rows_and_notify(&mut loaded_rows).await; + } + }; + + let mut loaded_rows = vec![]; + for chunk_row_orders in cloned_row_orders.chunks(CHUNK_SIZE) { + match cloned_database.upgrade() { + None => break, + Some(database) => { + for row_order in chunk_row_orders { + if let Some(database_row) = + database.read().await.init_database_row(&row_order.id).await + { + if let Some(row) = database_row.read().await.get_row() { + loaded_rows.push(Arc::new(row)); + } + } + } + + // stop init database rows + if new_token.is_cancelled() { + info!("[Database]: stop loading database rows"); + return; + } + }, + } + tokio::task::yield_now().await; + } + + info!("[Database]: Finish loading rows: {}", loaded_rows.len()); + apply_filter_and_sort(loaded_rows.clone(), view_editor).await; + }); + Ok::<_, FlowyError>(DatabasePB { id: database_id, fields, diff --git a/frontend/rust-lib/flowy-database2/src/services/filter/controller.rs b/frontend/rust-lib/flowy-database2/src/services/filter/controller.rs index b9c61ca1fa..cb3231f948 100644 --- a/frontend/rust-lib/flowy-database2/src/services/filter/controller.rs +++ b/frontend/rust-lib/flowy-database2/src/services/filter/controller.rs @@ -11,7 +11,7 @@ use flowy_error::FlowyResult; use lib_infra::priority_task::{QualityOfService, Task, TaskContent, TaskDispatcher}; use serde::{Deserialize, Serialize}; use tokio::sync::RwLock; -use tracing::error; +use tracing::{error, trace}; use crate::entities::filter_entities::*; use crate::entities::{FieldType, InsertedRowPB, RowMetaPB}; @@ -74,14 +74,13 @@ impl FilterController { let mut need_save = false; let mut filters = delegate.get_all_filters(view_id).await; + trace!("[Database]: filters: {:?}", filters); let mut filtering_field_ids: HashMap> = HashMap::new(); - for filter in filters.iter() { filter.get_all_filtering_field_ids(&mut filtering_field_ids); } let mut delete_filter_ids = vec![]; - for (field_id, filter_ids) in &filtering_field_ids { if !field_ids.contains(field_id) { need_save = true; @@ -385,7 +384,6 @@ impl FilterController { invisible_rows, visible_rows, }; - tracing::trace!("filter result {:?}", notification); let _ = self .notifier .send(DatabaseViewChanged::FilterNotification(notification)); diff --git a/frontend/rust-lib/flowy-user/src/event_map.rs b/frontend/rust-lib/flowy-user/src/event_map.rs index 6e03928c6e..844d640415 100644 --- a/frontend/rust-lib/flowy-user/src/event_map.rs +++ b/frontend/rust-lib/flowy-user/src/event_map.rs @@ -290,6 +290,7 @@ pub trait UserStatusCallback: Send + Sync + 'static { _cloud_config: &Option, _user_workspace: &UserWorkspace, _device_id: &str, + _authenticator: &Authenticator, ) -> FlowyResult<()> { Ok(()) } @@ -299,6 +300,7 @@ pub trait UserStatusCallback: Send + Sync + 'static { _user_id: i64, _user_workspace: &UserWorkspace, _device_id: &str, + _authenticator: &Authenticator, ) -> FlowyResult<()> { Ok(()) } @@ -309,6 +311,7 @@ pub trait UserStatusCallback: Send + Sync + 'static { _user_profile: &UserProfile, _user_workspace: &UserWorkspace, _device_id: &str, + _authenticator: &Authenticator, ) -> FlowyResult<()> { Ok(()) } @@ -320,6 +323,7 @@ pub trait UserStatusCallback: Send + Sync + 'static { &self, _user_id: i64, _user_workspace: &UserWorkspace, + _authenticator: &Authenticator, ) -> FlowyResult<()> { Ok(()) } diff --git a/frontend/rust-lib/flowy-user/src/user_manager/manager.rs b/frontend/rust-lib/flowy-user/src/user_manager/manager.rs index 78443360aa..1b38599814 100644 --- a/frontend/rust-lib/flowy-user/src/user_manager/manager.rs +++ b/frontend/rust-lib/flowy-user/src/user_manager/manager.rs @@ -280,6 +280,7 @@ impl UserManager { &cloud_config, &session.user_workspace, &self.authenticate_user.user_config.device_id, + &user.authenticator, ) .await?; } @@ -352,6 +353,7 @@ impl UserManager { user_profile.uid, &latest_workspace, &self.authenticate_user.user_config.device_id, + &authenticator, ) .await?; send_auth_state_notification(AuthStateChangedPB { @@ -443,6 +445,7 @@ impl UserManager { new_user_profile, &new_session.user_workspace, &self.authenticate_user.user_config.device_id, + authenticator, ) .await?; diff --git a/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs b/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs index 4b56b51df0..b674019265 100644 --- a/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs +++ b/frontend/rust-lib/flowy-user/src/user_manager/manager_user_workspace.rs @@ -196,7 +196,7 @@ impl UserManager { .user_status_callback .read() .await - .open_workspace(uid, &user_workspace) + .open_workspace(uid, &user_workspace, &user_profile.authenticator) .await { error!("Open workspace failed: {:?}", err); From 9ee8cc6a7b9813c0c31353ede2d7d00cd1116854 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Wed, 28 Aug 2024 18:53:16 +0800 Subject: [PATCH 5/5] feat: optimize sync error page (#6082) --- .../document_with_inline_page_test.dart | 4 +- .../{gesture.dart => animated_gesture.dart} | 0 .../home/mobile_home_page_header.dart | 3 +- .../home/shared/mobile_page_card.dart | 2 +- .../home/tab/ai_bubble_button.dart | 2 +- .../multi_select_notification_item.dart | 2 +- .../widgets/notification_item.dart | 2 +- .../lib/plugins/document/document_page.dart | 11 +- .../document/presentation/editor_page.dart | 2 +- .../appflowy_mobile_toolbar_item.dart | 7 +- .../document/presentation/editor_style.dart | 2 +- .../presentation/sync_error_page.dart | 168 ++++++++++++++++++ .../lib/plugins/shared/share/export_tab.dart | 49 +++++ .../lib/plugins/shared/share/share_bloc.dart | 20 ++- .../popup_menu/appflowy_popup_menu.dart | 8 +- .../appflowy_flutter/lib/startup/startup.dart | 3 +- .../startup/tasks/platform_error_catcher.dart | 19 ++ .../sign_in_screen/mobile_sign_in_screen.dart | 1 + .../widgets/third_party_sign_in_button.dart | 2 +- .../settings/share/export_service.dart | 8 + .../application/sidebar/space/space_bloc.dart | 3 + .../menu/sidebar/footer/sidebar_footer.dart | 90 +++++----- .../sidebar/footer/sidebar_footer_button.dart | 39 ++++ .../presentation/widgets/dialogs.dart | 7 + .../flowy_icons/16x/icon_template.svg | 5 + .../flowy_icons/40x/icon_warning.svg | 5 + frontend/resources/translations/en.json | 13 +- .../src/deps_resolve/folder_deps.rs | 2 +- .../src/entities/share_entities.rs | 3 + .../flowy-database2/src/event_handler.rs | 14 ++ .../rust-lib/flowy-database2/src/event_map.rs | 4 + .../rust-lib/flowy-database2/src/manager.rs | 10 +- 32 files changed, 438 insertions(+), 72 deletions(-) rename frontend/appflowy_flutter/lib/mobile/presentation/base/{gesture.dart => animated_gesture.dart} (100%) create mode 100644 frontend/appflowy_flutter/lib/plugins/document/presentation/sync_error_page.dart create mode 100644 frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/footer/sidebar_footer_button.dart create mode 100644 frontend/resources/flowy_icons/16x/icon_template.svg create mode 100644 frontend/resources/flowy_icons/40x/icon_warning.svg diff --git a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_inline_page_test.dart b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_inline_page_test.dart index 335f9a377f..ed30529149 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_inline_page_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_inline_page_test.dart @@ -1,7 +1,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_page_block.dart'; +import 'package:appflowy/plugins/document/presentation/sync_error_page.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart'; import 'package:flowy_infra/uuid.dart'; -import 'package:flowy_infra_ui/widget/error_page.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; @@ -92,7 +92,7 @@ void main() { ); expect(finder, findsOneWidget); await tester.tapButton(finder); - expect(find.byType(FlowyErrorPage), findsOneWidget); + expect(find.byType(SyncErrorPage), findsOneWidget); }); }); } diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/base/gesture.dart b/frontend/appflowy_flutter/lib/mobile/presentation/base/animated_gesture.dart similarity index 100% rename from frontend/appflowy_flutter/lib/mobile/presentation/base/gesture.dart rename to frontend/appflowy_flutter/lib/mobile/presentation/base/animated_gesture.dart diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/home/mobile_home_page_header.dart b/frontend/appflowy_flutter/lib/mobile/presentation/home/mobile_home_page_header.dart index 28e3812b93..8742dce817 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/home/mobile_home_page_header.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/home/mobile_home_page_header.dart @@ -1,6 +1,6 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; -import 'package:appflowy/mobile/presentation/base/gesture.dart'; +import 'package:appflowy/mobile/presentation/base/animated_gesture.dart'; import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart'; import 'package:appflowy/mobile/presentation/home/workspaces/workspace_menu_bottom_sheet.dart'; import 'package:appflowy/plugins/base/emoji/emoji_picker_screen.dart'; @@ -109,6 +109,7 @@ class _MobileWorkspace extends StatelessWidget { return const SizedBox.shrink(); } return AnimatedGestureDetector( + scaleFactor: 0.99, alignment: Alignment.centerLeft, onTapUp: () { context.read().add( diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/home/shared/mobile_page_card.dart b/frontend/appflowy_flutter/lib/mobile/presentation/home/shared/mobile_page_card.dart index 85cdc98c4a..f5e031666a 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/home/shared/mobile_page_card.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/home/shared/mobile_page_card.dart @@ -5,7 +5,7 @@ import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/mobile/application/mobile_router.dart'; import 'package:appflowy/mobile/application/page_style/document_page_style_bloc.dart'; import 'package:appflowy/mobile/application/recent/recent_view_bloc.dart'; -import 'package:appflowy/mobile/presentation/base/gesture.dart'; +import 'package:appflowy/mobile/presentation/base/animated_gesture.dart'; import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; import 'package:appflowy/shared/appflowy_network_image.dart'; diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/home/tab/ai_bubble_button.dart b/frontend/appflowy_flutter/lib/mobile/presentation/home/tab/ai_bubble_button.dart index 8ecd70f7e5..a5cb39ffaa 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/home/tab/ai_bubble_button.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/home/tab/ai_bubble_button.dart @@ -1,6 +1,6 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; -import 'package:appflowy/mobile/presentation/base/gesture.dart'; +import 'package:appflowy/mobile/presentation/base/animated_gesture.dart'; import 'package:appflowy/mobile/presentation/home/tab/mobile_space_tab.dart'; import 'package:appflowy/util/theme_extension.dart'; import 'package:easy_localization/easy_localization.dart'; diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/notifications/widgets/multi_select_notification_item.dart b/frontend/appflowy_flutter/lib/mobile/presentation/notifications/widgets/multi_select_notification_item.dart index 448f2033f4..ea57d5d391 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/notifications/widgets/multi_select_notification_item.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/notifications/widgets/multi_select_notification_item.dart @@ -1,5 +1,5 @@ import 'package:appflowy/mobile/application/notification/notification_reminder_bloc.dart'; -import 'package:appflowy/mobile/presentation/base/gesture.dart'; +import 'package:appflowy/mobile/presentation/base/animated_gesture.dart'; import 'package:appflowy/mobile/presentation/notifications/mobile_notifications_screen.dart'; import 'package:appflowy/mobile/presentation/notifications/widgets/widgets.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart'; diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/notifications/widgets/notification_item.dart b/frontend/appflowy_flutter/lib/mobile/presentation/notifications/widgets/notification_item.dart index 0f17bba68c..8f6db18265 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/notifications/widgets/notification_item.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/notifications/widgets/notification_item.dart @@ -1,6 +1,6 @@ import 'package:appflowy/mobile/application/mobile_router.dart'; import 'package:appflowy/mobile/application/notification/notification_reminder_bloc.dart'; -import 'package:appflowy/mobile/presentation/base/gesture.dart'; +import 'package:appflowy/mobile/presentation/base/animated_gesture.dart'; import 'package:appflowy/mobile/presentation/notifications/widgets/widgets.dart'; import 'package:appflowy/user/application/reminder/reminder_bloc.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/document/document_page.dart b/frontend/appflowy_flutter/lib/plugins/document/document_page.dart index d835a7c00b..ed4ed73dff 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/document_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/document_page.dart @@ -1,4 +1,3 @@ -import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/mobile/application/page_style/document_page_style_bloc.dart'; import 'package:appflowy/plugins/document/application/document_bloc.dart'; import 'package:appflowy/plugins/document/presentation/banner.dart'; @@ -12,6 +11,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/image/cust import 'package:appflowy/plugins/document/presentation/editor_plugins/image/multi_image_block_component/multi_image_block_component.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; import 'package:appflowy/plugins/document/presentation/editor_style.dart'; +import 'package:appflowy/plugins/document/presentation/sync_error_page.dart'; import 'package:appflowy/shared/patterns/common_patterns.dart'; import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/workspace/application/action_navigation/action_navigation_bloc.dart'; @@ -22,8 +22,6 @@ import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_editor/appflowy_editor.dart' hide Log; import 'package:cross_file/cross_file.dart'; import 'package:desktop_drop/desktop_drop.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:flowy_infra_ui/widget/error_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:provider/provider.dart'; @@ -115,9 +113,10 @@ class _DocumentPageState extends State final error = state.error; if (error != null || editorState == null) { Log.error(error); - return FlowyErrorPage.message( - error.toString(), - howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(), + return Center( + child: SyncErrorPage( + error: error, + ), ); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart index 53dbf57c6d..f5be9246b3 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart @@ -500,7 +500,7 @@ class _AppFlowyEditorPageState extends State { void _customizeBlockComponentBackgroundColorDecorator() { blockComponentBackgroundColorDecorator = (Node node, String colorString) { - if (context.mounted) { + if (mounted && context.mounted) { return buildEditorCustomizedColor(context, node, colorString); } return null; diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/appflowy_mobile_toolbar_item.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/appflowy_mobile_toolbar_item.dart index d138e644cd..014889261f 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/appflowy_mobile_toolbar_item.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/appflowy_mobile_toolbar_item.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:appflowy/generated/flowy_svgs.g.dart'; +import 'package:appflowy/mobile/presentation/base/animated_gesture.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/aa_menu/_toolbar_theme.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/appflowy_mobile_toolbar.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; @@ -106,9 +107,9 @@ class _AppFlowyMobileToolbarIconItemState final enable = widget.enable?.call() ?? true; return Padding( padding: const EdgeInsets.symmetric(vertical: 5), - child: GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { + child: AnimatedGestureDetector( + scaleFactor: 0.95, + onTapUp: () { widget.onTap(); _rebuild(); }, diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart index 28f82c07e6..19e0dcaa00 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart @@ -138,7 +138,7 @@ class EditorStyleCustomizer { fontSize: fontSize, fontWeight: FontWeight.normal, color: Colors.red, - backgroundColor: theme.colorScheme.inverseSurface.withOpacity(0.8), + backgroundColor: Colors.grey.withOpacity(0.3), ), ), applyHeightToFirstAscent: true, diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/sync_error_page.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/sync_error_page.dart new file mode 100644 index 0000000000..6318254284 --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/sync_error_page.dart @@ -0,0 +1,168 @@ +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/mobile/presentation/base/animated_gesture.dart'; +import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart'; +import 'package:appflowy/startup/startup.dart'; +import 'package:appflowy/workspace/presentation/widgets/dialogs.dart'; +import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; +import 'package:appflowy_editor/appflowy_editor.dart' show PlatformExtension; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flowy_infra_ui/style_widget/text.dart'; +import 'package:flowy_infra_ui/widget/spacing.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; + +class SyncErrorPage extends StatelessWidget { + const SyncErrorPage({ + super.key, + this.error, + }); + + final FlowyError? error; + + @override + Widget build(BuildContext context) { + if (PlatformExtension.isMobile) { + return _MobileSyncErrorPage(error: error); + } else { + return _DesktopSyncErrorPage(error: error); + } + } +} + +class _MobileSyncErrorPage extends StatelessWidget { + const _MobileSyncErrorPage({ + this.error, + }); + + final FlowyError? error; + + @override + Widget build(BuildContext context) { + return AnimatedGestureDetector( + scaleFactor: 0.99, + onTapUp: () { + getIt().setPlainText(error.toString()); + showToastNotification( + context, + message: LocaleKeys.message_copy_success.tr(), + bottomPadding: 0, + ); + }, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const FlowySvg( + FlowySvgs.icon_warning_xl, + blendMode: null, + ), + const VSpace(16.0), + FlowyText.medium( + LocaleKeys.error_syncError.tr(), + fontSize: 15, + ), + const VSpace(8.0), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24.0), + child: FlowyText.regular( + LocaleKeys.error_syncErrorHint.tr(), + fontSize: 13, + color: Theme.of(context).hintColor, + textAlign: TextAlign.center, + maxLines: 10, + ), + ), + const VSpace(2.0), + FlowyText.regular( + '(${LocaleKeys.error_clickToCopy.tr()})', + fontSize: 13, + color: Theme.of(context).hintColor, + textAlign: TextAlign.center, + ), + ], + ), + ); + } +} + +class _DesktopSyncErrorPage extends StatelessWidget { + const _DesktopSyncErrorPage({ + this.error, + }); + + final FlowyError? error; + + @override + Widget build(BuildContext context) { + return AnimatedGestureDetector( + scaleFactor: 0.995, + onTapUp: () { + getIt().setPlainText(error.toString()); + showToastNotification( + context, + message: LocaleKeys.message_copy_success.tr(), + bottomPadding: 0, + ); + }, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const FlowySvg( + FlowySvgs.icon_warning_xl, + blendMode: null, + ), + const VSpace(16.0), + FlowyText.medium( + error?.code.toString() ?? '', + fontSize: 16, + ), + const VSpace(8.0), + RichText( + text: TextSpan( + children: [ + TextSpan( + text: LocaleKeys.errorDialog_howToFixFallbackHint1.tr(), + style: TextStyle( + fontSize: 14, + color: Theme.of(context).hintColor, + ), + ), + TextSpan( + text: 'Github', + style: TextStyle( + fontSize: 14, + color: Theme.of(context).colorScheme.primary, + decoration: TextDecoration.underline, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + afLaunchUrlString( + 'https://github.com/AppFlowy-IO/AppFlowy/issues/new?template=bug_report.yaml', + ); + }, + ), + TextSpan( + text: LocaleKeys.errorDialog_howToFixFallbackHint2.tr(), + style: TextStyle( + fontSize: 14, + color: Theme.of(context).hintColor, + ), + ), + ], + ), + ), + const VSpace(8.0), + FlowyText.regular( + '(${LocaleKeys.error_clickToCopy.tr()})', + fontSize: 14, + color: Theme.of(context).hintColor, + textAlign: TextAlign.center, + ), + ], + ), + ); + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/shared/share/export_tab.dart b/frontend/appflowy_flutter/lib/plugins/shared/share/export_tab.dart index 8f05e44185..ff95fe6acc 100644 --- a/frontend/appflowy_flutter/lib/plugins/shared/share/export_tab.dart +++ b/frontend/appflowy_flutter/lib/plugins/shared/share/export_tab.dart @@ -11,6 +11,7 @@ import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/file_picker/file_picker_service.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -51,6 +52,14 @@ class ExportTab extends StatelessWidget { svg: FlowySvgs.duplicate_s, onTap: () => _exportToClipboard(context), ), + if (kDebugMode) ...[ + const VSpace(10), + _ExportButton( + title: 'JSON (Debug Mode)', + svg: FlowySvgs.duplicate_s, + onTap: () => _exportJSON(context), + ), + ], ], ); } @@ -64,6 +73,14 @@ class ExportTab extends StatelessWidget { svg: FlowySvgs.database_layout_m, onTap: () => _exportCSV(context), ), + if (kDebugMode) ...[ + const VSpace(10), + _ExportButton( + title: 'Raw Database Data (Debug Mode)', + svg: FlowySvgs.duplicate_s, + onTap: () => _exportRawDatabaseData(context), + ), + ], ], ); } @@ -100,6 +117,22 @@ class ExportTab extends StatelessWidget { } } + Future _exportJSON(BuildContext context) async { + final viewName = context.read().state.viewName; + final exportPath = await getIt().saveFile( + dialogTitle: '', + fileName: '${viewName.toFileName()}.json', + ); + if (context.mounted && exportPath != null) { + context.read().add( + ShareEvent.share( + ShareType.json, + exportPath, + ), + ); + } + } + Future _exportCSV(BuildContext context) async { final viewName = context.read().state.viewName; final exportPath = await getIt().saveFile( @@ -116,6 +149,22 @@ class ExportTab extends StatelessWidget { } } + Future _exportRawDatabaseData(BuildContext context) async { + final viewName = context.read().state.viewName; + final exportPath = await getIt().saveFile( + dialogTitle: '', + fileName: '${viewName.toFileName()}.json', + ); + if (context.mounted && exportPath != null) { + context.read().add( + ShareEvent.share( + ShareType.rawDatabaseData, + exportPath, + ), + ); + } + } + Future _exportToClipboard(BuildContext context) async { final documentExporter = DocumentExporter(context.read().view); final result = await documentExporter.export(DocumentExportType.markdown); diff --git a/frontend/appflowy_flutter/lib/plugins/shared/share/share_bloc.dart b/frontend/appflowy_flutter/lib/plugins/shared/share/share_bloc.dart index 16d50d2212..af6adc4f68 100644 --- a/frontend/appflowy_flutter/lib/plugins/shared/share/share_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/shared/share/share_bloc.dart @@ -179,6 +179,14 @@ class ShareBloc extends Bloc { (s) => FlowyResult.success(s.data), (f) => FlowyResult.failure(f), ); + } else if (type == ShareType.rawDatabaseData) { + final exportResult = await BackendExportService.exportDatabaseAsRawData( + view.id, + ); + result = exportResult.fold( + (s) => FlowyResult.success(s.data), + (f) => FlowyResult.failure(f), + ); } else { result = await documentExporter.export(type.documentExportType); } @@ -189,6 +197,8 @@ class ShareBloc extends Bloc { case ShareType.markdown: case ShareType.html: case ShareType.csv: + case ShareType.json: + case ShareType.rawDatabaseData: File(path).writeAsStringSync(s); return FlowyResult.success(type); default: @@ -208,9 +218,11 @@ enum ShareType { html, text, link, + json, // only available in database - csv; + csv, + rawDatabaseData; static List get unimplemented => [link]; @@ -222,10 +234,16 @@ enum ShareType { return DocumentExportType.html; case ShareType.text: return DocumentExportType.text; + case ShareType.json: + return DocumentExportType.json; case ShareType.csv: throw UnsupportedError('DocumentShareType.csv is not supported'); case ShareType.link: throw UnsupportedError('DocumentShareType.link is not supported'); + case ShareType.rawDatabaseData: + throw UnsupportedError( + 'DocumentShareType.rawDatabaseData is not supported', + ); } } } diff --git a/frontend/appflowy_flutter/lib/shared/popup_menu/appflowy_popup_menu.dart b/frontend/appflowy_flutter/lib/shared/popup_menu/appflowy_popup_menu.dart index 550c8609e5..e6ace027fa 100644 --- a/frontend/appflowy_flutter/lib/shared/popup_menu/appflowy_popup_menu.dart +++ b/frontend/appflowy_flutter/lib/shared/popup_menu/appflowy_popup_menu.dart @@ -10,6 +10,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:appflowy/mobile/presentation/base/animated_gesture.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; @@ -1524,9 +1525,10 @@ class PopupMenuButtonState extends State> { assert(debugCheckHasMaterialLocalizations(context)); if (widget.child != null) { - return GestureDetector( - onTap: widget.enabled ? showButtonMenu : null, - child: widget.child, + return AnimatedGestureDetector( + scaleFactor: 0.99, + onTapUp: widget.enabled ? showButtonMenu : null, + child: widget.child!, ); } diff --git a/frontend/appflowy_flutter/lib/startup/startup.dart b/frontend/appflowy_flutter/lib/startup/startup.dart index 85be02f6a6..60b18fd8d6 100644 --- a/frontend/appflowy_flutter/lib/startup/startup.dart +++ b/frontend/appflowy_flutter/lib/startup/startup.dart @@ -109,7 +109,8 @@ class FlowyRunner { [ // this task should be first task, for handling platform errors. // don't catch errors in test mode - if (!mode.isUnitTest) const PlatformErrorCatcherTask(), + if (!mode.isUnitTest && !mode.isIntegrationTest) + const PlatformErrorCatcherTask(), if (!mode.isUnitTest) const InitSentryTask(), // this task should be second task, for handling memory leak. // there's a flag named _enable in memory_leak_detector.dart. If it's false, the task will be ignored. diff --git a/frontend/appflowy_flutter/lib/startup/tasks/platform_error_catcher.dart b/frontend/appflowy_flutter/lib/startup/tasks/platform_error_catcher.dart index 9d088bb5d4..c2c64536b2 100644 --- a/frontend/appflowy_flutter/lib/startup/tasks/platform_error_catcher.dart +++ b/frontend/appflowy_flutter/lib/startup/tasks/platform_error_catcher.dart @@ -1,5 +1,7 @@ import 'package:appflowy_backend/log.dart'; +import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import '../startup.dart'; @@ -17,6 +19,23 @@ class PlatformErrorCatcherTask extends LaunchTask { return true; }; } + + ErrorWidget.builder = (details) { + if (kDebugMode) { + return Container( + width: double.infinity, + height: 30, + color: Colors.red, + child: FlowyText( + 'ERROR: ${details.exceptionAsString()}', + color: Colors.white, + ), + ); + } + + // hide the error widget in release mode + return const SizedBox.shrink(); + }; } @override diff --git a/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/mobile_sign_in_screen.dart b/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/mobile_sign_in_screen.dart index 5526cb6c70..727062a108 100644 --- a/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/mobile_sign_in_screen.dart +++ b/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/mobile_sign_in_screen.dart @@ -44,6 +44,7 @@ class MobileSignInScreen extends StatelessWidget { const Spacer(flex: 2), const Spacer(), Expanded(child: _buildSettingsButton(context)), + if (Platform.isAndroid) const Spacer(), ], ), ), diff --git a/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/third_party_sign_in_button.dart b/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/third_party_sign_in_button.dart index 47c6ea515f..c51634bcf5 100644 --- a/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/third_party_sign_in_button.dart +++ b/frontend/appflowy_flutter/lib/user/presentation/screens/sign_in_screen/widgets/third_party_sign_in_button.dart @@ -1,6 +1,6 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; -import 'package:appflowy/mobile/presentation/base/gesture.dart'; +import 'package:appflowy/mobile/presentation/base/animated_gesture.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/share/export_service.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/share/export_service.dart index 7cf81b3bfb..e890959949 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/settings/share/export_service.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/settings/share/export_service.dart @@ -12,4 +12,12 @@ class BackendExportService { final payload = DatabaseViewIdPB.create()..value = viewId; return DatabaseEventExportCSV(payload).send(); } + + static Future> + exportDatabaseAsRawData( + String viewId, + ) async { + final payload = DatabaseViewIdPB.create()..value = viewId; + return DatabaseEventExportRawDatabaseData(payload).send(); + } } diff --git a/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart index 41ef345755..03a8f48be2 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart @@ -452,6 +452,9 @@ class SpaceBloc extends Bloc { )..start( sectionChanged: (result) async { Log.info('did receive section views changed'); + if (isClosed) { + return; + } add(const SpaceEvent.didReceiveSpaceUpdate()); }, ); diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/footer/sidebar_footer.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/footer/sidebar_footer.dart index c22bc95978..fa86082e74 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/footer/sidebar_footer.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/footer/sidebar_footer.dart @@ -1,17 +1,17 @@ -import 'package:appflowy/shared/feature_flags.dart'; -import 'package:appflowy/workspace/presentation/home/menu/sidebar/footer/sidebar_toast.dart'; -import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart'; -import 'package:flutter/material.dart'; - +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/shared/feature_flags.dart'; import 'package:appflowy/startup/plugin/plugin.dart'; import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart'; -import 'package:appflowy/workspace/presentation/home/home_sizes.dart'; import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart'; +import 'package:appflowy/workspace/presentation/home/menu/sidebar/footer/sidebar_toast.dart'; +import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart'; import 'package:easy_localization/easy_localization.dart'; -import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flutter/material.dart'; + +import 'sidebar_footer_button.dart'; class SidebarFooter extends StatelessWidget { const SidebarFooter({super.key}); @@ -26,52 +26,56 @@ class SidebarFooter extends StatelessWidget { return const SidebarToast(); }, ), - const Row( - children: [ - Expanded(child: SidebarTrashButton()), - // Enable it when the widget button is ready - // SizedBox( - // height: 16, - // child: VerticalDivider(width: 1, color: Color(0x141F2329)), - // ), - // Expanded(child: SidebarWidgetButton()), - ], - ), + const SidebarTemplateButton(), + const SidebarTrashButton(), ], ); } } +class SidebarTemplateButton extends StatelessWidget { + const SidebarTemplateButton({super.key}); + + @override + Widget build(BuildContext context) { + return SidebarFooterButton( + leftIconSize: const Size.square(24.0), + leftIcon: const Padding( + padding: EdgeInsets.all(2.0), + child: FlowySvg( + FlowySvgs.icon_template_s, + ), + ), + text: LocaleKeys.template_label.tr(), + onTap: () => afLaunchUrlString('https://appflowy.io/templates'), + ); + } +} + class SidebarTrashButton extends StatelessWidget { const SidebarTrashButton({super.key}); @override Widget build(BuildContext context) { - return SizedBox( - height: HomeSizes.workspaceSectionHeight, - child: ValueListenableBuilder( - valueListenable: getIt().notifier, - builder: (context, value, child) { - return FlowyButton( - leftIcon: const FlowySvg(FlowySvgs.sidebar_footer_trash_m), - leftIconSize: const Size.square(24.0), - iconPadding: 8.0, - margin: const EdgeInsets.all(4.0), - text: FlowyText.regular( - LocaleKeys.trash_text.tr(), - lineHeight: 1.15, - ), - onTap: () { - getIt().latestOpenView = null; - getIt().add( - TabsEvent.openPlugin( - plugin: makePlugin(pluginType: PluginType.trash), - ), - ); - }, - ); - }, - ), + return ValueListenableBuilder( + valueListenable: getIt().notifier, + builder: (context, value, child) { + return SidebarFooterButton( + leftIconSize: const Size.square(24.0), + leftIcon: const FlowySvg( + FlowySvgs.sidebar_footer_trash_m, + ), + text: LocaleKeys.trash_text.tr(), + onTap: () { + getIt().latestOpenView = null; + getIt().add( + TabsEvent.openPlugin( + plugin: makePlugin(pluginType: PluginType.trash), + ), + ); + }, + ); + }, ); } } diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/footer/sidebar_footer_button.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/footer/sidebar_footer_button.dart new file mode 100644 index 0000000000..f83e1dd046 --- /dev/null +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/footer/sidebar_footer_button.dart @@ -0,0 +1,39 @@ +import 'package:appflowy/workspace/presentation/home/home_sizes.dart'; +import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flutter/material.dart'; + +// This button style is used in +// - Trash button +// - Template button +class SidebarFooterButton extends StatelessWidget { + const SidebarFooterButton({ + super.key, + required this.leftIcon, + required this.leftIconSize, + required this.text, + required this.onTap, + }); + + final Widget leftIcon; + final Size leftIconSize; + final String text; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return SizedBox( + height: HomeSizes.workspaceSectionHeight, + child: FlowyButton( + leftIcon: leftIcon, + leftIconSize: leftIconSize, + iconPadding: 8.0, + margin: const EdgeInsets.all(4.0), + text: FlowyText.regular( + text, + lineHeight: 1.15, + ), + onTap: onTap, + ), + ); + } +} diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/dialogs.dart b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/dialogs.dart index 76a9421968..b8421235d3 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/widgets/dialogs.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/widgets/dialogs.dart @@ -97,6 +97,13 @@ class _NavigatorTextFieldDialogState extends State { VSpace(Insets.xl), OkCancelButton( onOkPressed: () { + if (newValue.isEmpty) { + showToastNotification( + context, + message: LocaleKeys.space_spaceNameCannotBeEmpty.tr(), + ); + return; + } widget.onConfirm(newValue, context); Navigator.of(context).pop(); }, diff --git a/frontend/resources/flowy_icons/16x/icon_template.svg b/frontend/resources/flowy_icons/16x/icon_template.svg new file mode 100644 index 0000000000..1b6af1bac6 --- /dev/null +++ b/frontend/resources/flowy_icons/16x/icon_template.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/frontend/resources/flowy_icons/40x/icon_warning.svg b/frontend/resources/flowy_icons/40x/icon_warning.svg new file mode 100644 index 0000000000..abdf5fb20d --- /dev/null +++ b/frontend/resources/flowy_icons/40x/icon_warning.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/frontend/resources/translations/en.json b/frontend/resources/translations/en.json index 2ff4a5a5f2..b2270cfd5d 100644 --- a/frontend/resources/translations/en.json +++ b/frontend/resources/translations/en.json @@ -1897,6 +1897,8 @@ "errorDialog": { "title": "@:appName Error", "howToFixFallback": "We're sorry for the inconvenience! Submit an issue on our GitHub page that describes your error.", + "howToFixFallbackHint1": "We're sorry for the inconvenience! Submit an issue on our ", + "howToFixFallbackHint2": " page that describes your error.", "github": "View on GitHub" }, "search": { @@ -2051,7 +2053,10 @@ }, "error": { "weAreSorry": "We're sorry", - "loadingViewError": "We're having trouble loading this view. Please check your internet connection, refresh the app, and do not hesitate to reach out to the team if the issue continues." + "loadingViewError": "We're having trouble loading this view. Please check your internet connection, refresh the app, and do not hesitate to reach out to the team if the issue continues.", + "syncError": "Data is not synced from another device", + "syncErrorHint": "Please reopen this page on the device where it was last edited, then open it again on the current device.", + "clickToCopy": "Click to copy error code" }, "editor": { "bold": "Bold", @@ -2317,7 +2322,8 @@ "quicklySwitch": "Quickly switch to the next space", "duplicate": "Duplicate Space", "movePageToSpace": "Move page to space", - "switchSpace": "Switch space" + "switchSpace": "Switch space", + "spaceNameCannotBeEmpty": "Space name cannot be empty" }, "publish": { "hasNotBeenPublished": "This page hasn't been published yet", @@ -2484,7 +2490,8 @@ "addRelatedTemplate": "Add related template", "removeRelatedTemplate": "Remove related template", "uploadAvatar": "Upload avatar", - "searchInCategory": "Search in {category}" + "searchInCategory": "Search in {category}", + "label": "Template" }, "fileDropzone": { "dropFile": "Click or drag file to this area to upload", diff --git a/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs b/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs index 241a96048b..9f793e9501 100644 --- a/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs +++ b/frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs @@ -377,7 +377,7 @@ impl FolderOperationHandler for DatabaseFolderOperation { } async fn duplicate_view(&self, view_id: &str) -> Result { - let delta_bytes = self.0.duplicate_database(view_id).await?; + let delta_bytes = self.0.get_database_json_bytes(view_id).await?; Ok(Bytes::from(delta_bytes)) } diff --git a/frontend/rust-lib/flowy-database2/src/entities/share_entities.rs b/frontend/rust-lib/flowy-database2/src/entities/share_entities.rs index b9fc85387f..981140e041 100644 --- a/frontend/rust-lib/flowy-database2/src/entities/share_entities.rs +++ b/frontend/rust-lib/flowy-database2/src/entities/share_entities.rs @@ -4,6 +4,9 @@ use flowy_derive::{ProtoBuf, ProtoBuf_Enum}; pub enum DatabaseExportDataType { #[default] CSV = 0, + + // DatabaseData + RawDatabaseData = 1, } #[derive(Debug, ProtoBuf, Default, Clone)] diff --git a/frontend/rust-lib/flowy-database2/src/event_handler.rs b/frontend/rust-lib/flowy-database2/src/event_handler.rs index 2a497e0e30..43f5010c3b 100644 --- a/frontend/rust-lib/flowy-database2/src/event_handler.rs +++ b/frontend/rust-lib/flowy-database2/src/event_handler.rs @@ -1027,6 +1027,20 @@ pub(crate) async fn export_csv_handler( }) } +#[tracing::instrument(level = "debug", skip_all, err)] +pub(crate) async fn export_raw_database_data_handler( + data: AFPluginData, + manager: AFPluginState>, +) -> DataResult { + let manager = upgrade_manager(manager)?; + let view_id = data.into_inner().value; + let data = manager.get_database_json_string(&view_id).await?; + data_result_ok(DatabaseExportDataPB { + export_type: DatabaseExportDataType::RawDatabaseData, + data, + }) +} + #[tracing::instrument(level = "debug", skip_all, err)] pub(crate) async fn get_snapshots_handler( data: AFPluginData, diff --git a/frontend/rust-lib/flowy-database2/src/event_map.rs b/frontend/rust-lib/flowy-database2/src/event_map.rs index 5b0db9d9ed..bbe01c2fe1 100644 --- a/frontend/rust-lib/flowy-database2/src/event_map.rs +++ b/frontend/rust-lib/flowy-database2/src/event_map.rs @@ -77,6 +77,7 @@ pub fn init(database_manager: Weak) -> AFPlugin { .event(DatabaseEvent::CreateDatabaseView, create_database_view) // Export .event(DatabaseEvent::ExportCSV, export_csv_handler) + .event(DatabaseEvent::ExportRawDatabaseData, export_raw_database_data_handler) .event(DatabaseEvent::GetDatabaseSnapshots, get_snapshots_handler) // Field settings .event(DatabaseEvent::GetFieldSettings, get_field_settings_handler) @@ -385,4 +386,7 @@ pub enum DatabaseEvent { #[event(input = "DatabaseViewIdPB", output = "RepeatedRowMetaPB")] GetAllRows = 177, + + #[event(input = "DatabaseViewIdPB", output = "DatabaseExportDataPB")] + ExportRawDatabaseData = 178, } diff --git a/frontend/rust-lib/flowy-database2/src/manager.rs b/frontend/rust-lib/flowy-database2/src/manager.rs index 0c4b13cab8..3885a8d0e6 100644 --- a/frontend/rust-lib/flowy-database2/src/manager.rs +++ b/frontend/rust-lib/flowy-database2/src/manager.rs @@ -345,7 +345,7 @@ impl DatabaseManager { Ok(()) } - pub async fn duplicate_database(&self, view_id: &str) -> FlowyResult> { + pub async fn get_database_json_bytes(&self, view_id: &str) -> FlowyResult> { let lock = self.workspace_database()?; let wdb = lock.read().await; let data = wdb.get_database_data(view_id).await?; @@ -353,6 +353,14 @@ impl DatabaseManager { Ok(json_bytes) } + pub async fn get_database_json_string(&self, view_id: &str) -> FlowyResult { + let lock = self.workspace_database()?; + let wdb = lock.read().await; + let data = wdb.get_database_data(view_id).await?; + let json_string = serde_json::to_string(&data)?; + Ok(json_string) + } + /// Create a new database with the given data that can be deserialized to [DatabaseData]. #[tracing::instrument(level = "trace", skip_all, err)] pub async fn create_database_with_database_data(