fix: + button in the navigation bar doesn't work when the space is not initialized (#5862)

This commit is contained in:
Lucas.Xu 2024-08-02 13:11:52 +08:00 committed by GitHub
parent cb60488bbe
commit e9fc003e10
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 265 additions and 183 deletions

View File

@ -1,10 +1,8 @@
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/mobile/application/mobile_router.dart';
import 'package:appflowy/mobile/presentation/home/home.dart';
import 'package:appflowy/mobile/presentation/home/section_folder/mobile_home_section_folder.dart'; import 'package:appflowy/mobile/presentation/home/section_folder/mobile_home_section_folder.dart';
import 'package:appflowy/mobile/presentation/home/space/mobile_space.dart'; import 'package:appflowy/mobile/presentation/home/space/mobile_space.dart';
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart'; import 'package:appflowy/mobile/presentation/presentation.dart';
import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart'; import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart';
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart'; import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart'; import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart';
@ -33,78 +31,56 @@ class MobileFolders extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MultiBlocProvider( final workspaceId =
providers: [ context.read<UserWorkspaceBloc>().state.currentWorkspace?.workspaceId ??
BlocProvider( '';
create: (_) => SidebarSectionsBloc() return BlocListener<UserWorkspaceBloc, UserWorkspaceState>(
..add(SidebarSectionsEvent.initial(user, workspaceId)), listener: (context, state) {
), context.read<SidebarSectionsBloc>().add(
BlocProvider( SidebarSectionsEvent.initial(
create: (_) => FavoriteBloc()..add(const FavoriteEvent.initial()), user,
), state.currentWorkspace?.workspaceId ?? workspaceId,
BlocProvider( ),
create: (_) => SpaceBloc() );
..add(SpaceEvent.initial(user, workspaceId, openFirstPage: false)), context.read<SpaceBloc>().add(
), SpaceEvent.reset(
], user,
child: BlocListener<UserWorkspaceBloc, UserWorkspaceState>( state.currentWorkspace?.workspaceId ?? workspaceId,
listener: (context, state) { ),
context.read<SidebarSectionsBloc>().add( );
SidebarSectionsEvent.initial( },
user, child: const _MobileFolder(),
state.currentWorkspace?.workspaceId ?? workspaceId, );
}
}
class _MobileFolder extends StatefulWidget {
const _MobileFolder();
@override
State<_MobileFolder> createState() => _MobileFolderState();
}
class _MobileFolderState extends State<_MobileFolder> {
@override
Widget build(BuildContext context) {
return BlocBuilder<SidebarSectionsBloc, SidebarSectionsState>(
builder: (context, state) {
return SlidableAutoCloseBehavior(
child: Column(
children: [
..._buildSpaceOrSection(context, state),
const VSpace(4.0),
const Padding(
padding: EdgeInsets.symmetric(
horizontal: HomeSpaceViewSizes.mHorizontalPadding,
), ),
); child: _TrashButton(),
context.read<SpaceBloc>().add( ),
SpaceEvent.reset( ],
user,
state.currentWorkspace?.workspaceId ?? workspaceId,
),
);
},
child: MultiBlocListener(
listeners: [
BlocListener<SpaceBloc, SpaceState>(
listenWhen: (p, c) =>
p.lastCreatedPage?.id != c.lastCreatedPage?.id,
listener: (context, state) {
final lastCreatedPage = state.lastCreatedPage;
if (lastCreatedPage != null) {
context.pushView(lastCreatedPage);
}
},
),
BlocListener<SidebarSectionsBloc, SidebarSectionsState>(
listenWhen: (p, c) =>
p.lastCreatedRootView?.id != c.lastCreatedRootView?.id,
listener: (context, state) {
final lastCreatedPage = state.lastCreatedRootView;
if (lastCreatedPage != null) {
context.pushView(lastCreatedPage);
}
},
),
],
child: BlocBuilder<SidebarSectionsBloc, SidebarSectionsState>(
builder: (context, state) {
return SlidableAutoCloseBehavior(
child: Column(
children: [
..._buildSpaceOrSection(context, state),
const VSpace(4.0),
const Padding(
padding: EdgeInsets.symmetric(
horizontal: HomeSpaceViewSizes.mHorizontalPadding,
),
child: _TrashButton(),
),
],
),
);
},
), ),
), );
), },
); );
} }

