diff --git a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_database_test.dart b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_database_test.dart index 915004133fbf..f401cb1e0be1 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/document/document_with_database_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/document/document_with_database_test.dart @@ -56,7 +56,7 @@ void main() { expect( find.descendant( of: find.byType(AppFlowyEditor), - matching: find.byType(BoardPage), + matching: find.byType(DesktopBoardPage), ), findsOneWidget, ); @@ -104,7 +104,7 @@ void main() { expect( find.descendant( of: find.byType(AppFlowyEditor), - matching: find.byType(BoardPage), + matching: find.byType(DesktopBoardPage), ), findsOneWidget, ); diff --git a/frontend/appflowy_flutter/integration_test/desktop/sidebar/sidebar_test.dart b/frontend/appflowy_flutter/integration_test/desktop/sidebar/sidebar_test.dart index d6e5431527da..158a8db88212 100644 --- a/frontend/appflowy_flutter/integration_test/desktop/sidebar/sidebar_test.dart +++ b/frontend/appflowy_flutter/integration_test/desktop/sidebar/sidebar_test.dart @@ -61,7 +61,7 @@ void main() { expect(find.byType(GridPage), findsOneWidget); break; case ViewLayoutPB.Board: - expect(find.byType(BoardPage), findsOneWidget); + expect(find.byType(DesktopBoardPage), findsOneWidget); break; case ViewLayoutPB.Calendar: expect(find.byType(CalendarPage), findsOneWidget); diff --git a/frontend/appflowy_flutter/integration_test/shared/database_test_op.dart b/frontend/appflowy_flutter/integration_test/shared/database_test_op.dart index 6b5b0cc1cf41..8697b832c0a8 100644 --- a/frontend/appflowy_flutter/integration_test/shared/database_test_op.dart +++ b/frontend/appflowy_flutter/integration_test/shared/database_test_op.dart @@ -1463,7 +1463,7 @@ extension AppFlowyDatabaseTest on WidgetTester { void assertCurrentDatabaseTagIs(DatabaseLayoutPB layout) => switch (layout) { DatabaseLayoutPB.Board => - expect(find.byType(BoardPage), findsOneWidget), + expect(find.byType(DesktopBoardPage), findsOneWidget), DatabaseLayoutPB.Calendar => expect(find.byType(CalendarPage), findsOneWidget), DatabaseLayoutPB.Grid => expect(find.byType(GridPage), findsOneWidget), @@ -1521,7 +1521,7 @@ extension AppFlowyDatabaseTest on WidgetTester { } Finder finderForDatabaseLayoutType(DatabaseLayoutPB layout) => switch (layout) { - DatabaseLayoutPB.Board => find.byType(BoardPage), + DatabaseLayoutPB.Board => find.byType(DesktopBoardPage), DatabaseLayoutPB.Calendar => find.byType(CalendarPage), DatabaseLayoutPB.Grid => find.byType(GridPage), _ => throw Exception('Unknown database layout type: $layout'), diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/board/board.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/board/board.dart index 89ae2411e18c..642a7ebeae72 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/board/board.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/board/board.dart @@ -1,4 +1,4 @@ export 'mobile_board_screen.dart'; -export 'mobile_board_content.dart'; +export 'mobile_board_page.dart'; export 'widgets/mobile_hidden_groups_column.dart'; export 'widgets/mobile_board_trailing.dart'; diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/board/mobile_board_content.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/board/mobile_board_page.dart similarity index 82% rename from frontend/appflowy_flutter/lib/mobile/presentation/database/board/mobile_board_content.dart rename to frontend/appflowy_flutter/lib/mobile/presentation/database/board/mobile_board_page.dart index c557167bdbb6..2c3b7c83c0ba 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/board/mobile_board_content.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/board/mobile_board_page.dart @@ -3,12 +3,15 @@ import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/mobile/presentation/database/board/board.dart'; import 'package:appflowy/mobile/presentation/database/board/widgets/group_card_header.dart'; import 'package:appflowy/mobile/presentation/database/card/card.dart'; +import 'package:appflowy/mobile/presentation/widgets/widgets.dart'; +import 'package:appflowy/plugins/database/application/database_controller.dart'; import 'package:appflowy/plugins/database/board/application/board_bloc.dart'; import 'package:appflowy/plugins/database/grid/presentation/widgets/header/field_type_extension.dart'; import 'package:appflowy/plugins/database/widgets/card/card.dart'; import 'package:appflowy/plugins/database/widgets/cell/card_cell_builder.dart'; import 'package:appflowy/plugins/database/widgets/cell/card_cell_style_maps/mobile_board_card_cell_style.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart'; +import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart'; import 'package:appflowy_board/appflowy_board.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; @@ -16,25 +19,65 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; -class MobileBoardContent extends StatefulWidget { - const MobileBoardContent({ +class MobileBoardPage extends StatefulWidget { + const MobileBoardPage({ super.key, + required this.view, + required this.databaseController, + this.onEditStateChanged, }); + final ViewPB view; + + final DatabaseController databaseController; + + /// Called when edit state changed + final VoidCallback? onEditStateChanged; + + @override + State createState() => _MobileBoardPageState(); +} + +class _MobileBoardPageState extends State { + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (_) => BoardBloc( + databaseController: widget.databaseController, + )..add(const BoardEvent.initial()), + child: BlocBuilder( + buildWhen: (p, c) => c.isReady, + builder: (context, state) => state.maybeMap( + loading: (_) => const Center( + child: CircularProgressIndicator.adaptive(), + ), + error: (err) => FlowyMobileStateContainer.error( + emoji: '🛸', + title: LocaleKeys.board_mobile_failedToLoad.tr(), + errorMsg: err.toString(), + ), + ready: (data) => const _BoardContent(), + orElse: () => const SizedBox.shrink(), + ), + ), + ); + } +} + +class _BoardContent extends StatefulWidget { + const _BoardContent(); + @override - State createState() => _MobileBoardContentState(); + State<_BoardContent> createState() => _BoardContentState(); } -class _MobileBoardContentState extends State { +class _BoardContentState extends State<_BoardContent> { late final ScrollController scrollController; late final AppFlowyBoardScrollController scrollManager; @override void initState() { super.initState(); - // mobile may not need this - // scroll to bottom when add a new card - scrollManager = AppFlowyBoardScrollController(); scrollController = ScrollController(); } 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 800b9bbe369c..307c90f38629 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 @@ -31,54 +31,60 @@ const bool _kOpenRowsAfterCreation = false; class BoardBloc extends Bloc { BoardBloc({ required this.databaseController, + AppFlowyBoardController? boardController, }) : super(const BoardState.loading()) { groupBackendSvc = GroupBackendService(viewId); - boardController = AppFlowyBoardController( - onMoveGroup: (fromGroupId, fromIndex, toGroupId, toIndex) => - databaseController.moveGroup( - fromGroupId: fromGroupId, - toGroupId: toGroupId, - ), - onMoveGroupItem: (groupId, fromIndex, toIndex) { - final fromRow = groupControllers[groupId]?.rowAtIndex(fromIndex); - final toRow = groupControllers[groupId]?.rowAtIndex(toIndex); - if (fromRow != null) { - databaseController.moveGroupRow( - fromRow: fromRow, - toRow: toRow, - fromGroupId: groupId, - toGroupId: groupId, - ); - } - }, - onMoveGroupItemToGroup: (fromGroupId, fromIndex, toGroupId, toIndex) { - final fromRow = groupControllers[fromGroupId]?.rowAtIndex(fromIndex); - final toRow = groupControllers[toGroupId]?.rowAtIndex(toIndex); - if (fromRow != null) { - databaseController.moveGroupRow( - fromRow: fromRow, - toRow: toRow, - fromGroupId: fromGroupId, - toGroupId: toGroupId, - ); - } - }, - ); - + _initBoardController(boardController); _dispatch(); } final DatabaseController databaseController; + late final AppFlowyBoardController boardController; final LinkedHashMap groupControllers = LinkedHashMap(); final List groupList = []; - late final AppFlowyBoardController boardController; late final GroupBackendService groupBackendSvc; FieldController get fieldController => databaseController.fieldController; String get viewId => databaseController.viewId; + void _initBoardController(AppFlowyBoardController? controller) { + boardController = controller ?? + AppFlowyBoardController( + onMoveGroup: (fromGroupId, fromIndex, toGroupId, toIndex) => + databaseController.moveGroup( + fromGroupId: fromGroupId, + toGroupId: toGroupId, + ), + onMoveGroupItem: (groupId, fromIndex, toIndex) { + final fromRow = groupControllers[groupId]?.rowAtIndex(fromIndex); + final toRow = groupControllers[groupId]?.rowAtIndex(toIndex); + if (fromRow != null) { + databaseController.moveGroupRow( + fromRow: fromRow, + toRow: toRow, + fromGroupId: groupId, + toGroupId: groupId, + ); + } + }, + onMoveGroupItemToGroup: (fromGroupId, fromIndex, toGroupId, toIndex) { + final fromRow = + groupControllers[fromGroupId]?.rowAtIndex(fromIndex); + final toRow = groupControllers[toGroupId]?.rowAtIndex(toIndex); + if (fromRow != null) { + databaseController.moveGroupRow( + fromRow: fromRow, + toRow: toRow, + fromGroupId: fromGroupId, + toGroupId: toGroupId, + ); + } + }, + ); + } + void _dispatch() { on( (event, emit) async { @@ -367,6 +373,7 @@ class BoardBloc extends Bloc { for (final controller in groupControllers.values) { await controller.dispose(); } + boardController.dispose(); return super.close(); } 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 f3aba78a0d24..d01b85c617ce 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 @@ -1,7 +1,6 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; -import 'package:appflowy/mobile/presentation/database/board/mobile_board_content.dart'; -import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_state_container.dart'; +import 'package:appflowy/mobile/presentation/database/board/mobile_board_page.dart'; import 'package:appflowy/plugins/database/application/database_controller.dart'; import 'package:appflowy/plugins/database/application/row/row_controller.dart'; import 'package:appflowy/plugins/database/board/presentation/widgets/board_column_header.dart'; @@ -45,11 +44,17 @@ class BoardPageTabBarBuilderImpl extends DatabaseTabBarItemBuilder { bool shrinkWrap, String? initialRowId, ) => - BoardPage( - key: _makeValueKey(controller), - view: view, - databaseController: controller, - ); + PlatformExtension.isDesktop + ? DesktopBoardPage( + key: _makeValueKey(controller), + view: view, + databaseController: controller, + ) + : MobileBoardPage( + key: _makeValueKey(controller), + view: view, + databaseController: controller, + ); @override Widget settingBar(BuildContext context, DatabaseController controller) => @@ -82,8 +87,8 @@ class BoardPageTabBarBuilderImpl extends DatabaseTabBarItemBuilder { ValueKey(controller.viewId); } -class BoardPage extends StatelessWidget { - const BoardPage({ +class DesktopBoardPage extends StatefulWidget { + const DesktopBoardPage({ super.key, required this.view, required this.databaseController, @@ -97,31 +102,88 @@ class BoardPage extends StatelessWidget { /// Called when edit state changed final VoidCallback? onEditStateChanged; + @override + State createState() => _DesktopBoardPageState(); +} + +class _DesktopBoardPageState extends State { + late final AppFlowyBoardController _boardController = AppFlowyBoardController( + onMoveGroup: (fromGroupId, fromIndex, toGroupId, toIndex) => + widget.databaseController.moveGroup( + fromGroupId: fromGroupId, + toGroupId: toGroupId, + ), + onMoveGroupItem: (groupId, fromIndex, toIndex) { + final groupControllers = bloc.groupControllers; + final fromRow = groupControllers[groupId]?.rowAtIndex(fromIndex); + final toRow = groupControllers[groupId]?.rowAtIndex(toIndex); + if (fromRow != null) { + widget.databaseController.moveGroupRow( + fromRow: fromRow, + toRow: toRow, + fromGroupId: groupId, + toGroupId: groupId, + ); + } + }, + onMoveGroupItemToGroup: (fromGroupId, fromIndex, toGroupId, toIndex) { + final groupControllers = bloc.groupControllers; + final fromRow = groupControllers[fromGroupId]?.rowAtIndex(fromIndex); + final toRow = groupControllers[toGroupId]?.rowAtIndex(toIndex); + if (fromRow != null) { + widget.databaseController.moveGroupRow( + fromRow: fromRow, + toRow: toRow, + fromGroupId: fromGroupId, + toGroupId: toGroupId, + ); + } + }, + onStartDraggingCard: (groupId, index) { + final groupControllers = bloc.groupControllers; + final toRow = groupControllers[groupId]?.rowAtIndex(index); + if (toRow != null) { + _focusScope.clear(); + } + }, + ); + + late final _focusScope = BoardFocusScope( + boardController: _boardController, + ); + + late final BoardBloc bloc = BoardBloc( + databaseController: widget.databaseController, + boardController: _boardController, + )..add(const BoardEvent.initial()); + + @override + void dispose() { + _focusScope.dispose(); + _boardController.dispose(); + bloc.close(); + super.dispose(); + } + @override Widget build(BuildContext context) { - return BlocProvider( - create: (_) => BoardBloc( - databaseController: databaseController, - )..add(const BoardEvent.initial()), + return BlocProvider.value( + value: bloc, child: BlocBuilder( buildWhen: (p, c) => c.isReady, builder: (context, state) => state.maybeMap( loading: (_) => const Center( child: CircularProgressIndicator.adaptive(), ), - error: (err) => PlatformExtension.isMobile - ? FlowyMobileStateContainer.error( - emoji: '🛸', - title: LocaleKeys.board_mobile_failedToLoad.tr(), - errorMsg: err.toString(), - ) - : FlowyErrorPage.message( - err.toString(), - howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(), - ), - ready: (data) => PlatformExtension.isMobile - ? const MobileBoardContent() - : DesktopBoardContent(onEditStateChanged: onEditStateChanged), + error: (err) => FlowyErrorPage.message( + err.toString(), + howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(), + ), + ready: (data) => _BoardContent( + onEditStateChanged: widget.onEditStateChanged, + focusScope: _focusScope, + boardController: _boardController, + ), orElse: () => const SizedBox.shrink(), ), ), @@ -129,19 +191,22 @@ class BoardPage extends StatelessWidget { } } -class DesktopBoardContent extends StatefulWidget { - const DesktopBoardContent({ - super.key, +class _BoardContent extends StatefulWidget { + const _BoardContent({ + required this.boardController, + required this.focusScope, this.onEditStateChanged, }); + final AppFlowyBoardController boardController; + final BoardFocusScope focusScope; final VoidCallback? onEditStateChanged; @override - State createState() => _DesktopBoardContentState(); + State<_BoardContent> createState() => _BoardContentState(); } -class _DesktopBoardContentState extends State { +class _BoardContentState extends State<_BoardContent> { final ScrollController scrollController = ScrollController(); final AppFlowyBoardScrollController scrollManager = AppFlowyBoardScrollController(); @@ -156,16 +221,14 @@ class _DesktopBoardContentState extends State { ); late final cellBuilder = CardCellBuilder( - databaseController: context.read().databaseController, + databaseController: databaseController, ); - late final _focusScope = BoardFocusScope( - boardController: context.read().boardController, - ); + DatabaseController get databaseController => + context.read().databaseController; @override void dispose() { - _focusScope.dispose(); scrollController.dispose(); super.dispose(); } @@ -202,7 +265,7 @@ class _DesktopBoardContentState extends State { state.maybeWhen( orElse: () {}, setFocus: (groupedRowIds) { - _focusScope.focusedGroupedRows = groupedRowIds; + widget.focusScope.focusedGroupedRows = groupedRowIds; }, ); }, @@ -217,7 +280,7 @@ class _DesktopBoardContentState extends State { return FocusScope( autofocus: true, child: BoardShortcutContainer( - focusScope: _focusScope, + focusScope: widget.focusScope, child: Padding( padding: const EdgeInsets.only(top: 8.0), child: AppFlowyBoard( @@ -254,7 +317,7 @@ class _DesktopBoardContentState extends State { afGroupData: column, groupItem: columnItem as GroupItem, boardConfig: config, - notifier: _focusScope, + notifier: widget.focusScope, cellBuilder: cellBuilder, ), ), diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index d322aac65409..139cb7b6ef32 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -44,8 +44,8 @@ packages: dependency: "direct main" description: path: "." - ref: "404262fca4369bc35ff305316e4d59341a732f56" - resolved-ref: "404262fca4369bc35ff305316e4d59341a732f56" + ref: "10ce6cde302b706eb60d8ff7636181c9f81fc15a" + resolved-ref: "10ce6cde302b706eb60d8ff7636181c9f81fc15a" url: "https://github.com/AppFlowy-IO/appflowy-board.git" source: git version: "0.1.2" diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index 56223b4d48bf..4282e7011260 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -44,7 +44,7 @@ dependencies: # path: ../../../appflowy-board git: url: https://github.com/AppFlowy-IO/appflowy-board.git - ref: 404262fca4369bc35ff305316e4d59341a732f56 + ref: 10ce6cde302b706eb60d8ff7636181c9f81fc15a appflowy_result: path: packages/appflowy_result appflowy_editor_plugins: ^0.0.2