diff --git a/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart b/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart index 53038c8d42..15db532129 100644 --- a/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart +++ b/frontend/app_flowy/lib/plugins/board/application/board_bloc.dart @@ -69,16 +69,31 @@ class BoardBloc extends Bloc { _startListening(); await _loadGrid(emit); }, - createRow: (groupId) async { + createBottomRow: (groupId) async { + final startRowId = groupControllers[groupId]?.lastRow()?.id; + final result = await _gridDataController.createBoardCard( + groupId, + startRowId: startRowId, + ); + result.fold( + (_) {}, + (err) => Log.error(err), + ); + }, + createHeaderRow: (String groupId) async { final result = await _gridDataController.createBoardCard(groupId); result.fold( (_) {}, (err) => Log.error(err), ); }, - didCreateRow: (String groupId, RowPB row) { + didCreateRow: (String groupId, RowPB row, int? index) { emit(state.copyWith( - editingRow: Some(BoardEditingRow(columnId: groupId, row: row)), + editingRow: Some(BoardEditingRow( + columnId: groupId, + row: row, + index: index, + )), )); }, endEditRow: (rowId) { @@ -142,8 +157,8 @@ class BoardBloc extends Bloc { for (final group in groups) { final delegate = GroupControllerDelegateImpl( controller: boardController, - onNewColumnItem: (groupId, row) { - add(BoardEvent.didCreateRow(groupId, row)); + onNewColumnItem: (groupId, row, index) { + add(BoardEvent.didCreateRow(groupId, row, index)); }, ); final controller = GroupController( @@ -231,9 +246,13 @@ class BoardBloc extends Bloc { @freezed class BoardEvent with _$BoardEvent { const factory BoardEvent.initial() = _InitialBoard; - const factory BoardEvent.createRow(String groupId) = _CreateRow; - const factory BoardEvent.didCreateRow(String groupId, RowPB row) = - _DidCreateRow; + const factory BoardEvent.createBottomRow(String groupId) = _CreateBottomRow; + const factory BoardEvent.createHeaderRow(String groupId) = _CreateHeaderRow; + const factory BoardEvent.didCreateRow( + String groupId, + RowPB row, + int? index, + ) = _DidCreateRow; const factory BoardEvent.endEditRow(String rowId) = _EndEditRow; const factory BoardEvent.didReceiveError(FlowyError error) = _DidReceiveError; const factory BoardEvent.didReceiveGridUpdate( @@ -313,7 +332,7 @@ class BoardColumnItem extends AFColumnItem { class GroupControllerDelegateImpl extends GroupControllerDelegate { final AFBoardDataController controller; - final void Function(String, RowPB) onNewColumnItem; + final void Function(String, RowPB, int?) onNewColumnItem; GroupControllerDelegateImpl({ required this.controller, @@ -351,23 +370,30 @@ class GroupControllerDelegateImpl extends GroupControllerDelegate { } @override - void addNewRow(GroupPB group, RowPB row) { + void addNewRow(GroupPB group, RowPB row, int? index) { final item = BoardColumnItem( row: row, fieldId: group.fieldId, requestFocus: true, ); - controller.addColumnItem(group.groupId, item); - onNewColumnItem(group.groupId, row); + + if (index != null) { + controller.insertColumnItem(group.groupId, index, item); + } else { + controller.addColumnItem(group.groupId, item); + } + onNewColumnItem(group.groupId, row, index); } } class BoardEditingRow { String columnId; RowPB row; + int? index; BoardEditingRow({ required this.columnId, required this.row, + required this.index, }); } diff --git a/frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart b/frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart index 79f53093f1..fdc1179fa1 100644 --- a/frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart +++ b/frontend/app_flowy/lib/plugins/board/application/board_data_controller.dart @@ -118,8 +118,9 @@ class BoardDataController { ); } - Future> createBoardCard(String groupId) { - return _gridFFIService.createBoardCard(groupId); + Future> createBoardCard(String groupId, + {String? startRowId}) { + return _gridFFIService.createBoardCard(groupId, startRowId); } Future dispose() async { diff --git a/frontend/app_flowy/lib/plugins/board/application/group_controller.dart b/frontend/app_flowy/lib/plugins/board/application/group_controller.dart index 9407ae360b..6a148c0312 100644 --- a/frontend/app_flowy/lib/plugins/board/application/group_controller.dart +++ b/frontend/app_flowy/lib/plugins/board/application/group_controller.dart @@ -9,7 +9,7 @@ abstract class GroupControllerDelegate { void removeRow(GroupPB group, String rowId); void insertRow(GroupPB group, RowPB row, int? index); void updateRow(GroupPB group, RowPB row); - void addNewRow(GroupPB group, RowPB row); + void addNewRow(GroupPB group, RowPB row, int? index); } class GroupController { @@ -31,6 +31,11 @@ class GroupController { } } + RowPB? lastRow() { + if (group.rows.isEmpty) return null; + return group.rows.last; + } + void startListening() { _listener.start(onGroupChanged: (result) { result.fold( @@ -50,7 +55,7 @@ class GroupController { } if (insertedRow.isNew) { - delegate.addNewRow(group, insertedRow.row); + delegate.addNewRow(group, insertedRow.row, index); } else { delegate.insertRow(group, insertedRow.row, index); } diff --git a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart index e850186f89..bc8a4fcfa7 100644 --- a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart +++ b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart @@ -84,11 +84,14 @@ class _BoardContentState extends State { () => null, (editingRow) { WidgetsBinding.instance.addPostFrameCallback((_) { - scrollManager.scrollToBottom(editingRow.columnId, () { - context - .read() - .add(BoardEvent.endEditRow(editingRow.row.id)); - }); + if (editingRow.index != null) { + } else { + scrollManager.scrollToBottom(editingRow.columnId, () { + context + .read() + .add(BoardEvent.endEditRow(editingRow.row.id)); + }); + } }); }, ); @@ -131,26 +134,32 @@ class _BoardContentState extends State { } Widget _buildHeader( - BuildContext context, AFBoardColumnHeaderData headerData) { + BuildContext context, + AFBoardColumnData columnData, + ) { return AppFlowyColumnHeader( title: Flexible( fit: FlexFit.tight, child: FlowyText.medium( - headerData.columnName, + columnData.headerData.columnName, fontSize: 14, overflow: TextOverflow.clip, color: context.read().textColor, ), ), - // addIcon: const Icon(Icons.add, size: 20), - // moreIcon: SizedBox( - // width: 20, - // height: 20, - // child: svgWidget( - // 'grid/details', - // color: context.read().iconColor, - // ), - // ), + addIcon: SizedBox( + height: 20, + width: 20, + child: svgWidget( + "home/add", + color: context.read().iconColor, + ), + ), + onAddButtonClick: () { + context.read().add( + BoardEvent.createHeaderRow(columnData.id), + ); + }, height: 50, margin: config.headerPadding, ); @@ -178,7 +187,9 @@ class _BoardContentState extends State { height: 50, margin: config.footerPadding, onAddButtonClick: () { - context.read().add(BoardEvent.createRow(columnData.id)); + context.read().add( + BoardEvent.createBottomRow(columnData.id), + ); }, ); } @@ -205,8 +216,13 @@ class _BoardContentState extends State { ); final cellBuilder = BoardCellBuilder(cardController); - - final isEditing = context.read().state.editingRow.isSome(); + bool isEditing = false; + context.read().state.editingRow.fold( + () => null, + (editingRow) { + isEditing = editingRow.row.id == columnItem.row.id; + }, + ); return AppFlowyColumnItemCard( key: ValueKey(columnItem.id), diff --git a/frontend/app_flowy/lib/plugins/grid/application/grid_service.dart b/frontend/app_flowy/lib/plugins/grid/application/grid_service.dart index c8b6873d91..40dd5eeda1 100644 --- a/frontend/app_flowy/lib/plugins/grid/application/grid_service.dart +++ b/frontend/app_flowy/lib/plugins/grid/application/grid_service.dart @@ -27,10 +27,18 @@ class GridFFIService { return GridEventCreateTableRow(payload).send(); } - Future> createBoardCard(String groupId) { + Future> createBoardCard( + String groupId, + String? startRowId, + ) { CreateBoardCardPayloadPB payload = CreateBoardCardPayloadPB.create() ..gridId = gridId ..groupId = groupId; + + if (startRowId != null) { + payload.startRowId = startRowId; + } + return GridEventCreateBoardCard(payload).send(); } diff --git a/frontend/app_flowy/packages/appflowy_board/example/lib/multi_board_list_example.dart b/frontend/app_flowy/packages/appflowy_board/example/lib/multi_board_list_example.dart index 7b9a84b6d2..b69fb1dbf3 100644 --- a/frontend/app_flowy/packages/appflowy_board/example/lib/multi_board_list_example.dart +++ b/frontend/app_flowy/packages/appflowy_board/example/lib/multi_board_list_example.dart @@ -73,17 +73,17 @@ class _MultiBoardListExampleState extends State { margin: config.columnItemPadding, ); }, - headerBuilder: (context, headerData) { + headerBuilder: (context, columnData) { return AppFlowyColumnHeader( icon: const Icon(Icons.lightbulb_circle), title: SizedBox( width: 60, child: TextField( controller: TextEditingController() - ..text = headerData.columnName, + ..text = columnData.headerData.columnName, onSubmitted: (val) { boardDataController - .getColumnController(headerData.columnId)! + .getColumnController(columnData.headerData.columnId)! .updateColumnName(val); }, ), diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board.dart index 3360325e8e..5b44ec64b7 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board.dart @@ -282,14 +282,16 @@ class _AFBoardContentState extends State { } Widget? _buildHeader( - BuildContext context, AFBoardColumnHeaderData headerData) { + BuildContext context, + AFBoardColumnData columnData, + ) { if (widget.headerBuilder == null) { return null; } return Selector( selector: (context, controller) => controller.columnData.headerData, builder: (context, headerData, _) { - return widget.headerBuilder!(context, headerData)!; + return widget.headerBuilder!(context, columnData)!; }, ); } diff --git a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column.dart b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column.dart index 2065d88ba2..79fe534941 100644 --- a/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column.dart +++ b/frontend/app_flowy/packages/appflowy_board/lib/src/widgets/board_column/board_column.dart @@ -31,7 +31,7 @@ typedef AFBoardColumnCardBuilder = Widget Function( typedef AFBoardColumnHeaderBuilder = Widget? Function( BuildContext context, - AFBoardColumnHeaderData headerData, + AFBoardColumnData headerData, ); typedef AFBoardColumnFooterBuilder = Widget Function( @@ -132,8 +132,8 @@ class _AFBoardColumnWidgetState extends State { .map((item) => _buildWidget(context, item)) .toList(); - final header = widget.headerBuilder - ?.call(context, widget.dataSource.columnData.headerData); + final header = + widget.headerBuilder?.call(context, widget.dataSource.columnData); final footer = widget.footBuilder?.call(context, widget.dataSource.columnData); diff --git a/frontend/rust-lib/flowy-grid/src/entities/group_entities/group.rs b/frontend/rust-lib/flowy-grid/src/entities/group_entities/group.rs index 75b133344f..002cb73c6d 100644 --- a/frontend/rust-lib/flowy-grid/src/entities/group_entities/group.rs +++ b/frontend/rust-lib/flowy-grid/src/entities/group_entities/group.rs @@ -14,6 +14,9 @@ pub struct CreateBoardCardPayloadPB { #[pb(index = 2)] pub group_id: String, + + #[pb(index = 3, one_of)] + pub start_row_id: Option, } impl TryInto for CreateBoardCardPayloadPB { @@ -22,9 +25,13 @@ impl TryInto for CreateBoardCardPayloadPB { fn try_into(self) -> Result { let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?; let group_id = NotEmptyStr::parse(self.group_id).map_err(|_| ErrorCode::GroupIdIsEmpty)?; + let start_row_id = match self.start_row_id { + None => None, + Some(start_row_id) => Some(NotEmptyStr::parse(start_row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?.0), + }; Ok(CreateRowParams { grid_id: grid_id.0, - start_row_id: None, + start_row_id, group_id: Some(group_id.0), layout: GridLayout::Board, }) diff --git a/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs b/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs index 013f59cd42..688a844707 100644 --- a/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs +++ b/frontend/rust-lib/flowy-grid/src/services/grid_view_editor.rs @@ -95,9 +95,13 @@ impl GridViewRevisionEditor { match params.group_id.as_ref() { None => {} Some(group_id) => { + let index = match params.start_row_id { + None => Some(0), + Some(_) => None, + }; let inserted_row = InsertedRowPB { row: row_pb.clone(), - index: None, + index, is_new: true, }; let changeset = GroupChangesetPB::insert(group_id.clone(), vec![inserted_row]);