View File

@ -7,7 +7,9 @@ import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/auth/auth_service.dart'; import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy/user/application/reminder/reminder_bloc.dart'; import 'package:appflowy/user/application/reminder/reminder_bloc.dart';
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart'; import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart';
import 'package:appflowy/workspace/application/recent/cached_recent_service.dart'; import 'package:appflowy/workspace/application/recent/cached_recent_service.dart';
import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart';
import 'package:appflowy/workspace/application/user/user_workspace_bloc.dart'; import 'package:appflowy/workspace/application/user/user_workspace_bloc.dart';
import 'package:appflowy/workspace/presentation/home/errors/workspace_failed_screen.dart'; import 'package:appflowy/workspace/presentation/home/errors/workspace_failed_screen.dart';
import 'package:appflowy/workspace/presentation/home/home_sizes.dart'; import 'package:appflowy/workspace/presentation/home/home_sizes.dart';
@ -102,6 +104,7 @@ class _MobileHomePageState extends State<MobileHomePage> {
@override @override
void dispose() { void dispose() {
getIt<MenuSharedState>().removeLatestViewListener(_onLatestViewChange); getIt<MenuSharedState>().removeLatestViewListener(_onLatestViewChange);
super.dispose(); super.dispose();
} }
@ -134,6 +137,8 @@ class _MobileHomePageState extends State<MobileHomePage> {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
final workspaceId = state.currentWorkspace!.workspaceId;
return Column( return Column(
children: [ children: [
// Header // Header
@ -149,9 +154,36 @@ class _MobileHomePageState extends State<MobileHomePage> {
), ),
Expanded( Expanded(
child: BlocProvider( child: MultiBlocProvider(
create: (context) => providers: [
SpaceOrderBloc()..add(const SpaceOrderEvent.initial()), BlocProvider(
create: (_) => SpaceOrderBloc()
..add(const SpaceOrderEvent.initial()),
),
BlocProvider(
create: (_) => SidebarSectionsBloc()
..add(
SidebarSectionsEvent.initial(
widget.userProfile,
workspaceId,
),
),
),
BlocProvider(
create: (_) =>
FavoriteBloc()..add(const FavoriteEvent.initial()),
),
BlocProvider(
create: (_) => SpaceBloc()
..add(
SpaceEvent.initial(
widget.userProfile,
workspaceId,
openFirstPage: false,
),
),
),
],
child: MobileSpaceTab( child: MobileSpaceTab(
userProfile: widget.userProfile, userProfile: widget.userProfile,
), ),

View File

@ -1,6 +1,6 @@
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/application/mobile_router.dart'; import 'package:appflowy/mobile/application/mobile_router.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/default_mobile_action_pane.dart'; import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
import 'package:appflowy/mobile/presentation/home/section_folder/mobile_home_section_folder_header.dart'; import 'package:appflowy/mobile/presentation/home/section_folder/mobile_home_section_folder_header.dart';
import 'package:appflowy/mobile/presentation/page_item/mobile_view_item.dart'; import 'package:appflowy/mobile/presentation/page_item/mobile_view_item.dart';
import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart'; import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart';
@ -43,48 +43,17 @@ class MobileSectionFolder extends StatelessWidget {
onPressed: () => context onPressed: () => context
.read<FolderBloc>() .read<FolderBloc>()
.add(const FolderEvent.expandOrUnExpand()), .add(const FolderEvent.expandOrUnExpand()),
onAdded: () { onAdded: () => _createNewPage(context),
context.read<SidebarSectionsBloc>().add(
SidebarSectionsEvent.createRootViewInSection(
name: LocaleKeys.menuAppHeader_defaultNewPageName
.tr(),
index: 0,
viewSection: spaceType.toViewSectionPB,
),
);
context.read<FolderBloc>().add(
const FolderEvent.expandOrUnExpand(isExpanded: true),
);
},
), ),
), ),
if (state.isExpanded) if (state.isExpanded)
...views.map( Padding(
(view) => MobileViewItem( padding: const EdgeInsets.only(
key: ValueKey( left: HomeSpaceViewSizes.leftPadding,
'${FolderSpaceType.private.name} ${view.id}', ),
), child: _Pages(
views: views,
spaceType: spaceType, spaceType: spaceType,
isFirstChild: view.id == views.first.id,
view: view,
level: 0,
leftPadding: HomeSpaceViewSizes.leftPadding,
isFeedback: false,
onSelected: context.pushView,
endActionPane: (context) {
final view = context.read<ViewBloc>().state.view;
return buildEndActionPane(
context,
[
MobilePaneActionType.more,
if (view.layout == ViewLayoutPB.Document)
MobilePaneActionType.add,
],
spaceType: spaceType,
needSpace: false,
spaceRatio: 5,
);
},
), ),
), ),
], ],
@ -93,4 +62,63 @@ class MobileSectionFolder extends StatelessWidget {
), ),
); );
} }
void _createNewPage(BuildContext context) {
context.read<SidebarSectionsBloc>().add(
SidebarSectionsEvent.createRootViewInSection(
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
index: 0,
viewSection: spaceType.toViewSectionPB,
),
);
context.read<FolderBloc>().add(
const FolderEvent.expandOrUnExpand(isExpanded: true),
);
}
}
class _Pages extends StatelessWidget {
const _Pages({
required this.views,
required this.spaceType,
});
final List<ViewPB> views;
final FolderSpaceType spaceType;
@override
Widget build(BuildContext context) {
return Column(
children: views
.map(
(view) => MobileViewItem(
key: ValueKey(
'${FolderSpaceType.private.name} ${view.id}',
),
spaceType: spaceType,
isFirstChild: view.id == views.first.id,
view: view,
level: 0,
leftPadding: HomeSpaceViewSizes.leftPadding,
isFeedback: false,
onSelected: context.pushView,
endActionPane: (context) {
final view = context.read<ViewBloc>().state.view;
return buildEndActionPane(
context,
[
MobilePaneActionType.more,
if (view.layout == ViewLayoutPB.Document)
MobilePaneActionType.add,
],
spaceType: spaceType,
needSpace: false,
spaceRatio: 5,
);
},
),
)
.toList(),
);
}
} }

