diff --git a/frontend/app_flowy/lib/startup/deps_resolver.dart b/frontend/app_flowy/lib/startup/deps_resolver.dart index 2b37642f39..a0af57f580 100644 --- a/frontend/app_flowy/lib/startup/deps_resolver.dart +++ b/frontend/app_flowy/lib/startup/deps_resolver.dart @@ -194,7 +194,6 @@ void _resolveGridDeps(GetIt getIt) { getIt.registerFactoryParam( (cellData, _) => NumberCellBloc( - service: CellService(), cellData: cellData, ), ); diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell_bloc/checkbox_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell_bloc/checkbox_cell_bloc.dart index ae69176fd9..a2b888a295 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell_bloc/checkbox_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell_bloc/checkbox_cell_bloc.dart @@ -26,7 +26,7 @@ class CheckboxCellBloc extends Bloc { _startListening(); }, select: (_Selected value) async { - service.updateCell( + _service.updateCell( gridId: state.cellData.gridId, fieldId: state.cellData.field.id, rowId: state.cellData.rowId, @@ -43,6 +43,7 @@ class CheckboxCellBloc extends Bloc { @override Future close() async { + await _listener.stop(); return super.close(); } diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell_bloc/number_cell_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/cell_bloc/number_cell_bloc.dart index 83d2eff57d..fe5c6befeb 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell_bloc/number_cell_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell_bloc/number_cell_bloc.dart @@ -1,4 +1,6 @@ +import 'package:app_flowy/workspace/application/grid/cell_bloc/cell_listener.dart'; import 'package:app_flowy/workspace/application/grid/row/row_service.dart'; +import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -8,37 +10,87 @@ import 'cell_service.dart'; part 'number_cell_bloc.freezed.dart'; class NumberCellBloc extends Bloc { - final CellService service; + final CellService _service; + final CellListener _listener; NumberCellBloc({ - required this.service, required CellData cellData, - }) : super(NumberCellState.initial()) { + }) : _service = CellService(), + _listener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id), + super(NumberCellState.initial(cellData)) { on( (event, emit) async { await event.map( - initial: (_InitialCell value) async {}, + initial: (_Initial value) async { + _startListening(); + }, + didReceiveCellUpdate: (_DidReceiveCellUpdate value) { + emit(state.copyWith(content: value.cell.content)); + }, + updateCell: (_UpdateCell value) { + _updateCellValue(value, emit); + }, ); }, ); } + void _updateCellValue(_UpdateCell value, Emitter emit) { + final number = num.tryParse(value.text); + if (number == null) { + emit(state.copyWith(content: "")); + } else { + _service.updateCell( + gridId: state.cellData.gridId, + fieldId: state.cellData.field.id, + rowId: state.cellData.rowId, + data: value.text, + ); + } + } + @override Future close() async { + await _listener.stop(); return super.close(); } + + void _startListening() { + _listener.updateCellNotifier.addPublishListener((result) { + result.fold( + (notificationData) async { + final result = await _service.getCell( + gridId: state.cellData.gridId, + fieldId: state.cellData.field.id, + rowId: state.cellData.rowId, + ); + result.fold( + (cell) => add(NumberCellEvent.didReceiveCellUpdate(cell)), + (err) => Log.error(err), + ); + }, + (err) => Log.error(err), + ); + }); + _listener.start(); + } } @freezed class NumberCellEvent with _$NumberCellEvent { - const factory NumberCellEvent.initial() = _InitialCell; + const factory NumberCellEvent.initial() = _Initial; + const factory NumberCellEvent.updateCell(String text) = _UpdateCell; + const factory NumberCellEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate; } @freezed class NumberCellState with _$NumberCellState { const factory NumberCellState({ - Cell? cell, + required CellData cellData, + required String content, }) = _NumberCellState; - factory NumberCellState.initial() => const NumberCellState(); + factory NumberCellState.initial(CellData cellData) { + return NumberCellState(cellData: cellData, content: cellData.cell?.content ?? ""); + } } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/number_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/number_cell.dart index 41b8cf6ee5..ba9d75ed1b 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/number_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/number_cell.dart @@ -1,5 +1,8 @@ +import 'dart:async'; + import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/workspace/application/grid/prelude.dart'; +import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/cell_container.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -17,20 +20,44 @@ class NumberCell extends StatefulWidget { class _NumberCellState extends State { late NumberCellBloc _cellBloc; + late TextEditingController _controller; + late CellFocusNode _focusNode; + Timer? _delayOperation; @override void initState() { _cellBloc = getIt(param1: widget.cellData); + _controller = TextEditingController(text: _cellBloc.state.content); + _focusNode = CellFocusNode(); super.initState(); } @override Widget build(BuildContext context) { + _focusNode.addCallback(context, focusChanged); + return BlocProvider.value( value: _cellBloc, - child: BlocBuilder( + child: BlocConsumer( + listener: (context, state) { + if (_controller.text != state.content) { + _controller.text = state.content; + } + }, builder: (context, state) { - return Container(); + return TextField( + controller: _controller, + focusNode: _focusNode, + onChanged: (value) => focusChanged(), + onEditingComplete: () => _focusNode.unfocus(), + maxLines: 1, + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500), + decoration: const InputDecoration( + contentPadding: EdgeInsets.zero, + border: InputBorder.none, + isDense: true, + ), + ); }, ), ); @@ -38,7 +65,20 @@ class _NumberCellState extends State { @override Future dispose() async { + _delayOperation?.cancel(); _cellBloc.close(); + _focusNode.dispose(); super.dispose(); } + + Future focusChanged() async { + if (mounted) { + _delayOperation?.cancel(); + _delayOperation = Timer(const Duration(milliseconds: 300), () { + if (_cellBloc.isClosed == false && _controller.text != _cellBloc.state.content) { + _cellBloc.add(NumberCellEvent.updateCell(_controller.text)); + } + }); + } + } } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/text_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/text_cell.dart index c7b4219bab..543a337b9b 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/text_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/text_cell.dart @@ -63,6 +63,7 @@ class _GridTextCellState extends State { @override Future dispose() async { + _delayOperation?.cancel(); _cellBloc.close(); _focusNode.dispose(); super.dispose(); @@ -72,7 +73,7 @@ class _GridTextCellState extends State { if (mounted) { _delayOperation?.cancel(); _delayOperation = Timer(const Duration(milliseconds: 300), () { - if (_cellBloc.isClosed == false) { + if (_cellBloc.isClosed == false && _controller.text != _cellBloc.state.content) { _cellBloc.add(TextCellEvent.updateText(_controller.text)); } }); diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_type_list.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_type_list.dart index 2965556f60..dfeacf4d17 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_type_list.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_type_list.dart @@ -19,7 +19,7 @@ class FieldTypeList extends StatelessWidget with FlowyOverlayDelegate { @override Widget build(BuildContext context) { - final cells = FieldType.values.map((fieldType) { + final cells = FieldType.values.where((ty) => ty != FieldType.DateTime).map((fieldType) { return FieldTypeCell( fieldType: fieldType, onSelectField: (fieldType) {