diff --git a/frontend/appflowy_flutter/integration_test/desktop/uncategorized/empty_test.dart b/frontend/appflowy_flutter/integration_test/desktop/uncategorized/empty_test.dart index a058ff2281..8ff3cf3c72 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/uncategorized/empty_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/uncategorized/empty_test.dart @@ -11,6 +11,7 @@ void main() { testWidgets('toggle theme mode', (tester) async { await tester.initializeAppFlowy(); await tester.tapAnonymousSignInButton(); + await tester.wait(1000); }); }); } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/appflowy_mobile_toolbar.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/appflowy_mobile_toolbar.dart index 95cd855667..894dde0442 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/appflowy_mobile_toolbar.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/appflowy_mobile_toolbar.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:io'; +import 'dart:math'; import 'package:appflowy/plugins/document/application/document_bloc.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/aa_menu/_close_keyboard_or_menu_button.dart'; @@ -9,6 +10,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_too import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:collection/collection.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -284,6 +286,14 @@ class _MobileToolbarState extends State<_MobileToolbar> if (canUpdateCachedKeyboardHeight) { cachedKeyboardHeight.value = height; + + if (defaultTargetPlatform == TargetPlatform.android) { + // cache the keyboard height with the view padding in Android + if (cachedKeyboardHeight.value != 0) { + cachedKeyboardHeight.value += + MediaQuery.of(context).viewPadding.bottom; + } + } } if (height == 0) { @@ -385,13 +395,19 @@ class _MobileToolbarState extends State<_MobileToolbar> return ValueListenableBuilder( valueListenable: showMenuNotifier, builder: (_, showingMenu, __) { - var paddingHeight = height; - if (Platform.isAndroid) { - paddingHeight = - height + MediaQuery.of(context).viewPadding.bottom; + var keyboardHeight = height; + if (defaultTargetPlatform == TargetPlatform.android) { + if (!showingMenu) { + // take the max value of the keyboard height and the view padding + // to make sure the toolbar is above the keyboard + keyboardHeight = max( + keyboardHeight, + MediaQuery.of(context).viewInsets.bottom, + ); + } } return SizedBox( - height: paddingHeight, + height: keyboardHeight, child: (showingMenu && selectedMenuIndex != null) ? widget.toolbarItems[selectedMenuIndex!].menuBuilder?.call( context, diff --git a/frontend/appflowy_flutter/lib/workspace/application/home/home_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/home/home_bloc.dart index 9d635fd692..2fb801595c 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/home/home_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/home/home_bloc.dart @@ -5,6 +5,7 @@ import 'package:appflowy_backend/protobuf/flowy-folder/workspace.pb.dart' show WorkspaceSettingPB; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; + part 'home_bloc.freezed.dart'; class HomeBloc extends Bloc { diff --git a/frontend/appflowy_flutter/lib/workspace/application/menu/sidebar_sections_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/menu/sidebar_sections_bloc.dart index e38f4cf0da..0da3d973db 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/menu/sidebar_sections_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/menu/sidebar_sections_bloc.dart @@ -54,9 +54,11 @@ class SidebarSectionsBloc _initial(userProfile, workspaceId); final sectionViews = await _getSectionViews(); if (sectionViews != null) { + final containsSpace = _containsSpace(sectionViews); emit( state.copyWith( section: sectionViews, + containsSpace: containsSpace, ), ); } @@ -65,9 +67,11 @@ class SidebarSectionsBloc _reset(userProfile, workspaceId); final sectionViews = await _getSectionViews(); if (sectionViews != null) { + final containsSpace = _containsSpace(sectionViews); emit( state.copyWith( section: sectionViews, + containsSpace: containsSpace, ), ); } @@ -160,9 +164,11 @@ class SidebarSectionsBloc _initial(userProfile, workspaceId); final sectionViews = await _getSectionViews(); if (sectionViews != null) { + final containsSpace = _containsSpace(sectionViews); emit( state.copyWith( section: sectionViews, + containsSpace: containsSpace, ), ); // try to open the fist view in public section or private section @@ -229,6 +235,11 @@ class SidebarSectionsBloc } } + bool _containsSpace(SidebarSection section) { + return section.publicViews.any((view) => view.isSpace) || + section.privateViews.any((view) => view.isSpace); + } + void _initial(UserProfilePB userProfile, String workspaceId) { _workspaceService = WorkspaceService(workspaceId: workspaceId); @@ -292,6 +303,7 @@ class SidebarSectionsState with _$SidebarSectionsState { required SidebarSection section, @Default(null) ViewPB? lastCreatedRootView, FlowyResult? createRootViewResult, + @Default(true) bool containsSpace, }) = _SidebarSectionsState; factory SidebarSectionsState.initial() => const SidebarSectionsState( 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 eeb5043920..f152945d60 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 @@ -4,12 +4,14 @@ import 'dart:convert'; import 'package:appflowy/core/config/kv.dart'; import 'package:appflowy/core/config/kv_keys.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/shared/list_extension.dart'; import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/user/application/user_service.dart'; +import 'package:appflowy/workspace/application/view/prelude.dart'; import 'package:appflowy/workspace/application/view/view_ext.dart'; import 'package:appflowy/workspace/application/view/view_service.dart'; +import 'package:appflowy/workspace/application/workspace/prelude.dart'; import 'package:appflowy/workspace/application/workspace/workspace_sections_listener.dart'; -import 'package:appflowy/workspace/application/workspace/workspace_service.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_icon_popup.dart'; import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; @@ -20,6 +22,7 @@ import 'package:appflowy_result/appflowy_result.dart'; import 'package:collection/collection.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/uuid.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:protobuf/protobuf.dart'; @@ -68,6 +71,7 @@ class SpaceBloc extends Bloc { _initial(userProfile, workspaceId); final (spaces, publicViews, privateViews) = await _getSpaces(); + final shouldShowUpgradeDialog = await this.shouldShowUpgradeDialog( spaces: spaces, publicViews: publicViews, @@ -82,13 +86,12 @@ class SpaceBloc extends Bloc { currentSpace: currentSpace, isExpanded: isExpanded, shouldShowUpgradeDialog: shouldShowUpgradeDialog, + isInitialized: true, ), ); - if (shouldShowUpgradeDialog) { - if (!integrationMode().isTest) { - add(const SpaceEvent.migrate()); - } + if (shouldShowUpgradeDialog && !integrationMode().isTest) { + add(const SpaceEvent.migrate()); } if (openFirstPage) { @@ -192,13 +195,31 @@ class SpaceBloc extends Bloc { open: (space) async { await _openSpace(space); final isExpanded = await _getSpaceExpandStatus(space); - emit(state.copyWith(currentSpace: space, isExpanded: isExpanded)); + final views = await ViewBackendService.getChildViews( + viewId: space.id, + ); + final currentSpace = views.fold( + (views) { + space.freeze(); + return space.rebuild((b) { + b.childViews.clear(); + b.childViews.addAll(views); + }); + }, + (_) => space, + ); + emit( + state.copyWith( + currentSpace: currentSpace, + isExpanded: isExpanded, + ), + ); // don't open the page automatically on mobile if (PlatformExtension.isDesktop) { // open the first page by default - if (space.childViews.isNotEmpty) { - final firstPage = space.childViews.first; + if (currentSpace.childViews.isNotEmpty) { + final firstPage = currentSpace.childViews.first; emit( state.copyWith( lastCreatedPage: firstPage, @@ -330,8 +351,9 @@ class SpaceBloc extends Bloc { if (sectionViews == null || sectionViews.views.isEmpty) { return ([], [], []); } - final publicViews = sectionViews.publicViews; - final privateViews = sectionViews.privateViews; + + final publicViews = sectionViews.publicViews.unique((e) => e.id); + final privateViews = sectionViews.privateViews.unique((e) => e.id); final publicSpaces = publicViews.where((e) => e.isSpace); final privateSpaces = privateViews.where((e) => e.isSpace); @@ -690,6 +712,7 @@ class SpaceState with _$SpaceState { FlowyResult? createPageResult, @Default(false) bool shouldShowUpgradeDialog, @Default(false) bool isDuplicatingSpace, + @Default(false) bool isInitialized, }) = _SpaceState; factory SpaceState.initial() => const SpaceState(); diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar.dart index f2abaecc1d..75936f42d5 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar.dart @@ -1,7 +1,5 @@ import 'dart:async'; -import 'package:flutter/material.dart'; - import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/plugins/blank/blank.dart'; @@ -29,14 +27,16 @@ import 'package:appflowy/workspace/presentation/home/menu/sidebar/shared/sidebar import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/sidebar_space.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_migration.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/sidebar_workspace.dart'; +import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/workspace.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart' show UserProfilePB; -import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:appflowy_editor/appflowy_editor.dart' hide Log; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/style_widget/button.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; Loading? _duplicateSpaceLoading; @@ -360,10 +360,29 @@ class _SidebarState extends State<_Sidebar> { Widget _renderFolderOrSpace(EdgeInsets menuHorizontalInset) { final spaceState = context.read().state; final workspaceState = context.read().state; + + if (!spaceState.isInitialized) { + return const SizedBox.shrink(); + } + // there's no space or the workspace is not collaborative, // show the folder section (Workspace, Private, Personal) // otherwise, show the space - return spaceState.spaces.isEmpty || !workspaceState.isCollabWorkspaceOn + final sidebarSectionBloc = context.watch(); + final containsSpace = sidebarSectionBloc.state.containsSpace; + + Log.info('fetch the space info from sidebar section: $containsSpace'); + Log.info( + 'fetch the space info from space: ${spaceState.spaces.isNotEmpty}', + ); + + if (containsSpace && spaceState.spaces.isEmpty) { + context.read().add(const SpaceEvent.didReceiveSpaceUpdate()); + } + + return !containsSpace || + spaceState.spaces.isEmpty || + !workspaceState.isCollabWorkspaceOn ? Expanded( child: Padding( padding: menuHorizontalInset - const EdgeInsets.only(right: 6),