View File

@ -32,6 +32,7 @@ class _MobileSectionFolderHeaderState extends State<MobileSectionFolderHeader> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Row( return Row(
children: [ children: [
const HSpace(HomeSpaceViewSizes.mHorizontalPadding),
Expanded( Expanded(
child: FlowyButton( child: FlowyButton(
text: FlowyText.medium( text: FlowyText.medium(
@ -57,15 +58,19 @@ class _MobileSectionFolderHeaderState extends State<MobileSectionFolderHeader> {
}, },
), ),
), ),
FlowyIconButton( GestureDetector(
key: mobileCreateNewPageButtonKey, behavior: HitTestBehavior.translucent,
hoverColor: Theme.of(context).colorScheme.secondaryContainer, onTap: widget.onAdded,
height: HomeSpaceViewSizes.mViewButtonDimension, child: Container(
width: HomeSpaceViewSizes.mViewButtonDimension, // expand the touch area
icon: const FlowySvg( margin: const EdgeInsets.symmetric(
FlowySvgs.m_space_add_s, horizontal: HomeSpaceViewSizes.mHorizontalPadding,
vertical: 8.0,
),
child: const FlowySvg(
FlowySvgs.m_space_add_s,
),
), ),
onPressed: widget.onAdded,
), ),
], ],
); );

View File

@ -4,7 +4,6 @@ import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
import 'package:appflowy/mobile/presentation/home/space/mobile_space_header.dart'; import 'package:appflowy/mobile/presentation/home/space/mobile_space_header.dart';
import 'package:appflowy/mobile/presentation/home/space/mobile_space_menu.dart'; import 'package:appflowy/mobile/presentation/home/space/mobile_space_menu.dart';
import 'package:appflowy/mobile/presentation/page_item/mobile_view_item.dart'; import 'package:appflowy/mobile/presentation/page_item/mobile_view_item.dart';
import 'package:appflowy/mobile/presentation/presentation.dart';
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart'; import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart'; import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart';
import 'package:appflowy/workspace/application/view/view_bloc.dart'; import 'package:appflowy/workspace/application/view/view_bloc.dart';
@ -15,26 +14,9 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
class MobileSpace extends StatefulWidget { class MobileSpace extends StatelessWidget {
const MobileSpace({super.key}); const MobileSpace({super.key});
@override
State<MobileSpace> createState() => _MobileSpaceState();
}
class _MobileSpaceState extends State<MobileSpace> {
@override
void initState() {
super.initState();
createNewPageNotifier.addListener(_createNewPage);
}
@override
void dispose() {
createNewPageNotifier.removeListener(_createNewPage);
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<SpaceBloc, SpaceState>( return BlocBuilder<SpaceBloc, SpaceState>(
@ -50,7 +32,7 @@ class _MobileSpaceState extends State<MobileSpace> {
MobileSpaceHeader( MobileSpaceHeader(
isExpanded: state.isExpanded, isExpanded: state.isExpanded,
space: currentSpace, space: currentSpace,
onAdded: () => _showCreatePageMenu(currentSpace), onAdded: () => _showCreatePageMenu(context, currentSpace),
onPressed: () => _showSpaceMenu(context), onPressed: () => _showSpaceMenu(context),
), ),
Padding( Padding(
@ -91,16 +73,7 @@ class _MobileSpaceState extends State<MobileSpace> {
); );
} }
void _createNewPage() { void _showCreatePageMenu(BuildContext context, ViewPB space) {
context.read<SpaceBloc>().add(
SpaceEvent.createPage(
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
layout: ViewLayoutPB.Document,
),
);
}
void _showCreatePageMenu(ViewPB space) {
final title = space.name; final title = space.name;
showMobileBottomSheet( showMobileBottomSheet(
context, context,

View File

@ -1,16 +1,27 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/application/mobile_router.dart';
import 'package:appflowy/mobile/presentation/home/favorite_folder/favorite_space.dart'; import 'package:appflowy/mobile/presentation/home/favorite_folder/favorite_space.dart';
import 'package:appflowy/mobile/presentation/home/home_space/home_space.dart'; import 'package:appflowy/mobile/presentation/home/home_space/home_space.dart';
import 'package:appflowy/mobile/presentation/home/recent_folder/recent_space.dart'; import 'package:appflowy/mobile/presentation/home/recent_folder/recent_space.dart';
import 'package:appflowy/mobile/presentation/home/tab/_tab_bar.dart'; import 'package:appflowy/mobile/presentation/home/tab/_tab_bar.dart';
import 'package:appflowy/mobile/presentation/home/tab/space_order_bloc.dart'; import 'package:appflowy/mobile/presentation/home/tab/space_order_bloc.dart';
import 'package:appflowy/mobile/presentation/presentation.dart';
import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart';
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class MobileSpaceTab extends StatefulWidget { class MobileSpaceTab extends StatefulWidget {
const MobileSpaceTab({super.key, required this.userProfile}); const MobileSpaceTab({
super.key,
required this.userProfile,
});
final UserProfilePB userProfile; final UserProfilePB userProfile;
@ -22,10 +33,19 @@ class _MobileSpaceTabState extends State<MobileSpaceTab>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
TabController? tabController; TabController? tabController;
@override
void initState() {
super.initState();
mobileCreateNewPageNotifier.addListener(_createNewPage);
}
@override @override
void dispose() { void dispose() {
tabController?.removeListener(_onTabChange); tabController?.removeListener(_onTabChange);
tabController?.dispose(); tabController?.dispose();
mobileCreateNewPageNotifier.removeListener(_createNewPage);
super.dispose(); super.dispose();
} }
@ -33,36 +53,60 @@ class _MobileSpaceTabState extends State<MobileSpaceTab>
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Provider.value( return Provider.value(
value: widget.userProfile, value: widget.userProfile,
child: BlocBuilder<SpaceOrderBloc, SpaceOrderState>( child: MultiBlocListener(
builder: (context, state) { listeners: [
if (state.isLoading) { BlocListener<SpaceBloc, SpaceState>(
return const SizedBox.shrink(); listenWhen: (p, c) =>
} p.lastCreatedPage?.id != c.lastCreatedPage?.id,
listener: (context, state) {
final lastCreatedPage = state.lastCreatedPage;
if (lastCreatedPage != null) {
context.pushView(lastCreatedPage);
}
},
),
BlocListener<SidebarSectionsBloc, SidebarSectionsState>(
listenWhen: (p, c) =>
p.lastCreatedRootView?.id != c.lastCreatedRootView?.id,
listener: (context, state) {
final lastCreatedPage = state.lastCreatedRootView;
if (lastCreatedPage != null) {
context.pushView(lastCreatedPage);
}
},
),
],
child: BlocBuilder<SpaceOrderBloc, SpaceOrderState>(
builder: (context, state) {
if (state.isLoading) {
return const SizedBox.shrink();
}
_initTabController(state); _initTabController(state);
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
MobileSpaceTabBar( MobileSpaceTabBar(
tabController: tabController!, tabController: tabController!,
tabs: state.tabsOrder, tabs: state.tabsOrder,
onReorder: (from, to) { onReorder: (from, to) {
context.read<SpaceOrderBloc>().add( context.read<SpaceOrderBloc>().add(
SpaceOrderEvent.reorder(from, to), SpaceOrderEvent.reorder(from, to),
); );
}, },
),
const HSpace(12.0),
Expanded(
child: TabBarView(
controller: tabController,
children: _buildTabs(state),
), ),
), const HSpace(12.0),
], Expanded(
); child: TabBarView(
}, controller: tabController,
children: _buildTabs(state),
),
),
],
);
},
),
), ),
); );
} }
@ -104,4 +148,27 @@ class _MobileSpaceTabState extends State<MobileSpaceTab>
} }
}).toList(); }).toList();
} }
// quick create new page when clicking the add button in navigation bar
void _createNewPage() {
if (context.read<SpaceBloc>().state.spaces.isNotEmpty) {
context.read<SpaceBloc>().add(
SpaceEvent.createPage(
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
layout: ViewLayoutPB.Document,
),
);
} else {
context.read<SidebarSectionsBloc>().add(
SidebarSectionsEvent.createRootViewInSection(
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
index: 0,
viewSection: FolderSpaceType.public.toViewSectionPB,
),
);
context.read<FolderBloc>().add(
const FolderEvent.expandOrUnExpand(isExpanded: true),
);
}
}
} }

