Merge pull request #951 from AppFlowy-IO/feat/create_card_from_header

chore: enable create card from header button
This commit is contained in:
Nathan.fooo 2022-08-31 15:54:10 +08:00 committed by GitHub
commit 0225f0aa4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 115 additions and 46 deletions

View File

@ -69,16 +69,31 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
_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<BoardEvent, BoardState> {
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<BoardEvent, BoardState> {
@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,
});
}

View File

@ -119,8 +119,9 @@ class BoardDataController {
);
}
Future<Either<RowPB, FlowyError>> createBoardCard(String groupId) {
return _gridFFIService.createBoardCard(groupId);
Future<Either<RowPB, FlowyError>> createBoardCard(String groupId,
{String? startRowId}) {
return _gridFFIService.createBoardCard(groupId, startRowId);
}
Future<void> dispose() async {

View File

@ -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);
}

View File

@ -84,11 +84,14 @@ class _BoardContentState extends State<BoardContent> {
() => null,
(editingRow) {
WidgetsBinding.instance.addPostFrameCallback((_) {
scrollManager.scrollToBottom(editingRow.columnId, () {
context
.read<BoardBloc>()
.add(BoardEvent.endEditRow(editingRow.row.id));
});
if (editingRow.index != null) {
} else {
scrollManager.scrollToBottom(editingRow.columnId, () {
context
.read<BoardBloc>()
.add(BoardEvent.endEditRow(editingRow.row.id));
});
}
});
},
);
@ -131,26 +134,32 @@ class _BoardContentState extends State<BoardContent> {
}
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<AppTheme>().textColor,
),
),
// addIcon: const Icon(Icons.add, size: 20),
// moreIcon: SizedBox(
// width: 20,
// height: 20,
// child: svgWidget(
// 'grid/details',
// color: context.read<AppTheme>().iconColor,
// ),
// ),
addIcon: SizedBox(
height: 20,
width: 20,
child: svgWidget(
"home/add",
color: context.read<AppTheme>().iconColor,
),
),
onAddButtonClick: () {
context.read<BoardBloc>().add(
BoardEvent.createHeaderRow(columnData.id),
);
},
height: 50,
margin: config.headerPadding,
);
@ -178,7 +187,9 @@ class _BoardContentState extends State<BoardContent> {
height: 50,
margin: config.footerPadding,
onAddButtonClick: () {
context.read<BoardBloc>().add(BoardEvent.createRow(columnData.id));
context.read<BoardBloc>().add(
BoardEvent.createBottomRow(columnData.id),
);
},
);
}
@ -205,8 +216,13 @@ class _BoardContentState extends State<BoardContent> {
);
final cellBuilder = BoardCellBuilder(cardController);
final isEditing = context.read<BoardBloc>().state.editingRow.isSome();
bool isEditing = false;
context.read<BoardBloc>().state.editingRow.fold(
() => null,
(editingRow) {
isEditing = editingRow.row.id == columnItem.row.id;
},
);
return AppFlowyColumnItemCard(
key: ValueKey(columnItem.id),

View File

@ -27,10 +27,18 @@ class GridFFIService {
return GridEventCreateTableRow(payload).send();
}
Future<Either<RowPB, FlowyError>> createBoardCard(String groupId) {
Future<Either<RowPB, FlowyError>> createBoardCard(
String groupId,
String? startRowId,
) {
CreateBoardCardPayloadPB payload = CreateBoardCardPayloadPB.create()
..gridId = gridId
..groupId = groupId;
if (startRowId != null) {
payload.startRowId = startRowId;
}
return GridEventCreateBoardCard(payload).send();
}

View File

@ -73,17 +73,17 @@ class _MultiBoardListExampleState extends State<MultiBoardListExample> {
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);
},
),

View File

@ -282,14 +282,16 @@ class _AFBoardContentState extends State<AFBoardContent> {
}
Widget? _buildHeader(
BuildContext context, AFBoardColumnHeaderData headerData) {
BuildContext context,
AFBoardColumnData columnData,
) {
if (widget.headerBuilder == null) {
return null;
}
return Selector<AFBoardColumnDataController, AFBoardColumnHeaderData>(
selector: (context, controller) => controller.columnData.headerData,
builder: (context, headerData, _) {
return widget.headerBuilder!(context, headerData)!;
return widget.headerBuilder!(context, columnData)!;
},
);
}

View File

@ -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<AFBoardColumnWidget> {
.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);

View File

@ -14,6 +14,9 @@ pub struct CreateBoardCardPayloadPB {
#[pb(index = 2)]
pub group_id: String,
#[pb(index = 3, one_of)]
pub start_row_id: Option<String>,
}
impl TryInto<CreateRowParams> for CreateBoardCardPayloadPB {
@ -22,9 +25,13 @@ impl TryInto<CreateRowParams> for CreateBoardCardPayloadPB {
fn try_into(self) -> Result<CreateRowParams, Self::Error> {
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,
})

View File

@ -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]);