fix: take the max value of the keyboard height and the view insets bottom to make the toolbar visible (#5700)

* fix: android toolbar will be blocked if the scroll bar is hidden

* fix: last opened view is synced in same workspace

* fix: fallback to space if the section sidebar contains space

* chore: revert last opened view code

* fix: integration tests
This commit is contained in:
Lucas.Xu 2024-07-08 13:15:42 +08:00 committed by GitHub
parent 2d11215bb2
commit 2ecc2a67a9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 91 additions and 19 deletions

View File

@ -11,6 +11,7 @@ void main() {
testWidgets('toggle theme mode', (tester) async {
await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton();
await tester.wait(1000);
});
});
}

View File

@ -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,

View File

@ -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<HomeEvent, HomeState> {

View File

@ -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<void, FlowyError>? createRootViewResult,
@Default(true) bool containsSpace,
}) = _SidebarSectionsState;
factory SidebarSectionsState.initial() => const SidebarSectionsState(

View File

@ -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<SpaceEvent, SpaceState> {
_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<SpaceEvent, SpaceState> {
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<SpaceEvent, SpaceState> {
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<SpaceEvent, SpaceState> {
if (sectionViews == null || sectionViews.views.isEmpty) {
return (<ViewPB>[], <ViewPB>[], <ViewPB>[]);
}
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<void, FlowyError>? createPageResult,
@Default(false) bool shouldShowUpgradeDialog,
@Default(false) bool isDuplicatingSpace,
@Default(false) bool isInitialized,
}) = _SpaceState;
factory SpaceState.initial() => const SpaceState();

View File

@ -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<SpaceBloc>().state;
final workspaceState = context.read<UserWorkspaceBloc>().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<SidebarSectionsBloc>();
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<SpaceBloc>().add(const SpaceEvent.didReceiveSpaceUpdate());
}
return !containsSpace ||
spaceState.spaces.isEmpty ||
!workspaceState.isCollabWorkspaceOn
? Expanded(
child: Padding(
padding: menuHorizontalInset - const EdgeInsets.only(right: 6),