View File

@ -21,7 +21,7 @@ enum BottomNavigationBarActionType {
notificationMultiSelect, notificationMultiSelect,
} }
final PropertyValueNotifier<ViewLayoutPB?> createNewPageNotifier = final PropertyValueNotifier<ViewLayoutPB?> mobileCreateNewPageNotifier =
PropertyValueNotifier(null); PropertyValueNotifier(null);
final ValueNotifier<BottomNavigationBarActionType> bottomNavigationBarType = final ValueNotifier<BottomNavigationBarActionType> bottomNavigationBarType =
ValueNotifier(BottomNavigationBarActionType.home); ValueNotifier(BottomNavigationBarActionType.home);
@ -232,7 +232,8 @@ class _HomePageNavigationBar extends StatelessWidget {
void _onTap(BuildContext context, int bottomBarIndex) { void _onTap(BuildContext context, int bottomBarIndex) {
if (_items[bottomBarIndex].label == _addLabel) { if (_items[bottomBarIndex].label == _addLabel) {
// show an add dialog // show an add dialog
createNewPageNotifier.value = ViewLayoutPB.Document; mobileCreateNewPageNotifier.value = ViewLayoutPB.Document;
return; return;
} }
// When navigating to a new branch, it's recommended to use the goBranch // When navigating to a new branch, it's recommended to use the goBranch