mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
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:
parent
2d11215bb2
commit
2ecc2a67a9
@ -11,6 +11,7 @@ void main() {
|
|||||||
testWidgets('toggle theme mode', (tester) async {
|
testWidgets('toggle theme mode', (tester) async {
|
||||||
await tester.initializeAppFlowy();
|
await tester.initializeAppFlowy();
|
||||||
await tester.tapAnonymousSignInButton();
|
await tester.tapAnonymousSignInButton();
|
||||||
|
await tester.wait(1000);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:appflowy/plugins/document/application/document_bloc.dart';
|
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';
|
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:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
@ -284,6 +286,14 @@ class _MobileToolbarState extends State<_MobileToolbar>
|
|||||||
|
|
||||||
if (canUpdateCachedKeyboardHeight) {
|
if (canUpdateCachedKeyboardHeight) {
|
||||||
cachedKeyboardHeight.value = height;
|
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) {
|
if (height == 0) {
|
||||||
@ -385,13 +395,19 @@ class _MobileToolbarState extends State<_MobileToolbar>
|
|||||||
return ValueListenableBuilder(
|
return ValueListenableBuilder(
|
||||||
valueListenable: showMenuNotifier,
|
valueListenable: showMenuNotifier,
|
||||||
builder: (_, showingMenu, __) {
|
builder: (_, showingMenu, __) {
|
||||||
var paddingHeight = height;
|
var keyboardHeight = height;
|
||||||
if (Platform.isAndroid) {
|
if (defaultTargetPlatform == TargetPlatform.android) {
|
||||||
paddingHeight =
|
if (!showingMenu) {
|
||||||
height + MediaQuery.of(context).viewPadding.bottom;
|
// 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(
|
return SizedBox(
|
||||||
height: paddingHeight,
|
height: keyboardHeight,
|
||||||
child: (showingMenu && selectedMenuIndex != null)
|
child: (showingMenu && selectedMenuIndex != null)
|
||||||
? widget.toolbarItems[selectedMenuIndex!].menuBuilder?.call(
|
? widget.toolbarItems[selectedMenuIndex!].menuBuilder?.call(
|
||||||
context,
|
context,
|
||||||
|
@ -5,6 +5,7 @@ import 'package:appflowy_backend/protobuf/flowy-folder/workspace.pb.dart'
|
|||||||
show WorkspaceSettingPB;
|
show WorkspaceSettingPB;
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
part 'home_bloc.freezed.dart';
|
part 'home_bloc.freezed.dart';
|
||||||
|
|
||||||
class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||||
|
@ -54,9 +54,11 @@ class SidebarSectionsBloc
|
|||||||
_initial(userProfile, workspaceId);
|
_initial(userProfile, workspaceId);
|
||||||
final sectionViews = await _getSectionViews();
|
final sectionViews = await _getSectionViews();
|
||||||
if (sectionViews != null) {
|
if (sectionViews != null) {
|
||||||
|
final containsSpace = _containsSpace(sectionViews);
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
section: sectionViews,
|
section: sectionViews,
|
||||||
|
containsSpace: containsSpace,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -65,9 +67,11 @@ class SidebarSectionsBloc
|
|||||||
_reset(userProfile, workspaceId);
|
_reset(userProfile, workspaceId);
|
||||||
final sectionViews = await _getSectionViews();
|
final sectionViews = await _getSectionViews();
|
||||||
if (sectionViews != null) {
|
if (sectionViews != null) {
|
||||||
|
final containsSpace = _containsSpace(sectionViews);
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
section: sectionViews,
|
section: sectionViews,
|
||||||
|
containsSpace: containsSpace,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -160,9 +164,11 @@ class SidebarSectionsBloc
|
|||||||
_initial(userProfile, workspaceId);
|
_initial(userProfile, workspaceId);
|
||||||
final sectionViews = await _getSectionViews();
|
final sectionViews = await _getSectionViews();
|
||||||
if (sectionViews != null) {
|
if (sectionViews != null) {
|
||||||
|
final containsSpace = _containsSpace(sectionViews);
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
section: sectionViews,
|
section: sectionViews,
|
||||||
|
containsSpace: containsSpace,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
// try to open the fist view in public section or private section
|
// 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) {
|
void _initial(UserProfilePB userProfile, String workspaceId) {
|
||||||
_workspaceService = WorkspaceService(workspaceId: workspaceId);
|
_workspaceService = WorkspaceService(workspaceId: workspaceId);
|
||||||
|
|
||||||
@ -292,6 +303,7 @@ class SidebarSectionsState with _$SidebarSectionsState {
|
|||||||
required SidebarSection section,
|
required SidebarSection section,
|
||||||
@Default(null) ViewPB? lastCreatedRootView,
|
@Default(null) ViewPB? lastCreatedRootView,
|
||||||
FlowyResult<void, FlowyError>? createRootViewResult,
|
FlowyResult<void, FlowyError>? createRootViewResult,
|
||||||
|
@Default(true) bool containsSpace,
|
||||||
}) = _SidebarSectionsState;
|
}) = _SidebarSectionsState;
|
||||||
|
|
||||||
factory SidebarSectionsState.initial() => const SidebarSectionsState(
|
factory SidebarSectionsState.initial() => const SidebarSectionsState(
|
||||||
|
@ -4,12 +4,14 @@ import 'dart:convert';
|
|||||||
import 'package:appflowy/core/config/kv.dart';
|
import 'package:appflowy/core/config/kv.dart';
|
||||||
import 'package:appflowy/core/config/kv_keys.dart';
|
import 'package:appflowy/core/config/kv_keys.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.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/startup/startup.dart';
|
||||||
import 'package:appflowy/user/application/user_service.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_ext.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_service.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_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/workspace/presentation/home/menu/sidebar/space/space_icon_popup.dart';
|
||||||
import 'package:appflowy_backend/log.dart';
|
import 'package:appflowy_backend/log.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.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:collection/collection.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra/uuid.dart';
|
import 'package:flowy_infra/uuid.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:protobuf/protobuf.dart';
|
import 'package:protobuf/protobuf.dart';
|
||||||
@ -68,6 +71,7 @@ class SpaceBloc extends Bloc<SpaceEvent, SpaceState> {
|
|||||||
_initial(userProfile, workspaceId);
|
_initial(userProfile, workspaceId);
|
||||||
|
|
||||||
final (spaces, publicViews, privateViews) = await _getSpaces();
|
final (spaces, publicViews, privateViews) = await _getSpaces();
|
||||||
|
|
||||||
final shouldShowUpgradeDialog = await this.shouldShowUpgradeDialog(
|
final shouldShowUpgradeDialog = await this.shouldShowUpgradeDialog(
|
||||||
spaces: spaces,
|
spaces: spaces,
|
||||||
publicViews: publicViews,
|
publicViews: publicViews,
|
||||||
@ -82,14 +86,13 @@ class SpaceBloc extends Bloc<SpaceEvent, SpaceState> {
|
|||||||
currentSpace: currentSpace,
|
currentSpace: currentSpace,
|
||||||
isExpanded: isExpanded,
|
isExpanded: isExpanded,
|
||||||
shouldShowUpgradeDialog: shouldShowUpgradeDialog,
|
shouldShowUpgradeDialog: shouldShowUpgradeDialog,
|
||||||
|
isInitialized: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (shouldShowUpgradeDialog) {
|
if (shouldShowUpgradeDialog && !integrationMode().isTest) {
|
||||||
if (!integrationMode().isTest) {
|
|
||||||
add(const SpaceEvent.migrate());
|
add(const SpaceEvent.migrate());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (openFirstPage) {
|
if (openFirstPage) {
|
||||||
if (currentSpace != null) {
|
if (currentSpace != null) {
|
||||||
@ -192,13 +195,31 @@ class SpaceBloc extends Bloc<SpaceEvent, SpaceState> {
|
|||||||
open: (space) async {
|
open: (space) async {
|
||||||
await _openSpace(space);
|
await _openSpace(space);
|
||||||
final isExpanded = await _getSpaceExpandStatus(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
|
// don't open the page automatically on mobile
|
||||||
if (PlatformExtension.isDesktop) {
|
if (PlatformExtension.isDesktop) {
|
||||||
// open the first page by default
|
// open the first page by default
|
||||||
if (space.childViews.isNotEmpty) {
|
if (currentSpace.childViews.isNotEmpty) {
|
||||||
final firstPage = space.childViews.first;
|
final firstPage = currentSpace.childViews.first;
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
lastCreatedPage: firstPage,
|
lastCreatedPage: firstPage,
|
||||||
@ -330,8 +351,9 @@ class SpaceBloc extends Bloc<SpaceEvent, SpaceState> {
|
|||||||
if (sectionViews == null || sectionViews.views.isEmpty) {
|
if (sectionViews == null || sectionViews.views.isEmpty) {
|
||||||
return (<ViewPB>[], <ViewPB>[], <ViewPB>[]);
|
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 publicSpaces = publicViews.where((e) => e.isSpace);
|
||||||
final privateSpaces = privateViews.where((e) => e.isSpace);
|
final privateSpaces = privateViews.where((e) => e.isSpace);
|
||||||
@ -690,6 +712,7 @@ class SpaceState with _$SpaceState {
|
|||||||
FlowyResult<void, FlowyError>? createPageResult,
|
FlowyResult<void, FlowyError>? createPageResult,
|
||||||
@Default(false) bool shouldShowUpgradeDialog,
|
@Default(false) bool shouldShowUpgradeDialog,
|
||||||
@Default(false) bool isDuplicatingSpace,
|
@Default(false) bool isDuplicatingSpace,
|
||||||
|
@Default(false) bool isInitialized,
|
||||||
}) = _SpaceState;
|
}) = _SpaceState;
|
||||||
|
|
||||||
factory SpaceState.initial() => const SpaceState();
|
factory SpaceState.initial() => const SpaceState();
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/plugins/blank/blank.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/sidebar_space.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/space_migration.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/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-folder/workspace.pb.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
|
||||||
show UserProfilePB;
|
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:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
Loading? _duplicateSpaceLoading;
|
Loading? _duplicateSpaceLoading;
|
||||||
@ -360,10 +360,29 @@ class _SidebarState extends State<_Sidebar> {
|
|||||||
Widget _renderFolderOrSpace(EdgeInsets menuHorizontalInset) {
|
Widget _renderFolderOrSpace(EdgeInsets menuHorizontalInset) {
|
||||||
final spaceState = context.read<SpaceBloc>().state;
|
final spaceState = context.read<SpaceBloc>().state;
|
||||||
final workspaceState = context.read<UserWorkspaceBloc>().state;
|
final workspaceState = context.read<UserWorkspaceBloc>().state;
|
||||||
|
|
||||||
|
if (!spaceState.isInitialized) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
// there's no space or the workspace is not collaborative,
|
// there's no space or the workspace is not collaborative,
|
||||||
// show the folder section (Workspace, Private, Personal)
|
// show the folder section (Workspace, Private, Personal)
|
||||||
// otherwise, show the space
|
// 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(
|
? Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: menuHorizontalInset - const EdgeInsets.only(right: 6),
|
padding: menuHorizontalInset - const EdgeInsets.only(right: 6),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user