diff --git a/frontend/.vscode/settings.json b/frontend/.vscode/settings.json index 93ea743c8a..f7d0c23058 100644 --- a/frontend/.vscode/settings.json +++ b/frontend/.vscode/settings.json @@ -24,7 +24,6 @@ "svgviewer.showzoominout": true, "editor.wordWrapColumn": 80, "editor.minimap.maxColumn": 140, - "prettier.printWidth": 140, "editor.wordWrap": "wordWrapColumn", "dart.lineLength": 80, "typescript.validate.enable": true, diff --git a/frontend/appflowy_flutter/assets/images/Local Disk (C) - Shortcut.lnk b/frontend/appflowy_flutter/assets/images/Local Disk (C) - Shortcut.lnk new file mode 100644 index 0000000000..52816016db Binary files /dev/null and b/frontend/appflowy_flutter/assets/images/Local Disk (C) - Shortcut.lnk differ diff --git a/frontend/appflowy_flutter/assets/images/app_flowy_abstract_cover_1.jpg b/frontend/appflowy_flutter/assets/images/app_flowy_abstract_cover_1.jpg new file mode 100644 index 0000000000..ebfbc367a6 Binary files /dev/null and b/frontend/appflowy_flutter/assets/images/app_flowy_abstract_cover_1.jpg differ diff --git a/frontend/appflowy_flutter/assets/images/app_flowy_abstract_cover_2.jpg b/frontend/appflowy_flutter/assets/images/app_flowy_abstract_cover_2.jpg new file mode 100644 index 0000000000..aeaa6a0f29 Binary files /dev/null and b/frontend/appflowy_flutter/assets/images/app_flowy_abstract_cover_2.jpg differ diff --git a/frontend/appflowy_flutter/assets/translations/en.json b/frontend/appflowy_flutter/assets/translations/en.json index 576085d450..c77f4c5e92 100644 --- a/frontend/appflowy_flutter/assets/translations/en.json +++ b/frontend/appflowy_flutter/assets/translations/en.json @@ -353,7 +353,15 @@ "smartEditFixSpelling": "Fix spelling", "smartEditSummarize": "Summarize", "smartEditCouldNotFetchResult": "Could not fetch result from OpenAI", - "smartEditCouldNotFetchKey": "Could not fetch OpenAI key" + "smartEditCouldNotFetchKey": "Could not fetch OpenAI key", + "cover": { + "changeCover": "Change Cover", + "colors": "Colors", + "images": "Images", + "abstract": "Abstract", + "addCover": "Add Cover", + "addLocalImage": "Add local image" + } } }, "board": { @@ -371,4 +379,4 @@ "nextMonth": "Next Month" } } -} \ No newline at end of file +} diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_controller.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_controller.dart index bc7d6e83f0..a5e036b677 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_controller.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_controller.dart @@ -1,118 +1,16 @@ -part of 'cell_service.dart'; - -typedef TextCellController = CellController; -typedef CheckboxCellController = CellController; -typedef NumberCellController = CellController; -typedef SelectOptionCellController - = CellController; -typedef ChecklistCellController - = CellController; -typedef DateCellController = CellController; -typedef URLCellController = CellController; - -abstract class CellControllerBuilderDelegate { - CellFieldNotifier buildFieldNotifier(); -} - -class CellControllerBuilder { - final CellIdentifier _cellId; - final CellCache _cellCache; - final CellControllerBuilderDelegate delegate; - - CellControllerBuilder({ - required this.delegate, - required CellIdentifier cellId, - required CellCache cellCache, - }) : _cellCache = cellCache, - _cellId = cellId; - - CellController build() { - final cellFieldNotifier = delegate.buildFieldNotifier(); - switch (_cellId.fieldType) { - case FieldType.Checkbox: - final cellDataLoader = CellDataLoader( - cellId: _cellId, - parser: StringCellDataParser(), - ); - return TextCellController( - cellId: _cellId, - cellCache: _cellCache, - cellDataLoader: cellDataLoader, - fieldNotifier: cellFieldNotifier, - cellDataPersistence: TextCellDataPersistence(cellId: _cellId), - ); - case FieldType.DateTime: - final cellDataLoader = CellDataLoader( - cellId: _cellId, - parser: DateCellDataParser(), - reloadOnFieldChanged: true, - ); - - return DateCellController( - cellId: _cellId, - cellCache: _cellCache, - cellDataLoader: cellDataLoader, - fieldNotifier: cellFieldNotifier, - cellDataPersistence: DateCellDataPersistence(cellId: _cellId), - ); - case FieldType.Number: - final cellDataLoader = CellDataLoader( - cellId: _cellId, - parser: StringCellDataParser(), - reloadOnFieldChanged: true, - ); - return NumberCellController( - cellId: _cellId, - cellCache: _cellCache, - cellDataLoader: cellDataLoader, - fieldNotifier: cellFieldNotifier, - cellDataPersistence: TextCellDataPersistence(cellId: _cellId), - ); - case FieldType.RichText: - final cellDataLoader = CellDataLoader( - cellId: _cellId, - parser: StringCellDataParser(), - ); - return TextCellController( - cellId: _cellId, - cellCache: _cellCache, - cellDataLoader: cellDataLoader, - fieldNotifier: cellFieldNotifier, - cellDataPersistence: TextCellDataPersistence(cellId: _cellId), - ); - case FieldType.MultiSelect: - case FieldType.SingleSelect: - case FieldType.Checklist: - final cellDataLoader = CellDataLoader( - cellId: _cellId, - parser: SelectOptionCellDataParser(), - reloadOnFieldChanged: true, - ); - - return SelectOptionCellController( - cellId: _cellId, - cellCache: _cellCache, - cellDataLoader: cellDataLoader, - fieldNotifier: cellFieldNotifier, - cellDataPersistence: TextCellDataPersistence(cellId: _cellId), - ); - - case FieldType.URL: - final cellDataLoader = CellDataLoader( - cellId: _cellId, - parser: URLCellDataParser(), - ); - return URLCellController( - cellId: _cellId, - cellCache: _cellCache, - cellDataLoader: cellDataLoader, - fieldNotifier: cellFieldNotifier, - cellDataPersistence: TextCellDataPersistence(cellId: _cellId), - ); - } - throw UnimplementedError; - } -} +import 'dart:async'; +import 'package:appflowy/plugins/database_view/application/field/field_listener.dart'; +import 'package:appflowy_backend/log.dart'; +import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pbenum.dart'; +import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; +import 'package:dartz/dartz.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/foundation.dart'; +import '../field/field_controller.dart'; +import '../field/field_service.dart'; +import '../field/type_option/type_option_context.dart'; +import 'cell_listener.dart'; +import 'cell_service.dart'; /// IGridCellController is used to manipulate the cell and receive notifications. /// * Read/Write cell data @@ -124,40 +22,19 @@ class CellControllerBuilder { // ignore: must_be_immutable class CellController extends Equatable { final CellIdentifier cellId; - final CellCache _cellsCache; + final CellCache _cellCache; final CellCacheKey _cacheKey; final FieldBackendService _fieldBackendSvc; - final CellFieldNotifier _fieldNotifier; + final SingleFieldListener _fieldListener; final CellDataLoader _cellDataLoader; final CellDataPersistence _cellDataPersistence; CellListener? _cellListener; CellDataNotifier? _cellDataNotifier; - bool isListening = false; - VoidCallback? _onFieldChangedFn; + VoidCallback? _onCellFieldChanged; Timer? _loadDataOperation; Timer? _saveDataOperation; - bool _isDispose = false; - - CellController({ - required this.cellId, - required CellCache cellCache, - required CellFieldNotifier fieldNotifier, - required CellDataLoader cellDataLoader, - required CellDataPersistence cellDataPersistence, - }) : _cellsCache = cellCache, - _cellDataLoader = cellDataLoader, - _cellDataPersistence = cellDataPersistence, - _fieldNotifier = fieldNotifier, - _fieldBackendSvc = FieldBackendService( - viewId: cellId.viewId, - fieldId: cellId.fieldInfo.id, - ), - _cacheKey = CellCacheKey( - rowId: cellId.rowId, - fieldId: cellId.fieldInfo.id, - ); String get viewId => cellId.viewId; @@ -169,34 +46,28 @@ class CellController extends Equatable { FieldType get fieldType => cellId.fieldInfo.fieldType; - /// Listen on the cell content or field changes - /// - /// An optional [listenWhenOnCellChanged] can be implemented for more - /// granular control over when [listener] is called. - /// [listenWhenOnCellChanged] will be invoked on each [onCellChanged] - /// get called. - /// [listenWhenOnCellChanged] takes the previous `value` and current - /// `value` and must return a [bool] which determines whether or not - /// the [onCellChanged] function will be invoked. - /// [onCellChanged] is optional and if omitted, it will default to `true`. - /// - VoidCallback? startListening({ - required void Function(T?) onCellChanged, - bool Function(T? oldValue, T? newValue)? listenWhenOnCellChanged, - VoidCallback? onCellFieldChanged, - }) { - if (isListening) { - Log.error("Already started. It seems like you should call clone first"); - return null; - } - isListening = true; - - _cellDataNotifier = CellDataNotifier( - value: _cellsCache.get(_cacheKey), - listenWhen: listenWhenOnCellChanged, + CellController({ + required this.cellId, + required CellCache cellCache, + required CellDataLoader cellDataLoader, + required CellDataPersistence cellDataPersistence, + }) : _cellCache = cellCache, + _cellDataLoader = cellDataLoader, + _cellDataPersistence = cellDataPersistence, + _fieldListener = SingleFieldListener(fieldId: cellId.fieldId), + _fieldBackendSvc = FieldBackendService( + viewId: cellId.viewId, + fieldId: cellId.fieldInfo.id, + ), + _cacheKey = CellCacheKey( + rowId: cellId.rowId, + fieldId: cellId.fieldInfo.id, + ) { + _cellDataNotifier = CellDataNotifier(value: _cellCache.get(_cacheKey)); + _cellListener = CellListener( + rowId: cellId.rowId, + fieldId: cellId.fieldInfo.id, ); - _cellListener = - CellListener(rowId: cellId.rowId, fieldId: cellId.fieldInfo.id); /// 1.Listen on user edit event and load the new cell data if needed. /// For example: @@ -205,7 +76,7 @@ class CellController extends Equatable { _cellListener?.start(onCellChanged: (result) { result.fold( (_) { - _cellsCache.remove(_cacheKey); + _cellCache.remove(_cacheKey); _loadData(); }, (err) => Log.error(err), @@ -213,20 +84,25 @@ class CellController extends Equatable { }); /// 2.Listen on the field event and load the cell data if needed. - _onFieldChangedFn = () { - if (onCellFieldChanged != null) { - onCellFieldChanged(); - } + _fieldListener.start(onFieldChanged: (result) { + result.fold((fieldPB) { + /// reloadOnFieldChanged should be true if you need to load the data when the corresponding field is changed + /// For example: + /// ¥12 -> $12 + if (_cellDataLoader.reloadOnFieldChanged) { + _loadData(); + } + _onCellFieldChanged?.call(); + }, (err) => Log.error(err)); + }); + } - /// reloadOnFieldChanged should be true if you need to load the data when the corresponding field is changed - /// For example: - /// ¥12 -> $12 - if (_cellDataLoader.reloadOnFieldChanged) { - _loadData(); - } - }; - - _fieldNotifier.register(_cacheKey, _onFieldChangedFn!); + /// Listen on the cell content or field changes + VoidCallback? startListening({ + required void Function(T?) onCellChanged, + VoidCallback? onCellFieldChanged, + }) { + _onCellFieldChanged = onCellFieldChanged; /// Notify the listener, the cell data was changed. onCellChangedFn() => onCellChanged(_cellDataNotifier?.value); @@ -244,7 +120,7 @@ class CellController extends Equatable { /// The cell data will be read from the Cache first, and load from disk if it does not exist. /// You can set [loadIfNotExist] to false (default is true) to disable loading the cell data. T? getCellData({bool loadIfNotExist = true}) { - final data = _cellsCache.get(_cacheKey); + final data = _cellCache.get(_cacheKey); if (data == null && loadIfNotExist) { _loadData(); } @@ -294,9 +170,9 @@ class CellController extends Equatable { _loadDataOperation = Timer(const Duration(milliseconds: 10), () { _cellDataLoader.loadData().then((data) { if (data != null) { - _cellsCache.insert(_cacheKey, GridBaseCell(object: data)); + _cellCache.insert(_cacheKey, GridBaseCell(object: data)); } else { - _cellsCache.remove(_cacheKey); + _cellCache.remove(_cacheKey); } _cellDataNotifier?.value = data; @@ -305,54 +181,17 @@ class CellController extends Equatable { } Future dispose() async { - if (_isDispose) { - Log.error("$this should only dispose once"); - return; - } - _isDispose = true; await _cellListener?.stop(); _loadDataOperation?.cancel(); _saveDataOperation?.cancel(); _cellDataNotifier?.dispose(); + await _fieldListener.stop(); _cellDataNotifier = null; - - if (_onFieldChangedFn != null) { - _fieldNotifier.unregister(_cacheKey, _onFieldChangedFn!); - await _fieldNotifier.dispose(); - _onFieldChangedFn = null; - } } @override List get props => - [_cellsCache.get(_cacheKey) ?? "", cellId.rowId + cellId.fieldInfo.id]; -} - -class GridCellFieldNotifierImpl extends ICellFieldNotifier { - final FieldController _fieldController; - OnReceiveUpdateFields? _onChangesetFn; - - GridCellFieldNotifierImpl(FieldController cache) : _fieldController = cache; - - @override - void onCellDispose() { - if (_onChangesetFn != null) { - _fieldController.removeListener(onChangesetListener: _onChangesetFn!); - _onChangesetFn = null; - } - } - - @override - void onCellFieldChanged(void Function(FieldInfo) callback) { - _onChangesetFn = (List filedInfos) { - for (final field in filedInfos) { - callback(field); - } - }; - _fieldController.addListener( - onReceiveFields: _onChangesetFn, - ); - } + [_cellCache.get(_cacheKey) ?? "", cellId.rowId + cellId.fieldInfo.id]; } class CellDataNotifier extends ChangeNotifier { diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_controller_builder.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_controller_builder.dart new file mode 100644 index 0000000000..d80142e3f8 --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_controller_builder.dart @@ -0,0 +1,108 @@ +import 'package:appflowy_backend/protobuf/flowy-database/date_type_option_entities.pb.dart'; +import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pbenum.dart'; +import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart'; +import 'package:appflowy_backend/protobuf/flowy-database/url_type_option_entities.pb.dart'; + +import 'cell_controller.dart'; +import 'cell_service.dart'; + +typedef TextCellController = CellController; +typedef CheckboxCellController = CellController; +typedef NumberCellController = CellController; +typedef SelectOptionCellController + = CellController; +typedef ChecklistCellController + = CellController; +typedef DateCellController = CellController; +typedef URLCellController = CellController; + +class CellControllerBuilder { + final CellIdentifier _cellId; + final CellCache _cellCache; + + CellControllerBuilder({ + required CellIdentifier cellId, + required CellCache cellCache, + }) : _cellCache = cellCache, + _cellId = cellId; + + CellController build() { + switch (_cellId.fieldType) { + case FieldType.Checkbox: + final cellDataLoader = CellDataLoader( + cellId: _cellId, + parser: StringCellDataParser(), + ); + return TextCellController( + cellId: _cellId, + cellCache: _cellCache, + cellDataLoader: cellDataLoader, + cellDataPersistence: TextCellDataPersistence(cellId: _cellId), + ); + case FieldType.DateTime: + final cellDataLoader = CellDataLoader( + cellId: _cellId, + parser: DateCellDataParser(), + reloadOnFieldChanged: true, + ); + + return DateCellController( + cellId: _cellId, + cellCache: _cellCache, + cellDataLoader: cellDataLoader, + cellDataPersistence: DateCellDataPersistence(cellId: _cellId), + ); + case FieldType.Number: + final cellDataLoader = CellDataLoader( + cellId: _cellId, + parser: StringCellDataParser(), + reloadOnFieldChanged: true, + ); + return NumberCellController( + cellId: _cellId, + cellCache: _cellCache, + cellDataLoader: cellDataLoader, + cellDataPersistence: TextCellDataPersistence(cellId: _cellId), + ); + case FieldType.RichText: + final cellDataLoader = CellDataLoader( + cellId: _cellId, + parser: StringCellDataParser(), + ); + return TextCellController( + cellId: _cellId, + cellCache: _cellCache, + cellDataLoader: cellDataLoader, + cellDataPersistence: TextCellDataPersistence(cellId: _cellId), + ); + case FieldType.MultiSelect: + case FieldType.SingleSelect: + case FieldType.Checklist: + final cellDataLoader = CellDataLoader( + cellId: _cellId, + parser: SelectOptionCellDataParser(), + reloadOnFieldChanged: true, + ); + + return SelectOptionCellController( + cellId: _cellId, + cellCache: _cellCache, + cellDataLoader: cellDataLoader, + cellDataPersistence: TextCellDataPersistence(cellId: _cellId), + ); + + case FieldType.URL: + final cellDataLoader = CellDataLoader( + cellId: _cellId, + parser: URLCellDataParser(), + ); + return URLCellController( + cellId: _cellId, + cellCache: _cellCache, + cellDataLoader: cellDataLoader, + cellDataPersistence: TextCellDataPersistence(cellId: _cellId), + ); + } + throw UnimplementedError; + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_field_notifier.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_field_notifier.dart deleted file mode 100644 index 02fc0b7261..0000000000 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_field_notifier.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:flutter/foundation.dart'; -import '../field/field_controller.dart'; -import 'cell_service.dart'; - -abstract class ICellFieldNotifier { - void onCellFieldChanged(void Function(FieldInfo) callback); - void onCellDispose(); -} - -/// DatabasePB's cell helper wrapper that enables each cell will get notified when the corresponding field was changed. -/// You Register an onFieldChanged callback to listen to the cell changes, and unregister if you don't want to listen. -class CellFieldNotifier { - final ICellFieldNotifier notifier; - - /// fieldId: {objectId: callback} - final Map>> _fieldListenerByFieldId = - {}; - - CellFieldNotifier({required this.notifier}) { - notifier.onCellFieldChanged( - (field) { - final map = _fieldListenerByFieldId[field.id]; - if (map != null) { - for (final callbacks in map.values) { - for (final callback in callbacks) { - callback(); - } - } - } - }, - ); - } - - /// - void register(CellCacheKey cacheKey, VoidCallback onFieldChanged) { - var map = _fieldListenerByFieldId[cacheKey.fieldId]; - if (map == null) { - _fieldListenerByFieldId[cacheKey.fieldId] = {}; - map = _fieldListenerByFieldId[cacheKey.fieldId]; - map![cacheKey.rowId] = [onFieldChanged]; - } else { - var objects = map[cacheKey.rowId]; - if (objects == null) { - map[cacheKey.rowId] = [onFieldChanged]; - } else { - objects.add(onFieldChanged); - } - } - } - - void unregister(CellCacheKey cacheKey, VoidCallback fn) { - var callbacks = _fieldListenerByFieldId[cacheKey.fieldId]?[cacheKey.rowId]; - final index = callbacks?.indexWhere((callback) => callback == fn); - if (index != null && index != -1) { - callbacks?.removeAt(index); - } - } - - Future dispose() async { - notifier.onCellDispose(); - _fieldListenerByFieldId.clear(); - } -} diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_service.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_service.dart index 9d3ab3344e..19aa0b6048 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_service.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/cell/cell_service.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:collection'; import 'package:dartz/dartz.dart'; -import 'package:equatable/equatable.dart'; import 'package:appflowy_backend/dispatch/dispatch.dart'; import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; @@ -15,13 +14,8 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:convert' show utf8; import '../field/field_controller.dart'; -import '../field/field_service.dart'; -import '../field/type_option/type_option_context.dart'; -import 'cell_field_notifier.dart'; -import 'cell_listener.dart'; part 'cell_service.freezed.dart'; part 'cell_data_loader.dart'; -part 'cell_controller.dart'; part 'cell_cache.dart'; part 'cell_data_persistence.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_editor_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_editor_bloc.dart index 586235eeaa..8ec40c1c93 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_editor_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_editor_bloc.dart @@ -10,7 +10,7 @@ import 'type_option/type_option_data_controller.dart'; part 'field_editor_bloc.freezed.dart'; class FieldEditorBloc extends Bloc { - final TypeOptionDataController dataController; + final TypeOptionController dataController; FieldEditorBloc({ required String viewId, @@ -18,7 +18,7 @@ class FieldEditorBloc extends Bloc { required bool isGroupField, required IFieldTypeOptionLoader loader, }) : dataController = - TypeOptionDataController(viewId: viewId, loader: loader), + TypeOptionController(viewId: viewId, loader: loader), super(FieldEditorState.initial(viewId, fieldName, isGroupField)) { on( (event, emit) async { diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_type_option_edit_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_type_option_edit_bloc.dart index 65800cf54e..5f74756652 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_type_option_edit_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/field_type_option_edit_bloc.dart @@ -8,10 +8,10 @@ part 'field_type_option_edit_bloc.freezed.dart'; class FieldTypeOptionEditBloc extends Bloc { - final TypeOptionDataController _dataController; + final TypeOptionController _dataController; void Function()? _fieldListenFn; - FieldTypeOptionEditBloc(TypeOptionDataController dataController) + FieldTypeOptionEditBloc(TypeOptionController dataController) : _dataController = dataController, super(FieldTypeOptionEditState.initial(dataController)) { on( @@ -58,9 +58,9 @@ class FieldTypeOptionEditState with _$FieldTypeOptionEditState { }) = _FieldTypeOptionEditState; factory FieldTypeOptionEditState.initial( - TypeOptionDataController typeOptionDataController, + TypeOptionController typeOptionController, ) => FieldTypeOptionEditState( - field: typeOptionDataController.field, + field: typeOptionController.field, ); } diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/type_option/type_option_context.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/type_option/type_option_context.dart index 34ed5d161d..279792ed34 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/type_option/type_option_context.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/type_option/type_option_context.dart @@ -109,11 +109,11 @@ class ChecklistTypeOptionWidgetDataParser class TypeOptionContext { T? _typeOptionObject; final TypeOptionParser dataParser; - final TypeOptionDataController _dataController; + final TypeOptionController _dataController; TypeOptionContext({ required this.dataParser, - required TypeOptionDataController dataController, + required TypeOptionController dataController, }) : _dataController = dataController; String get viewId => _dataController.viewId; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/type_option/type_option_data_controller.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/type_option/type_option_data_controller.dart index b97257f0cc..eb91dc88a3 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/field/type_option/type_option_data_controller.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/field/type_option/type_option_data_controller.dart @@ -9,25 +9,25 @@ import 'package:appflowy_backend/log.dart'; import '../field_service.dart'; import 'type_option_context.dart'; -class TypeOptionDataController { +class TypeOptionController { final String viewId; + late TypeOptionPB _typeOption; final IFieldTypeOptionLoader loader; - late TypeOptionPB _typeOptiondata; final PublishNotifier _fieldNotifier = PublishNotifier(); - /// Returns a [TypeOptionDataController] used to modify the specified + /// Returns a [TypeOptionController] used to modify the specified /// [FieldPB]'s data /// /// Should call [loadTypeOptionData] if the passed-in [FieldInfo] /// is null /// - TypeOptionDataController({ + TypeOptionController({ required this.viewId, required this.loader, FieldInfo? fieldInfo, }) { if (fieldInfo != null) { - _typeOptiondata = TypeOptionPB.create() + _typeOption = TypeOptionPB.create() ..viewId = viewId ..field_2 = fieldInfo.field; } @@ -38,7 +38,7 @@ class TypeOptionDataController { return result.fold( (data) { data.freeze(); - _typeOptiondata = data; + _typeOption = data; _fieldNotifier.value = data.field_2; return left(data); }, @@ -50,28 +50,28 @@ class TypeOptionDataController { } FieldPB get field { - return _typeOptiondata.field_2; + return _typeOption.field_2; } T getTypeOption(TypeOptionParser parser) { - return parser.fromBuffer(_typeOptiondata.typeOptionData); + return parser.fromBuffer(_typeOption.typeOptionData); } set fieldName(String name) { - _typeOptiondata = _typeOptiondata.rebuild((rebuildData) { + _typeOption = _typeOption.rebuild((rebuildData) { rebuildData.field_2 = rebuildData.field_2.rebuild((rebuildField) { rebuildField.name = name; }); }); - _fieldNotifier.value = _typeOptiondata.field_2; + _fieldNotifier.value = _typeOption.field_2; FieldBackendService(viewId: viewId, fieldId: field.id) .updateField(name: name); } set typeOptionData(List typeOptionData) { - _typeOptiondata = _typeOptiondata.rebuild((rebuildData) { + _typeOption = _typeOption.rebuild((rebuildData) { if (typeOptionData.isNotEmpty) { rebuildData.typeOptionData = typeOptionData; } diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/application/row/row_data_controller.dart b/frontend/appflowy_flutter/lib/plugins/database_view/application/row/row_data_controller.dart index 1689bb3d90..bbb13d16a9 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/application/row/row_data_controller.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/application/row/row_data_controller.dart @@ -1,24 +1,20 @@ import 'package:flutter/material.dart'; -import '../../grid/presentation/widgets/cell/cell_builder.dart'; -import '../cell/cell_field_notifier.dart'; import '../cell/cell_service.dart'; -import '../field/field_controller.dart'; import 'row_cache.dart'; typedef OnRowChanged = void Function(CellByFieldId, RowsChangedReason); -class RowDataController extends GridCellBuilderDelegate { +class RowDataController { final RowInfo rowInfo; final List _onRowChangedListeners = []; - final FieldController _fieldController; final RowCache _rowCache; + get cellCache => _rowCache.cellCache; + RowDataController({ required this.rowInfo, - required FieldController fieldController, required RowCache rowCache, - }) : _fieldController = fieldController, - _rowCache = rowCache; + }) : _rowCache = rowCache; CellByFieldId loadData() { return _rowCache.loadGridCells(rowInfo.rowPB.id); @@ -36,14 +32,4 @@ class RowDataController extends GridCellBuilderDelegate { _rowCache.removeRowListener(fn); } } - - // GridCellBuilderDelegate implementation - @override - CellFieldNotifier buildFieldNotifier() { - return CellFieldNotifier( - notifier: GridCellFieldNotifierImpl(_fieldController)); - } - - @override - CellCache get cellCache => _rowCache.cellCache; } diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/board/application/board_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/board/application/board_bloc.dart index a486ae2e1f..a0be622128 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/board/application/board_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/board/application/board_bloc.dart @@ -20,18 +20,18 @@ import 'group_controller.dart'; part 'board_bloc.freezed.dart'; class BoardBloc extends Bloc { - final BoardDataController _gridDataController; + final BoardDataController _boardDataController; late final AppFlowyBoardController boardController; final GroupBackendService _groupBackendSvc; final LinkedHashMap groupControllers = LinkedHashMap(); - FieldController get fieldController => _gridDataController.fieldController; - String get viewId => _gridDataController.viewId; + FieldController get fieldController => _boardDataController.fieldController; + String get viewId => _boardDataController.viewId; BoardBloc({required ViewPB view}) : _groupBackendSvc = GroupBackendService(viewId: view.id), - _gridDataController = BoardDataController(view: view), + _boardDataController = BoardDataController(view: view), super(BoardState.initial(view.id)) { boardController = AppFlowyBoardController( onMoveGroup: ( @@ -72,7 +72,7 @@ class BoardBloc extends Bloc { }, createBottomRow: (groupId) async { final startRowId = groupControllers[groupId]?.lastRow()?.id; - final result = await _gridDataController.createBoardCard( + final result = await _boardDataController.createBoardCard( groupId, startRowId: startRowId, ); @@ -82,7 +82,7 @@ class BoardBloc extends Bloc { ); }, createHeaderRow: (String groupId) async { - final result = await _gridDataController.createBoardCard(groupId); + final result = await _boardDataController.createBoardCard(groupId); result.fold( (_) {}, (err) => Log.error(err), @@ -178,7 +178,7 @@ class BoardBloc extends Bloc { @override Future close() async { - await _gridDataController.dispose(); + await _boardDataController.dispose(); for (final controller in groupControllers.values) { controller.dispose(); } @@ -204,11 +204,11 @@ class BoardBloc extends Bloc { } RowCache? getRowCache(String blockId) { - return _gridDataController.rowCache; + return _boardDataController.rowCache; } void _startListening() { - _gridDataController.addListener( + _boardDataController.addListener( onDatabaseChanged: (grid) { if (!isClosed) { add(BoardEvent.didReceiveGridUpdate(grid)); @@ -264,7 +264,7 @@ class BoardBloc extends Bloc { } Future _openGrid(Emitter emit) async { - final result = await _gridDataController.openGrid(); + final result = await _boardDataController.openGrid(); result.fold( (grid) => emit( state.copyWith(loadingState: GridLoadingState.finish(left(unit))), diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/board/application/board_data_controller.dart b/frontend/appflowy_flutter/lib/plugins/database_view/board/application/board_data_controller.dart index ba801ea90d..5017b2c4e5 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/board/application/board_data_controller.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/board/application/board_data_controller.dart @@ -21,7 +21,7 @@ typedef OnResetGroups = void Function(List); class BoardDataController { final String viewId; - final DatabaseBackendService _databaseFFIService; + final DatabaseBackendService _databaseSvc; final FieldController fieldController; final BoardListener _listener; late DatabaseViewCache _viewCache; @@ -38,7 +38,7 @@ class BoardDataController { BoardDataController({required ViewPB view}) : viewId = view.id, _listener = BoardListener(view.id), - _databaseFFIService = DatabaseBackendService(viewId: view.id), + _databaseSvc = DatabaseBackendService(viewId: view.id), fieldController = FieldController(viewId: view.id) { // _viewCache = DatabaseViewCache( @@ -100,7 +100,7 @@ class BoardDataController { } Future> openGrid() async { - final result = await _databaseFFIService.openGrid(); + final result = await _databaseSvc.openGrid(); return result.fold( (grid) async { _onDatabaseChanged?.call(grid); @@ -121,17 +121,17 @@ class BoardDataController { Future> createBoardCard(String groupId, {String? startRowId}) { - return _databaseFFIService.createBoardCard(groupId, startRowId); + return _databaseSvc.createBoardCard(groupId, startRowId); } Future dispose() async { await _viewCache.dispose(); - await _databaseFFIService.closeView(); + await _databaseSvc.closeView(); await fieldController.dispose(); } Future _loadGroups() async { - final result = await _databaseFFIService.loadGroups(); + final result = await _databaseSvc.loadGroups(); return Future( () => result.fold( (groups) { diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_checkbox_cell_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_checkbox_cell_bloc.dart index 7f30e775af..57cc336ed7 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_checkbox_cell_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_checkbox_cell_bloc.dart @@ -1,8 +1,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; - -import '../../../application/cell/cell_service.dart'; +import '../../../application/cell/cell_controller_builder.dart'; part 'board_checkbox_cell_bloc.freezed.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_date_cell_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_date_cell_bloc.dart index 72538ed9d3..288f3f26ad 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_date_cell_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_date_cell_bloc.dart @@ -3,7 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; -import '../../../application/cell/cell_service.dart'; +import '../../../application/cell/cell_controller_builder.dart'; import '../../../application/field/field_controller.dart'; part 'board_date_cell_bloc.freezed.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_number_cell_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_number_cell_bloc.dart index cee2c0541c..bcad0469a0 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_number_cell_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_number_cell_bloc.dart @@ -1,7 +1,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; -import '../../../application/cell/cell_service.dart'; + +import '../../../application/cell/cell_controller_builder.dart'; part 'board_number_cell_bloc.freezed.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_select_option_cell_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_select_option_cell_bloc.dart index a42a9d14a4..747c055cda 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_select_option_cell_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_select_option_cell_bloc.dart @@ -1,5 +1,5 @@ import 'dart:async'; -import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_text_cell_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_text_cell_bloc.dart index 874404f304..7e862e3e81 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_text_cell_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_text_cell_bloc.dart @@ -1,4 +1,4 @@ -import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_url_cell_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_url_cell_bloc.dart index 0ff9316277..906b0c6f61 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_url_cell_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/board_url_cell_bloc.dart @@ -1,10 +1,9 @@ +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy_backend/protobuf/flowy-database/url_type_option_entities.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; -import '../../../application/cell/cell_service.dart'; - part 'board_url_cell_bloc.freezed.dart'; class BoardURLCellBloc extends Bloc { diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/card_data_controller.dart b/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/card_data_controller.dart index 67ddbaf993..d6b49a6302 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/card_data_controller.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/board/application/card/card_data_controller.dart @@ -1,9 +1,7 @@ import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart'; import 'package:flutter/foundation.dart'; -import '../../../application/cell/cell_field_notifier.dart'; import '../../../application/cell/cell_service.dart'; -import '../../../application/field/field_controller.dart'; import '../../../application/row/row_cache.dart'; import '../../presentation/card/card_cell_builder.dart'; @@ -11,16 +9,13 @@ typedef OnCardChanged = void Function(CellByFieldId, RowsChangedReason); class CardDataController extends BoardCellBuilderDelegate { final RowPB rowPB; - final FieldController _fieldController; final RowCache _rowCache; final List _onCardChangedListeners = []; CardDataController({ required this.rowPB, - required FieldController fieldController, required RowCache rowCache, - }) : _fieldController = fieldController, - _rowCache = rowCache; + }) : _rowCache = rowCache; CellByFieldId loadData() { return _rowCache.loadGridCells(rowPB.id); @@ -39,12 +34,6 @@ class CardDataController extends BoardCellBuilderDelegate { } } - @override - CellFieldNotifier buildFieldNotifier() { - return CellFieldNotifier( - notifier: GridCellFieldNotifierImpl(_fieldController)); - } - @override CellCache get cellCache => _rowCache.cellCache; } diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/board_page.dart b/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/board_page.dart index 80f9a45092..c3d0b41f93 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/board_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/board_page.dart @@ -229,7 +229,6 @@ class _BoardContentState extends State { final fieldController = context.read().fieldController; final viewId = context.read().viewId; final cardController = CardDataController( - fieldController: fieldController, rowCache: rowCache, rowPB: rowPB, ); @@ -306,7 +305,6 @@ class _BoardContentState extends State { final dataController = RowDataController( rowInfo: rowInfo, - fieldController: fieldController, rowCache: rowCache, ); @@ -314,7 +312,7 @@ class _BoardContentState extends State { context: context, builder: (BuildContext context) { return RowDetailPage( - cellBuilder: GridCellBuilder(delegate: dataController), + cellBuilder: GridCellBuilder(cellCache: dataController.cellCache), dataController: dataController, ); }, diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_checkbox_cell.dart b/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_checkbox_cell.dart index 011c68b60a..dee8b7b91f 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_checkbox_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_checkbox_cell.dart @@ -1,4 +1,4 @@ -import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database_view/board/application/card/board_checkbox_cell_bloc.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra_ui/style_widget/icon_button.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_checklist_cell.dart b/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_checklist_cell.dart index e8213786fb..ab4f6ad572 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_checklist_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_checklist_cell.dart @@ -1,7 +1,7 @@ +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../../application/cell/cell_service.dart'; import '../../../grid/application/cell/checklist_cell_bloc.dart'; import '../../../grid/presentation/widgets/cell/checklist_cell/checklist_progress_bar.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_date_cell.dart b/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_date_cell.dart index 7cb5173f25..4fd733af63 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_date_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_date_cell.dart @@ -1,9 +1,9 @@ +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database_view/board/application/card/board_date_cell_bloc.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../../application/cell/cell_service.dart'; import 'define.dart'; class BoardDateCell extends StatefulWidget { diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_number_cell.dart b/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_number_cell.dart index 13b8372b8b..d2d0aa69d7 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_number_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_number_cell.dart @@ -1,7 +1,7 @@ +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../../application/cell/cell_service.dart'; import '../../application/card/board_number_cell_bloc.dart'; import 'define.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_select_option_cell.dart b/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_select_option_cell.dart index b5e75fcf42..d3ae3dd55d 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_select_option_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_select_option_cell.dart @@ -1,9 +1,8 @@ +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; - -import '../../../application/cell/cell_service.dart'; import '../../../grid/presentation/widgets/cell/select_option_cell/extension.dart'; import '../../../grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart'; import '../../application/card/board_select_option_cell_bloc.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_text_cell.dart b/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_text_cell.dart index 31ea45f86b..8bac91820c 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_text_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_text_cell.dart @@ -1,10 +1,10 @@ +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database_view/grid/presentation/widgets/cell/cell_builder.dart'; import 'package:flowy_infra/size.dart'; import 'package:flowy_infra_ui/style_widget/text.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:textstyle_extensions/textstyle_extensions.dart'; -import '../../../application/cell/cell_service.dart'; import '../../application/card/board_text_cell_bloc.dart'; import 'board_cell.dart'; import 'define.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_url_cell.dart b/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_url_cell.dart index 25c8706440..62192d4f0f 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_url_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/board_url_cell.dart @@ -1,9 +1,9 @@ +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:flowy_infra/size.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:textstyle_extensions/textstyle_extensions.dart'; -import '../../../application/cell/cell_service.dart'; import '../../application/card/board_url_cell_bloc.dart'; import 'define.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/card_cell_builder.dart b/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/card_cell_builder.dart index a5e8a0e8a6..e7db611b95 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/card_cell_builder.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/board/presentation/card/card_cell_builder.dart @@ -1,3 +1,4 @@ +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart'; import 'package:flutter/material.dart'; @@ -11,7 +12,7 @@ import 'board_select_option_cell.dart'; import 'board_text_cell.dart'; import 'board_url_cell.dart'; -abstract class BoardCellBuilderDelegate extends CellControllerBuilderDelegate { +abstract class BoardCellBuilderDelegate { CellCache get cellCache; } @@ -26,7 +27,6 @@ class BoardCellBuilder { EditableCellNotifier cellNotifier, ) { final cellControllerBuilder = CellControllerBuilder( - delegate: delegate, cellId: cellId, cellCache: delegate.cellCache, ); diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/checkbox_cell_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/checkbox_cell_bloc.dart index 49fda2addd..e9ccb23ed9 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/checkbox_cell_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/checkbox_cell_bloc.dart @@ -1,3 +1,4 @@ +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/checklist_cell_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/checklist_cell_bloc.dart index adfff3b474..3b6a61e223 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/checklist_cell_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/checklist_cell_bloc.dart @@ -1,4 +1,4 @@ -import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -10,12 +10,12 @@ part 'checklist_cell_bloc.freezed.dart'; class ChecklistCellBloc extends Bloc { final ChecklistCellController cellController; - final SelectOptionFFIService _selectOptionService; + final SelectOptionBackendService _selectOptionSvc; void Function()? _onCellChangedFn; ChecklistCellBloc({ required this.cellController, - }) : _selectOptionService = - SelectOptionFFIService(cellId: cellController.cellId), + }) : _selectOptionSvc = + SelectOptionBackendService(cellId: cellController.cellId), super(ChecklistCellState.initial(cellController)) { on( (event, emit) async { @@ -60,7 +60,7 @@ class ChecklistCellBloc extends Bloc { } void _loadOptions() { - _selectOptionService.getOptionContext().then((result) { + _selectOptionSvc.getCellData().then((result) { if (isClosed) return; return result.fold( diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/checklist_cell_editor_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/checklist_cell_editor_bloc.dart index 0b8dc4abec..d0b70ffe8d 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/checklist_cell_editor_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/checklist_cell_editor_bloc.dart @@ -1,25 +1,24 @@ import 'dart:async'; -import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:dartz/dartz.dart'; import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; - import 'select_option_service.dart'; part 'checklist_cell_editor_bloc.freezed.dart'; class ChecklistCellEditorBloc extends Bloc { - final SelectOptionFFIService _selectOptionService; + final SelectOptionBackendService _selectOptionService; final ChecklistCellController cellController; ChecklistCellEditorBloc({ required this.cellController, }) : _selectOptionService = - SelectOptionFFIService(cellId: cellController.cellId), + SelectOptionBackendService(cellId: cellController.cellId), super(ChecklistCellEditorState.initial(cellController)) { on( (event, emit) async { @@ -87,7 +86,7 @@ class ChecklistCellEditorBloc } void _loadOptions() { - _selectOptionService.getOptionContext().then((result) { + _selectOptionService.getCellData().then((result) { if (isClosed) return; return result.fold( diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/date_cal_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/date_cal_bloc.dart index 6952adccee..f0a83077d3 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/date_cal_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/date_cal_bloc.dart @@ -1,4 +1,5 @@ import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; import 'package:appflowy/plugins/database_view/application/field/field_service.dart'; import 'package:easy_localization/easy_localization.dart' diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/date_cell_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/date_cell_bloc.dart index 349b25666d..95c389649d 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/date_cell_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/date_cell_bloc.dart @@ -1,4 +1,4 @@ -import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database_view/application/field/field_controller.dart'; import 'package:appflowy_backend/protobuf/flowy-database/date_type_option_entities.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/number_cell_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/number_cell_bloc.dart index da445497ca..e106e31dc8 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/number_cell_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/number_cell_bloc.dart @@ -1,4 +1,4 @@ -import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy_backend/log.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -49,22 +49,13 @@ class NumberCellBloc extends Bloc { } void _startListening() { - _onCellChangedFn = - cellController.startListening(onCellChanged: ((cellContent) { - if (!isClosed) { - add(NumberCellEvent.didReceiveCellUpdate(cellContent)); - } - }), listenWhenOnCellChanged: (oldValue, newValue) { - // If the new value is not the same as the content, which means the - // backend formatted the content that user enter. For example: - // - // state.cellContent: "abc" - // oldValue: "" - // newValue: "" - // The oldValue is the same as newValue. So the [onCellChanged] won't - // get called. So just return true to refresh the cell content - return true; - }); + _onCellChangedFn = cellController.startListening( + onCellChanged: ((cellContent) { + if (!isClosed) { + add(NumberCellEvent.didReceiveCellUpdate(cellContent)); + } + }), + ); } } diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/select_option_cell_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/select_option_cell_bloc.dart index 4022729959..a0654910bf 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/select_option_cell_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/select_option_cell_bloc.dart @@ -1,5 +1,5 @@ import 'dart:async'; -import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/select_option_editor_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/select_option_editor_bloc.dart index 10d01cc02a..19c950476c 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/select_option_editor_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/select_option_editor_bloc.dart @@ -1,5 +1,5 @@ import 'dart:async'; -import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:dartz/dartz.dart'; import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart'; @@ -11,13 +11,13 @@ part 'select_option_editor_bloc.freezed.dart'; class SelectOptionCellEditorBloc extends Bloc { - final SelectOptionFFIService _selectOptionService; + final SelectOptionBackendService _selectOptionService; final SelectOptionCellController cellController; SelectOptionCellEditorBloc({ required this.cellController, }) : _selectOptionService = - SelectOptionFFIService(cellId: cellController.cellId), + SelectOptionBackendService(cellId: cellController.cellId), super(SelectOptionEditorState.initial(cellController)) { on( (event, emit) async { @@ -159,7 +159,7 @@ class SelectOptionCellEditorBloc } Future _loadOptions() async { - final result = await _selectOptionService.getOptionContext(); + final result = await _selectOptionService.getCellData(); if (isClosed) { Log.warn("Unexpected closing the bloc"); return; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/select_option_service.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/select_option_service.dart index 774aba57f8..a7f6e82755 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/select_option_service.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/select_option_service.dart @@ -6,9 +6,9 @@ import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-database/cell_entities.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-database/select_type_option.pb.dart'; -class SelectOptionFFIService { +class SelectOptionBackendService { final CellIdentifier cellId; - SelectOptionFFIService({required this.cellId}); + SelectOptionBackendService({required this.cellId}); String get viewId => cellId.viewId; String get fieldId => cellId.fieldInfo.id; @@ -60,7 +60,7 @@ class SelectOptionFFIService { return DatabaseEventUpdateSelectOption(payload).send(); } - Future> getOptionContext() { + Future> getCellData() { final payload = CellIdPB.create() ..viewId = viewId ..fieldId = fieldId diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/text_cell_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/text_cell_bloc.dart index 5db3017a76..b5ef708a92 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/text_cell_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/text_cell_bloc.dart @@ -1,4 +1,4 @@ -import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/url_cell_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/url_cell_bloc.dart index 34bc741a91..35550d8a1a 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/url_cell_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/url_cell_bloc.dart @@ -1,4 +1,4 @@ -import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy_backend/protobuf/flowy-database/url_type_option_entities.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/url_cell_editor_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/url_cell_editor_bloc.dart index 8e3b613b04..e38588f38f 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/url_cell_editor_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/cell/url_cell_editor_bloc.dart @@ -1,4 +1,4 @@ -import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy_backend/protobuf/flowy-database/url_type_option_entities.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/filter/checklist_filter_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/filter/checklist_filter_bloc.dart index 6d2caf61e4..17e4397821 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/filter/checklist_filter_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/filter/checklist_filter_bloc.dart @@ -13,14 +13,11 @@ class ChecklistFilterEditorBloc extends Bloc { final FilterInfo filterInfo; final FilterBackendService _filterBackendSvc; - // final SelectOptionFFIService _selectOptionService; final FilterListener _listener; ChecklistFilterEditorBloc({ required this.filterInfo, }) : _filterBackendSvc = FilterBackendService(viewId: filterInfo.viewId), - // _selectOptionService = - // SelectOptionFFIService(cellId: cellController.cellId) _listener = FilterListener( viewId: filterInfo.viewId, filterId: filterInfo.filter.id, diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/grid_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/grid_bloc.dart index 4ad41d53c4..5df542893b 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/grid_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/application/grid_bloc.dart @@ -15,10 +15,9 @@ import 'dart:collection'; part 'grid_bloc.freezed.dart'; class GridBloc extends Bloc { - final DatabaseController gridController; - void Function()? _createRowOperation; + final DatabaseController databaseController; - GridBloc({required ViewPB view, required this.gridController}) + GridBloc({required ViewPB view, required this.databaseController}) : super(GridState.initial(view.id)) { on( (event, emit) async { @@ -28,12 +27,7 @@ class GridBloc extends Bloc { await _openGrid(emit); }, createRow: () { - state.loadingState.when( - loading: () { - _createRowOperation = () => gridController.createRow(); - }, - finish: (_) => gridController.createRow(), - ); + databaseController.createRow(); }, deleteRow: (rowInfo) async { final rowService = RowBackendService( @@ -63,16 +57,16 @@ class GridBloc extends Bloc { @override Future close() async { - await gridController.dispose(); + await databaseController.dispose(); return super.close(); } RowCache? getRowCache(String blockId, String rowId) { - return gridController.rowCache; + return databaseController.rowCache; } void _startListening() { - gridController.addListener( + databaseController.addListener( onGridChanged: (grid) { if (!isClosed) { add(GridEvent.didReceiveGridUpdate(grid)); @@ -92,13 +86,9 @@ class GridBloc extends Bloc { } Future _openGrid(Emitter emit) async { - final result = await gridController.openGrid(); + final result = await databaseController.openGrid(); result.fold( (grid) { - if (_createRowOperation != null) { - _createRowOperation?.call(); - _createRowOperation = null; - } emit( state.copyWith(loadingState: GridLoadingState.finish(left(unit))), ); diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/grid_page.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/grid_page.dart index a7b94a30ef..0c4af9382c 100755 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/grid_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/grid_page.dart @@ -35,11 +35,11 @@ class GridPage extends StatefulWidget { required this.view, this.onDeleted, Key? key, - }) : gridController = DatabaseController(view: view), + }) : databaseController = DatabaseController(view: view), super(key: key); final ViewPB view; - final DatabaseController gridController; + final DatabaseController databaseController; final VoidCallback? onDeleted; @override @@ -54,19 +54,19 @@ class _GridPageState extends State { BlocProvider( create: (context) => GridBloc( view: widget.view, - gridController: widget.gridController, + databaseController: widget.databaseController, )..add(const GridEvent.initial()), ), BlocProvider( create: (context) => GridFilterMenuBloc( viewId: widget.view.id, - fieldController: widget.gridController.fieldController, + fieldController: widget.databaseController.fieldController, )..add(const GridFilterMenuEvent.initial()), ), BlocProvider( create: (context) => SortMenuBloc( viewId: widget.view.id, - fieldController: widget.gridController.fieldController, + fieldController: widget.databaseController.fieldController, )..add(const SortMenuEvent.initial()), ), BlocProvider( @@ -190,7 +190,7 @@ class _FlowyGridState extends State { Widget _gridHeader(BuildContext context, String viewId) { final fieldController = - context.read().gridController.fieldController; + context.read().databaseController.fieldController; return GridHeaderSliverAdaptor( viewId: viewId, fieldController: fieldController, @@ -274,10 +274,9 @@ class _GridRowsState extends State<_GridRows> { if (rowCache == null) return const SizedBox(); final fieldController = - context.read().gridController.fieldController; + context.read().databaseController.fieldController; final dataController = RowDataController( rowInfo: rowInfo, - fieldController: fieldController, rowCache: rowCache, ); @@ -286,7 +285,7 @@ class _GridRowsState extends State<_GridRows> { child: GridRowWidget( rowInfo: rowInfo, dataController: dataController, - cellBuilder: GridCellBuilder(delegate: dataController), + cellBuilder: GridCellBuilder(cellCache: dataController.cellCache), openDetailPage: (context, cellBuilder) { _openRowDetailPage( context, @@ -310,7 +309,6 @@ class _GridRowsState extends State<_GridRows> { ) { final dataController = RowDataController( rowInfo: rowInfo, - fieldController: fieldController, rowCache: rowCache, ); diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/cell_builder.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/cell_builder.dart index e8dc7bfa29..4d0d36a9c9 100755 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/cell_builder.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/cell_builder.dart @@ -1,3 +1,4 @@ +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pb.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; @@ -13,21 +14,16 @@ import 'select_option_cell/select_option_cell.dart'; import 'text_cell.dart'; import 'url_cell/url_cell.dart'; -abstract class GridCellBuilderDelegate extends CellControllerBuilderDelegate { - CellCache get cellCache; -} - class GridCellBuilder { - final GridCellBuilderDelegate delegate; + final CellCache cellCache; GridCellBuilder({ - required this.delegate, + required this.cellCache, }); GridCellWidget build(CellIdentifier cellId, {GridCellStyle? style}) { final cellControllerBuilder = CellControllerBuilder( cellId: cellId, - cellCache: delegate.cellCache, - delegate: delegate, + cellCache: cellCache, ); final key = cellId.key(); diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/checkbox_cell.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/checkbox_cell.dart index 845c85bd6b..4dcf818e59 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/checkbox_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/checkbox_cell.dart @@ -1,9 +1,9 @@ +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy/startup/startup.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra_ui/style_widget/icon_button.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../../../application/cell/cell_service.dart'; import '../../../application/cell/checkbox_cell_bloc.dart'; import '../../layout/sizes.dart'; import 'cell_builder.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/checklist_cell/checklist_cell.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/checklist_cell/checklist_cell.dart index 18893b0b37..f8e825398d 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/checklist_cell/checklist_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/checklist_cell/checklist_cell.dart @@ -1,4 +1,4 @@ -import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database_view/grid/application/cell/checklist_cell_bloc.dart'; import 'package:appflowy/plugins/database_view/grid/presentation/layout/sizes.dart'; import 'package:appflowy_popover/appflowy_popover.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/checklist_cell/checklist_cell_editor.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/checklist_cell/checklist_cell_editor.dart index 36976d28a4..c98d9f3c35 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/checklist_cell/checklist_cell_editor.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/checklist_cell/checklist_cell_editor.dart @@ -1,3 +1,4 @@ +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; @@ -8,7 +9,6 @@ import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../../../../application/cell/cell_service.dart'; import '../../../../application/cell/checklist_cell_editor_bloc.dart'; import '../../../layout/sizes.dart'; import '../../header/type_option/select_option_editor.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/date_cell/date_cell.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/date_cell/date_cell.dart index 29573cdad0..b90f01b2f4 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/date_cell/date_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/date_cell/date_cell.dart @@ -1,4 +1,4 @@ -import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database_view/grid/application/cell/date_cell_bloc.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/widgets.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/date_cell/date_editor.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/date_cell/date_editor.dart index 4db76db17f..49db79edad 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/date_cell/date_editor.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/date_cell/date_editor.dart @@ -1,4 +1,5 @@ import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database_view/application/field/type_option/type_option_context.dart'; import 'package:appflowy/plugins/database_view/grid/application/cell/date_cal_bloc.dart'; import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart'; @@ -20,7 +21,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:table_calendar/table_calendar.dart'; import 'package:textstyle_extensions/textstyle_extensions.dart'; -import '../../../../../application/cell/cell_service.dart'; import '../../../layout/sizes.dart'; import '../../common/type_option_separator.dart'; import '../../header/type_option/date.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/number_cell.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/number_cell.dart index de73f74823..88287aa361 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/number_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/number_cell.dart @@ -1,9 +1,9 @@ import 'dart:async'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy/startup/startup.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../../../application/cell/cell_service.dart'; import '../../../application/cell/number_cell_bloc.dart'; import '../../layout/sizes.dart'; import 'cell_builder.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/select_option_cell/select_option_cell.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/select_option_cell/select_option_cell.dart index 48b2089019..d01144edb0 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/select_option_cell/select_option_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/select_option_cell/select_option_cell.dart @@ -1,4 +1,4 @@ -import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy/startup/startup.dart'; import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart index 789c2f0181..f269794a51 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart @@ -1,4 +1,5 @@ import 'dart:collection'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database_view/grid/application/cell/select_option_editor_bloc.dart'; import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:flowy_infra/theme_extension.dart'; @@ -15,7 +16,6 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:textfield_tags/textfield_tags.dart'; -import '../../../../../application/cell/cell_service.dart'; import '../../../layout/sizes.dart'; import '../../common/type_option_separator.dart'; import '../../header/type_option/select_option_editor.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/text_cell.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/text_cell.dart index 6a85a8f4f5..f29741c81f 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/text_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/text_cell.dart @@ -1,5 +1,5 @@ import 'dart:async'; -import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database_view/grid/application/cell/text_cell_bloc.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/url_cell/cell_editor.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/url_cell/cell_editor.dart index fd438055ab..ec97c7edf5 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/url_cell/cell_editor.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/url_cell/cell_editor.dart @@ -1,9 +1,8 @@ +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:flutter/material.dart'; import 'dart:async'; - import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../../../../application/cell/cell_service.dart'; import '../../../../application/cell/url_cell_editor_bloc.dart'; class URLCellEditor extends StatefulWidget { diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/url_cell/url_cell.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/url_cell/url_cell.dart index c8273f7be3..b67fb94e01 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/url_cell/url_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/cell/url_cell/url_cell.dart @@ -1,6 +1,6 @@ import 'dart:async'; import 'package:appflowy/generated/locale_keys.g.dart'; -import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy/workspace/presentation/home/toast.dart'; import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:easy_localization/easy_localization.dart'; diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_type_option_editor.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_type_option_editor.dart index d78d825929..68c24d291b 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_type_option_editor.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/field_type_option_editor.dart @@ -23,11 +23,11 @@ typedef SwitchToFieldCallback = Future> ); class FieldTypeOptionEditor extends StatelessWidget { - final TypeOptionDataController _dataController; + final TypeOptionController _dataController; final PopoverMutex popoverMutex; const FieldTypeOptionEditor({ - required TypeOptionDataController dataController, + required TypeOptionController dataController, required this.popoverMutex, Key? key, }) : _dataController = dataController, diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/builder.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/builder.dart index 487eed0a5e..8c1ed332d8 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/builder.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/header/type_option/builder.dart @@ -48,7 +48,7 @@ abstract class TypeOptionWidgetBuilder { Widget? makeTypeOptionWidget({ required BuildContext context, - required TypeOptionDataController dataController, + required TypeOptionController dataController, required PopoverMutex popoverMutex, }) { final builder = makeTypeOptionWidgetBuilder( @@ -59,7 +59,7 @@ Widget? makeTypeOptionWidget({ } TypeOptionWidgetBuilder makeTypeOptionWidgetBuilder({ - required TypeOptionDataController dataController, + required TypeOptionController dataController, required PopoverMutex popoverMutex, }) { final viewId = dataController.viewId; @@ -144,7 +144,7 @@ TypeOptionContext makeTypeOptionContext({ required FieldInfo fieldInfo, }) { final loader = FieldTypeOptionLoader(viewId: viewId, field: fieldInfo.field); - final dataController = TypeOptionDataController( + final dataController = TypeOptionController( viewId: viewId, loader: loader, fieldInfo: fieldInfo, @@ -178,7 +178,7 @@ TypeOptionContext makeSelectTypeOptionContext({ viewId: viewId, field: fieldPB, ); - final dataController = TypeOptionDataController( + final dataController = TypeOptionController( viewId: viewId, loader: loader, ); @@ -194,7 +194,7 @@ TypeOptionContext makeTypeOptionContextWithDataController({ required String viewId, required FieldType fieldType, - required TypeOptionDataController dataController, + required TypeOptionController dataController, }) { switch (fieldType) { case FieldType.Checkbox: diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/setting_button.dart b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/setting_button.dart index 64ba7e7deb..9de1d2091a 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/setting_button.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/grid/presentation/widgets/toolbar/setting_button.dart @@ -35,7 +35,7 @@ class _SettingButtonState extends State { return BlocSelector( selector: (state) { final fieldController = - context.read().gridController.fieldController; + context.read().databaseController.fieldController; return GridSettingContext( viewId: state.viewId, fieldController: fieldController, diff --git a/frontend/appflowy_flutter/lib/plugins/document/document_page.dart b/frontend/appflowy_flutter/lib/plugins/document/document_page.dart index f23ace9995..b5d45eb71d 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/document_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/document_page.dart @@ -1,5 +1,6 @@ import 'package:appflowy/plugins/document/presentation/plugins/board/board_menu_item.dart'; import 'package:appflowy/plugins/document/presentation/plugins/board/board_node_widget.dart'; +import 'package:appflowy/plugins/document/presentation/plugins/cover/cover_node_widget.dart'; import 'package:appflowy/plugins/document/presentation/plugins/grid/grid_menu_item.dart'; import 'package:appflowy/plugins/document/presentation/plugins/grid/grid_node_widget.dart'; import 'package:appflowy/plugins/document/presentation/plugins/openai/widgets/auto_completion_node_widget.dart'; @@ -9,6 +10,7 @@ import 'package:appflowy/plugins/document/presentation/plugins/openai/widgets/sm import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart'; +import 'package:dartz/dartz.dart' as dartz; import 'package:flowy_infra_ui/widget/error_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -126,9 +128,11 @@ class _AppFlowyEditorPageState extends State<_AppFlowyEditorPage> { @override Widget build(BuildContext context) { final theme = Theme.of(context); + final autoFocusParamters = _autoFocusParamters(); final editor = AppFlowyEditor( editorState: editorState, - autoFocus: editorState.document.isEmpty, + autoFocus: autoFocusParamters.value1, + focusedSelection: autoFocusParamters.value2, customBuilders: { // Divider kDividerType: DividerWidgetBuilder(), @@ -144,6 +148,8 @@ class _AppFlowyEditorPageState extends State<_AppFlowyEditorPage> { kCalloutType: CalloutNodeWidgetBuilder(), // Auto Generator, kAutoCompletionInputType: AutoCompletionInputBuilder(), + // Cover + kCoverType: CoverNodeWidgetBuilder(), // Smart Edit, kSmartEditType: SmartEditInputBuilder(), }, @@ -174,10 +180,12 @@ class _AppFlowyEditorPageState extends State<_AppFlowyEditorPage> { // enable open ai features if needed. if (openAIKey != null && openAIKey!.isNotEmpty) ...[ autoGeneratorMenuItem, - ] + ], ], toolbarItems: [ - smartEditItem, + if (openAIKey != null && openAIKey!.isNotEmpty) ...[ + smartEditItem, + ] ], themeData: theme.copyWith(extensions: [ ...theme.extensions.values, @@ -227,4 +235,18 @@ class _AppFlowyEditorPageState extends State<_AppFlowyEditorPage> { await editorState.apply(transaction, withUpdateCursor: false); } } + + dartz.Tuple2 _autoFocusParamters() { + if (editorState.document.isEmpty) { + return dartz.Tuple2(true, Selection.single(path: [0], startOffset: 0)); + } + final texts = editorState.document.root.children.whereType(); + if (texts.every((element) => element.toPlainText().isEmpty)) { + return dartz.Tuple2( + true, + Selection.single(path: texts.first.path, startOffset: 0), + ); + } + return const dartz.Tuple2(false, null); + } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/editor_styles.dart b/frontend/appflowy_flutter/lib/plugins/document/editor_styles.dart index a623db872b..d4e467ab27 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/editor_styles.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/editor_styles.dart @@ -9,7 +9,7 @@ EditorStyle customEditorTheme(BuildContext context) { ? EditorStyle.dark : EditorStyle.light; editorStyle = editorStyle.copyWith( - padding: const EdgeInsets.symmetric(horizontal: 100, vertical: 28), + padding: const EdgeInsets.symmetric(horizontal: 100, vertical: 0), textStyle: editorStyle.textStyle?.copyWith( fontFamily: 'poppins', fontSize: documentStyle.fontSize, diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/cover/change_cover_popover.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/cover/change_cover_popover.dart new file mode 100644 index 0000000000..66957652e3 --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/cover/change_cover_popover.dart @@ -0,0 +1,376 @@ +import 'dart:io'; +import 'dart:ui'; + +import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/document/presentation/plugins/cover/cover_node_widget.dart'; +import 'package:appflowy/startup/startup.dart'; +import 'package:appflowy/util/file_picker/file_picker_service.dart'; +import 'package:appflowy/workspace/application/settings/settings_location_cubit.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:file_picker/file_picker.dart' show FileType; +import 'package:flowy_infra/size.dart'; +import 'package:flowy_infra/theme_extension.dart'; +import 'package:flowy_infra_ui/style_widget/icon_button.dart'; +import 'package:flowy_infra_ui/style_widget/text.dart'; +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +const String kLocalImagesKey = 'local_images'; + +List get builtInAssetImages => [ + "assets/images/app_flowy_abstract_cover_1.jpg", + "assets/images/app_flowy_abstract_cover_2.jpg" + ]; + +class ChangeCoverPopover extends StatefulWidget { + final EditorState editorState; + final Node node; + final Function( + CoverSelectionType selectionType, + String selection, + ) onCoverChanged; + + const ChangeCoverPopover({ + super.key, + required this.editorState, + required this.onCoverChanged, + required this.node, + }); + + @override + State createState() => _ChangeCoverPopoverState(); +} + +class ColorOption { + final String colorHex; + + final String name; + const ColorOption({ + required this.colorHex, + required this.name, + }); +} + +class CoverColorPicker extends StatefulWidget { + final String? selectedBackgroundColorHex; + + final Color pickerBackgroundColor; + final Color pickerItemHoverColor; + final void Function(String color) onSubmittedbackgroundColorHex; + final List backgroundColorOptions; + const CoverColorPicker({ + super.key, + this.selectedBackgroundColorHex, + required this.pickerBackgroundColor, + required this.backgroundColorOptions, + required this.pickerItemHoverColor, + required this.onSubmittedbackgroundColorHex, + }); + + @override + State createState() => _CoverColorPickerState(); +} + +class _ChangeCoverPopoverState extends State { + late Future>? fileImages; + + @override + void initState() { + super.initState(); + fileImages = _getPreviouslyPickedImagePaths(); + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(15), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + FlowyText.semibold(LocaleKeys.document_plugins_cover_colors.tr()), + const SizedBox(height: 10), + _buildColorPickerList(), + const SizedBox(height: 10), + FlowyText.semibold(LocaleKeys.document_plugins_cover_images.tr()), + const SizedBox(height: 10), + _buildFileImagePicker(), + const SizedBox(height: 10), + FlowyText.semibold(LocaleKeys.document_plugins_cover_abstract.tr()), + const SizedBox(height: 10), + _buildAbstractImagePicker(), + ], + ), + ), + ); + } + + Widget _buildAbstractImagePicker() { + return GridView.builder( + shrinkWrap: true, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + childAspectRatio: 1 / 0.65, + crossAxisSpacing: 7, + mainAxisSpacing: 7), + itemCount: builtInAssetImages.length, + itemBuilder: (BuildContext ctx, index) { + return InkWell( + onTap: () { + widget.onCoverChanged( + CoverSelectionType.asset, + builtInAssetImages[index], + ); + }, + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage(builtInAssetImages[index]), + fit: BoxFit.cover, + ), + borderRadius: Corners.s8Border, + ), + ), + ); + }, + ); + } + + Widget _buildColorPickerList() { + return CoverColorPicker( + pickerBackgroundColor: + widget.editorState.editorStyle.selectionMenuBackgroundColor ?? + Colors.white, + pickerItemHoverColor: + widget.editorState.editorStyle.selectionMenuItemSelectedColor ?? + Colors.blue.withOpacity(0.3), + selectedBackgroundColorHex: + widget.node.attributes[kCoverSelectionTypeAttribute] == + CoverSelectionType.color.toString() + ? widget.node.attributes[kCoverSelectionAttribute] + : "ffffff", + backgroundColorOptions: + _generateBackgroundColorOptions(widget.editorState), + onSubmittedbackgroundColorHex: (color) { + widget.onCoverChanged(CoverSelectionType.color, color); + setState(() {}); + }, + ); + } + + Widget _buildFileImagePicker() { + return FutureBuilder>( + future: _getPreviouslyPickedImagePaths(), + builder: (context, snapshot) { + if (snapshot.hasData) { + List images = snapshot.data!; + return GridView.builder( + shrinkWrap: true, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + childAspectRatio: 1 / 0.65, + crossAxisSpacing: 7, + mainAxisSpacing: 7, + ), + itemCount: images.length + 1, + itemBuilder: (BuildContext ctx, index) { + if (index == 0) { + return Container( + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .primary + .withOpacity(0.15), + border: Border.all( + color: Theme.of(context).colorScheme.primary, + ), + borderRadius: Corners.s8Border, + ), + child: FlowyIconButton( + iconPadding: EdgeInsets.zero, + icon: Icon( + Icons.add, + color: Theme.of(context).colorScheme.primary, + ), + width: 20, + onPressed: () { + _pickImages(); + }, + ), + ); + } + return InkWell( + onTap: () { + widget.onCoverChanged( + CoverSelectionType.file, + images[index - 1], + ); + }, + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: FileImage(File(images[index - 1])), + fit: BoxFit.cover, + ), + borderRadius: Corners.s8Border, + ), + ), + ); + }, + ); + } else { + return Container(); + } + }); + } + + List _generateBackgroundColorOptions(EditorState editorState) { + return FlowyTint.values + .map((t) => ColorOption( + colorHex: t.color(context).toHex(), + name: t.tintName(AppFlowyEditorLocalizations.current), + )) + .toList(); + } + + Future> _getPreviouslyPickedImagePaths() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + final imageNames = prefs.getStringList(kLocalImagesKey) ?? []; + final removeNames = []; + for (final name in imageNames) { + if (!File(name).existsSync()) { + removeNames.add(name); + } + } + imageNames.removeWhere((element) => removeNames.contains(element)); + prefs.setStringList(kLocalImagesKey, imageNames); + return imageNames; + } + + Future _pickImages() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + List imageNames = prefs.getStringList(kLocalImagesKey) ?? []; + FilePickerResult? result = await getIt().pickFiles( + dialogTitle: LocaleKeys.document_plugins_cover_addLocalImage.tr(), + allowMultiple: false, + type: FileType.image, + allowedExtensions: ['jpg', 'png', 'jpeg'], + ); + if (result != null && result.files.isNotEmpty) { + final path = result.files.first.path; + if (path != null) { + final directory = await _coverPath(); + final newPath = await File(path).copy( + '$directory/${path.split('/').last}', + ); + imageNames.add(newPath.path); + } + } + await prefs.setStringList(kLocalImagesKey, imageNames); + setState(() {}); + } + + Future _coverPath() async { + final directory = await getIt().fetchLocation(); + return Directory('$directory/covers') + .create(recursive: true) + .then((value) => value.path); + } +} + +class _CoverColorPickerState extends State { + final scrollController = ScrollController(); + + @override + Widget build(BuildContext context) { + return Container( + height: 30, + alignment: Alignment.center, + child: ScrollConfiguration( + behavior: ScrollConfiguration.of(context).copyWith(dragDevices: { + PointerDeviceKind.touch, + PointerDeviceKind.mouse, + }, platform: TargetPlatform.windows), + child: ListView.builder( + controller: scrollController, + shrinkWrap: true, + itemCount: widget.backgroundColorOptions.length, + scrollDirection: Axis.horizontal, + itemBuilder: (context, index) { + return _buildColorItems( + widget.backgroundColorOptions, + widget.selectedBackgroundColorHex, + ); + }, + ), + ), + ); + } + + @override + void dispose() { + super.dispose(); + scrollController.dispose(); + } + + Widget _buildColorItem(ColorOption option, bool isChecked) { + return InkWell( + customBorder: const RoundedRectangleBorder( + borderRadius: Corners.s6Border, + ), + hoverColor: widget.pickerItemHoverColor, + onTap: () { + widget.onSubmittedbackgroundColorHex(option.colorHex); + }, + child: Padding( + padding: const EdgeInsets.only(right: 10.0), + child: SizedBox.square( + dimension: 25, + child: Container( + decoration: BoxDecoration( + color: isChecked + ? Colors.transparent + : Color(int.tryParse(option.colorHex) ?? 0xFFFFFFFF), + border: isChecked + ? Border.all( + color: Color(int.tryParse(option.colorHex) ?? 0xFFFFFF)) + : null, + shape: BoxShape.circle, + ), + child: isChecked + ? SizedBox.square( + dimension: 25, + child: Container( + margin: const EdgeInsets.all(4), + decoration: BoxDecoration( + color: + Color(int.tryParse(option.colorHex) ?? 0xFFFFFFFF), + shape: BoxShape.circle, + ), + ), + ) + : null, + ), + ), + ), + ); + } + + Widget _buildColorItems(List options, String? selectedColor) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: options + .map((e) => _buildColorItem(e, e.colorHex == selectedColor)) + .toList(), + ); + } +} + +extension on Color { + String toHex() { + return '0x${value.toRadixString(16)}'; + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/cover/cover_node_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/cover/cover_node_widget.dart new file mode 100644 index 0000000000..11788cd1d9 --- /dev/null +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/cover/cover_node_widget.dart @@ -0,0 +1,302 @@ +import 'dart:io'; + +import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/document/presentation/plugins/cover/change_cover_popover.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:appflowy_popover/appflowy_popover.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flowy_infra/image.dart'; +import 'package:flowy_infra/size.dart'; +import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flowy_infra_ui/style_widget/button.dart'; +import 'package:flowy_infra_ui/style_widget/icon_button.dart'; +import 'package:flowy_infra_ui/widget/rounded_button.dart'; +import 'package:flutter/material.dart'; + +const String kCoverType = 'cover'; +const String kCoverSelectionTypeAttribute = 'cover_selection_type'; +const String kCoverSelectionAttribute = 'cover_selection'; + +enum CoverSelectionType { + initial, + + color, + file, + asset; + + static CoverSelectionType fromString(String? value) { + if (value == null) { + return CoverSelectionType.initial; + } + return CoverSelectionType.values.firstWhere( + (e) => e.toString() == value, + orElse: () => CoverSelectionType.initial, + ); + } +} + +class CoverNodeWidgetBuilder implements NodeWidgetBuilder { + @override + Widget build(NodeWidgetContext context) { + return _CoverImageNodeWidget( + key: context.node.key, + node: context.node, + editorState: context.editorState, + ); + } + + @override + NodeValidator get nodeValidator => (node) { + return true; + }; +} + +class _CoverImageNodeWidget extends StatefulWidget { + const _CoverImageNodeWidget({ + Key? key, + required this.node, + required this.editorState, + }) : super(key: key); + + final Node node; + final EditorState editorState; + + @override + State<_CoverImageNodeWidget> createState() => _CoverImageNodeWidgetState(); +} + +class _CoverImageNodeWidgetState extends State<_CoverImageNodeWidget> { + CoverSelectionType get selectionType => CoverSelectionType.fromString( + widget.node.attributes[kCoverSelectionTypeAttribute], + ); + + @override + Widget build(BuildContext context) { + if (selectionType == CoverSelectionType.initial) { + return _AddCoverButton( + onTap: () { + _insertCover(CoverSelectionType.asset, builtInAssetImages.first); + }, + ); + } else { + return _CoverImage( + editorState: widget.editorState, + node: widget.node, + onCoverChanged: (type, value) { + _insertCover(type, value); + }, + ); + } + } + + Future _insertCover(CoverSelectionType type, dynamic cover) async { + final transaction = widget.editorState.transaction; + transaction.updateNode(widget.node, { + kCoverSelectionTypeAttribute: type.toString(), + kCoverSelectionAttribute: cover, + }); + return widget.editorState.apply(transaction); + } +} + +class _AddCoverButton extends StatefulWidget { + const _AddCoverButton({ + required this.onTap, + }); + + final VoidCallback onTap; + + @override + State<_AddCoverButton> createState() => _AddCoverButtonState(); +} + +class _AddCoverButtonState extends State<_AddCoverButton> { + bool isHidden = true; + + @override + Widget build(BuildContext context) { + return MouseRegion( + onEnter: (event) { + setHidden(false); + }, + onExit: (event) { + setHidden(true); + }, + child: Container( + height: 50.0, + width: double.infinity, + padding: const EdgeInsets.only(top: 20, bottom: 5), + // color: Colors.red, + child: isHidden + ? const SizedBox() + : Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + // Add Cover Button. + FlowyButton( + leftIconSize: const Size.square(18), + onTap: widget.onTap, + useIntrinsicWidth: true, + leftIcon: svgWidget( + 'editor/image', + color: Theme.of(context).colorScheme.onSurface, + ), + text: FlowyText.regular( + LocaleKeys.document_plugins_cover_addCover.tr(), + ), + ) + // Add Icon Button. + // ... + ], + ), + ), + ); + } + + void setHidden(bool value) { + if (isHidden == value) return; + setState(() { + isHidden = value; + }); + } +} + +class _CoverImage extends StatefulWidget { + const _CoverImage({ + required this.editorState, + required this.node, + required this.onCoverChanged, + }); + + final Node node; + final EditorState editorState; + final Function( + CoverSelectionType selectionType, + dynamic selection, + ) onCoverChanged; + + @override + State<_CoverImage> createState() => _CoverImageState(); +} + +class _CoverImageState extends State<_CoverImage> { + final popoverController = PopoverController(); + + CoverSelectionType get selectionType => CoverSelectionType.fromString( + widget.node.attributes[kCoverSelectionTypeAttribute], + ); + Color get color => + Color(int.tryParse(widget.node.attributes[kCoverSelectionAttribute]) ?? + 0xFFFFFFFF); + + bool isOverlayButtonsHidden = true; + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + _buildCoverImage(context), + _buildCoverOverlayButtons(context), + ], + ); + } + + Widget _buildCoverOverlayButtons(BuildContext context) { + return Positioned( + bottom: 22, + right: 12, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + AppFlowyPopover( + offset: const Offset(-125, 10), + controller: popoverController, + direction: PopoverDirection.bottomWithCenterAligned, + constraints: BoxConstraints.loose(const Size(380, 450)), + margin: EdgeInsets.zero, + child: RoundedTextButton( + onPressed: () { + popoverController.show(); + }, + hoverColor: Theme.of(context).colorScheme.surface, + textColor: Theme.of(context).colorScheme.onSurface, + fillColor: Theme.of(context).colorScheme.surface.withOpacity(0.8), + width: 120, + height: 28, + title: LocaleKeys.document_plugins_cover_changeCover.tr(), + ), + popupBuilder: (BuildContext popoverContext) { + return ChangeCoverPopover( + node: widget.node, + editorState: widget.editorState, + onCoverChanged: widget.onCoverChanged, + ); + }, + ), + const SizedBox(width: 10), + FlowyIconButton( + fillColor: Theme.of(context).colorScheme.surface.withOpacity(0.8), + hoverColor: Theme.of(context).colorScheme.surface, + iconPadding: const EdgeInsets.all(5), + width: 28, + icon: svgWidget( + 'editor/delete', + color: Theme.of(context).colorScheme.onSurface, + ), + onPressed: () { + widget.onCoverChanged(CoverSelectionType.initial, null); + }, + ), + ], + ), + ); + } + + Widget _buildCoverImage(BuildContext context) { + final screenSize = MediaQuery.of(context).size; + const height = 200.0; + final Widget coverImage; + switch (selectionType) { + case CoverSelectionType.file: + coverImage = Image.file( + File(widget.node.attributes[kCoverSelectionAttribute]), + fit: BoxFit.cover, + ); + break; + case CoverSelectionType.asset: + coverImage = Image.asset( + widget.node.attributes[kCoverSelectionAttribute], + fit: BoxFit.cover, + ); + break; + case CoverSelectionType.color: + coverImage = Container( + decoration: BoxDecoration( + color: color, + borderRadius: Corners.s6Border, + ), + alignment: Alignment.center, + ); + break; + case CoverSelectionType.initial: + coverImage = const SizedBox(); // just an empty sizebox + break; + } + return UnconstrainedBox( + child: Container( + padding: const EdgeInsets.only(bottom: 10), + height: height, + width: screenSize.width, + child: coverImage, + ), + ); + } + + void setOverlayButtonsHidden(bool value) { + if (isOverlayButtonsHidden == value) return; + setState(() { + isOverlayButtonsHidden = value; + }); + } +} diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/openai/service/openai_client.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/openai/service/openai_client.dart index f8b3b050fb..3bd41e2d52 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/openai/service/openai_client.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/openai/service/openai_client.dart @@ -37,7 +37,7 @@ abstract class OpenAIRepository { Future> getCompletions({ required String prompt, String? suffix, - int maxTokens = 50, + int maxTokens = 500, double temperature = .3, }); @@ -72,7 +72,7 @@ class HttpOpenAIRepository implements OpenAIRepository { Future> getCompletions({ required String prompt, String? suffix, - int maxTokens = 50, + int maxTokens = 500, double temperature = 0.3, }) async { final parameters = { @@ -102,12 +102,14 @@ class HttpOpenAIRepository implements OpenAIRepository { required String input, required String instruction, double temperature = 0.3, + int n = 1, }) async { final parameters = { 'model': 'text-davinci-edit-001', 'input': input, 'instruction': instruction, 'temperature': temperature, + 'n': n, }; final response = await client.post( diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/openai/widgets/smart_edit_action.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/openai/widgets/smart_edit_action.dart index a7c2ef2626..5ceb4d7ea6 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/openai/widgets/smart_edit_action.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/openai/widgets/smart_edit_action.dart @@ -10,9 +10,9 @@ enum SmartEditAction { String get toInstruction { switch (this) { case SmartEditAction.summarize: - return 'Summarize'; + return 'Make it shorter'; case SmartEditAction.fixSpelling: - return 'Fix the spelling mistakes'; + return 'Fix all the spelling mistakes'; } } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/openai/widgets/smart_edit_node_widget.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/openai/widgets/smart_edit_node_widget.dart index 2ce7267a7e..d98595e0da 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/openai/widgets/smart_edit_node_widget.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/openai/widgets/smart_edit_node_widget.dart @@ -254,6 +254,7 @@ class _SmartEditInputState extends State<_SmartEditInput> { final edits = await openAIRepository.getEdits( input: input, instruction: instruction, + n: input.split('\n').length, ); return edits.fold((error) async { return dartz.Left( diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/openai/widgets/smart_edit_toolbar_item.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/openai/widgets/smart_edit_toolbar_item.dart index 8ec609ef8f..0247413544 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/openai/widgets/smart_edit_toolbar_item.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/plugins/openai/widgets/smart_edit_toolbar_item.dart @@ -42,11 +42,13 @@ class _SmartEditWidgetState extends State<_SmartEditWidget> { .toList(), buildChild: (controller) { return FlowyIconButton( + hoverColor: Colors.transparent, tooltipText: 'Smart Edit', preferBelow: false, icon: const Icon( - Icons.edit, - size: 14, + Icons.lightbulb_outline, + size: 13, + color: Colors.white, ), onPressed: () { controller.show(); @@ -55,39 +57,44 @@ class _SmartEditWidgetState extends State<_SmartEditWidget> { }, onSelected: (action, controller) { controller.close(); - final selection = - widget.editorState.service.selectionService.currentSelection.value; - if (selection == null) { - return; - } - final textNodes = widget - .editorState.service.selectionService.currentSelectedNodes - .whereType() - .toList(growable: false); - final input = widget.editorState.getTextInSelection( - textNodes.normalized, - selection.normalized, - ); - final transaction = widget.editorState.transaction; - transaction.insertNode( - selection.normalized.end.path.next, - Node( - type: kSmartEditType, - attributes: { - kSmartEditInstructionType: action.inner.toInstruction, - kSmartEditInputType: input, - }, - ), - ); - widget.editorState.apply( - transaction, - options: const ApplyOptions( - recordUndo: false, - recordRedo: false, - ), - withUpdateCursor: false, - ); + _insertSmartEditNode(action); }, ); } + + Future _insertSmartEditNode( + SmartEditActionWrapper actionWrapper) async { + final selection = + widget.editorState.service.selectionService.currentSelection.value; + if (selection == null) { + return; + } + final textNodes = widget + .editorState.service.selectionService.currentSelectedNodes + .whereType() + .toList(growable: false); + final input = widget.editorState.getTextInSelection( + textNodes.normalized, + selection.normalized, + ); + final transaction = widget.editorState.transaction; + transaction.insertNode( + selection.normalized.end.path.next, + Node( + type: kSmartEditType, + attributes: { + kSmartEditInstructionType: actionWrapper.inner.toInstruction, + kSmartEditInputType: input, + }, + ), + ); + return widget.editorState.apply( + transaction, + options: const ApplyOptions( + recordUndo: false, + recordRedo: false, + ), + withUpdateCursor: false, + ); + } } diff --git a/frontend/appflowy_flutter/lib/startup/deps_resolver.dart b/frontend/appflowy_flutter/lib/startup/deps_resolver.dart index ab0ed1a092..f0a34ff713 100644 --- a/frontend/appflowy_flutter/lib/startup/deps_resolver.dart +++ b/frontend/appflowy_flutter/lib/startup/deps_resolver.dart @@ -1,4 +1,5 @@ import 'package:appflowy/core/network_monitor.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; import 'package:appflowy/plugins/database_view/application/field/field_action_sheet_bloc.dart'; import 'package:appflowy/plugins/database_view/application/field/field_controller.dart'; diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/add_button.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/add_button.dart index 5c6c7aa32f..5d1bfee6bc 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/add_button.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/add_button.dart @@ -1,9 +1,10 @@ import 'package:appflowy/plugins/document/document.dart'; +import 'package:appflowy/plugins/document/presentation/plugins/cover/cover_node_widget.dart'; import 'package:appflowy/startup/plugin/plugin.dart'; import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/workspace/presentation/home/menu/app/header/import/import_panel.dart'; import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart'; -import 'package:appflowy_editor/appflowy_editor.dart' show Document; +import 'package:appflowy_editor/appflowy_editor.dart' show Document, Node; import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra_ui/style_widget/icon_button.dart'; @@ -60,7 +61,12 @@ class AddButton extends StatelessWidget { }, onSelected: (action, controller) { if (action is AddButtonActionWrapper) { - onSelected(action.pluginBuilder, null); + Document? document; + if (action.pluginType == PluginType.editor) { + // initialize the document if needed. + document = buildInitialDocument(); + } + onSelected(action.pluginBuilder, document); } if (action is ImportActionWrapper) { showImportPanel(context, (document) { @@ -74,6 +80,12 @@ class AddButton extends StatelessWidget { }, ); } + + Document buildInitialDocument() { + final document = Document.empty(); + document.insert([0], [Node(type: kCoverType)]); + return document; + } } class AddButtonActionWrapper extends ActionCell { @@ -87,6 +99,8 @@ class AddButtonActionWrapper extends ActionCell { @override String get name => pluginBuilder.menuName; + + PluginType get pluginType => pluginBuilder.pluginType; } class ImportActionWrapper extends ActionCell { diff --git a/frontend/appflowy_flutter/packages/appflowy_backend/windows/appflowy_backend_plugin.cpp b/frontend/appflowy_flutter/packages/appflowy_backend/windows/appflowy_backend_plugin.cpp index a7928b31e1..701547e022 100644 --- a/frontend/appflowy_flutter/packages/appflowy_backend/windows/appflowy_backend_plugin.cpp +++ b/frontend/appflowy_flutter/packages/appflowy_backend/windows/appflowy_backend_plugin.cpp @@ -1,4 +1,3 @@ -#include "include/appflowy_backend/appflowy_flutter_backend_plugin.h" // This must be included before many other Windows headers. #include @@ -13,70 +12,85 @@ #include #include #include +#include "include/appflowy_backend/app_flowy_backend_plugin.h" -namespace { +namespace +{ -class AppFlowyBackendPlugin : public flutter::Plugin { - public: - static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar); + class AppFlowyBackendPlugin : public flutter::Plugin + { + public: + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar); - AppFlowyBackendPlugin(); + AppFlowyBackendPlugin(); - virtual ~AppFlowyBackendPlugin(); + virtual ~AppFlowyBackendPlugin(); - private: - // Called when a method is called on this plugin's channel from Dart. - void HandleMethodCall( - const flutter::MethodCall &method_call, - std::unique_ptr> result); -}; + private: + // Called when a method is called on this plugin's channel from Dart. + void HandleMethodCall( + const flutter::MethodCall &method_call, + std::unique_ptr> result); + }; -// static -void AppFlowyBackendPlugin::RegisterWithRegistrar( - flutter::PluginRegistrarWindows *registrar) { - auto channel = - std::make_unique>( - registrar->messenger(), "appflowy_backend", - &flutter::StandardMethodCodec::GetInstance()); + // static + void AppFlowyBackendPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarWindows *registrar) + { + auto channel = + std::make_unique>( + registrar->messenger(), "appflowy_backend", + &flutter::StandardMethodCodec::GetInstance()); - auto plugin = std::make_unique(); + auto plugin = std::make_unique(); - channel->SetMethodCallHandler( - [plugin_pointer = plugin.get()](const auto &call, auto result) { - plugin_pointer->HandleMethodCall(call, std::move(result)); - }); + channel->SetMethodCallHandler( + [plugin_pointer = plugin.get()](const auto &call, auto result) + { + plugin_pointer->HandleMethodCall(call, std::move(result)); + }); - registrar->AddPlugin(std::move(plugin)); -} - -AppFlowyBackendPlugin::AppFlowyBackendPlugin() {} - -AppFlowyBackendPlugin::~AppFlowyBackendPlugin() {} - -void AppFlowyBackendPlugin::HandleMethodCall( - const flutter::MethodCall &method_call, - std::unique_ptr> result) { - if (method_call.method_name().compare("getPlatformVersion") == 0) { - std::ostringstream version_stream; - version_stream << "Windows "; - if (IsWindows10OrGreater()) { - version_stream << "10+"; - } else if (IsWindows8OrGreater()) { - version_stream << "8"; - } else if (IsWindows7OrGreater()) { - version_stream << "7"; - } - result->Success(flutter::EncodableValue(version_stream.str())); - } else { - result->NotImplemented(); + registrar->AddPlugin(std::move(plugin)); } -} -} // namespace + AppFlowyBackendPlugin::AppFlowyBackendPlugin() {} + + AppFlowyBackendPlugin::~AppFlowyBackendPlugin() {} + + void AppFlowyBackendPlugin::HandleMethodCall( + const flutter::MethodCall &method_call, + std::unique_ptr> result) + { + if (method_call.method_name().compare("getPlatformVersion") == 0) + { + std::ostringstream version_stream; + version_stream << "Windows "; + if (IsWindows10OrGreater()) + { + version_stream << "10+"; + } + else if (IsWindows8OrGreater()) + { + version_stream << "8"; + } + else if (IsWindows7OrGreater()) + { + version_stream << "7"; + } + result->Success(flutter::EncodableValue(version_stream.str())); + } + else + { + result->NotImplemented(); + } + } + +} // namespace void AppFlowyBackendPluginRegisterWithRegistrar( - FlutterDesktopPluginRegistrarRef registrar) { + FlutterDesktopPluginRegistrarRef registrar) +{ AppFlowyBackendPlugin::RegisterWithRegistrar( flutter::PluginRegistrarManager::GetInstance() ->GetRegistrar(registrar)); -} +} \ No newline at end of file diff --git a/frontend/appflowy_flutter/packages/appflowy_backend/windows/appflowy_backend_plugin_c_api.cpp b/frontend/appflowy_flutter/packages/appflowy_backend/windows/appflowy_backend_plugin_c_api.cpp index 1902de1c91..58d345008b 100644 --- a/frontend/appflowy_flutter/packages/appflowy_backend/windows/appflowy_backend_plugin_c_api.cpp +++ b/frontend/appflowy_flutter/packages/appflowy_backend/windows/appflowy_backend_plugin_c_api.cpp @@ -5,8 +5,9 @@ #include "appflowy_flutter_backend_plugin.h" void AppFlowyBackendPluginCApiRegisterWithRegistrar( - FlutterDesktopPluginRegistrarRef registrar) { - appflowy_backend::AppFlowyBackendPlugin::RegisterWithRegistrar( - flutter::PluginRegistrarManager::GetInstance() - ->GetRegistrar(registrar)); + FlutterDesktopPluginRegistrarRef registrar) +{ + appflowy_backend::AppFlowyBackendPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); } diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/commands/command_extension.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/commands/command_extension.dart index e383cb1d1d..5a9ea4f4bd 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/commands/command_extension.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/commands/command_extension.dart @@ -59,12 +59,18 @@ extension CommandExtension on EditorState { List res = []; if (!selection.isCollapsed) { for (var i = 0; i < textNodes.length; i++) { + final plainText = textNodes[i].toPlainText(); if (i == 0) { - res.add(textNodes[i].toPlainText().substring(selection.startIndex)); + res.add( + plainText.substring( + selection.startIndex, + plainText.length, + ), + ); } else if (i == textNodes.length - 1) { - res.add(textNodes[i].toPlainText().substring(0, selection.endIndex)); + res.add(plainText.substring(0, selection.endIndex)); } else { - res.add(textNodes[i].toPlainText()); + res.add(plainText); } } } diff --git a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/editor_service.dart b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/editor_service.dart index cd045a0582..9fcfcfd108 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/editor_service.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor/lib/src/service/editor_service.dart @@ -32,6 +32,7 @@ class AppFlowyEditor extends StatefulWidget { this.toolbarItems = const [], this.editable = true, this.autoFocus = false, + this.focusedSelection, this.customActionMenuBuilder, ThemeData? themeData, }) : super(key: key) { @@ -60,6 +61,7 @@ class AppFlowyEditor extends StatefulWidget { /// Set the value to true to focus the editor on the start of the document. final bool autoFocus; + final Selection? focusedSelection; final Positioned Function(BuildContext context, List items)? customActionMenuBuilder; @@ -89,7 +91,8 @@ class _AppFlowyEditorState extends State { WidgetsBinding.instance.addPostFrameCallback((timeStamp) { if (widget.editable && widget.autoFocus) { editorState.service.selectionService.updateSelection( - Selection.single(path: [0], startOffset: 0), + widget.focusedSelection ?? + Selection.single(path: [0], startOffset: 0), ); } }); diff --git a/frontend/appflowy_flutter/packages/appflowy_editor_plugins/lib/appflowy_editor_plugins.dart b/frontend/appflowy_flutter/packages/appflowy_editor_plugins/lib/appflowy_editor_plugins.dart index 7dbf2eb000..2a422eaf97 100644 --- a/frontend/appflowy_flutter/packages/appflowy_editor_plugins/lib/appflowy_editor_plugins.dart +++ b/frontend/appflowy_flutter/packages/appflowy_editor_plugins/lib/appflowy_editor_plugins.dart @@ -12,3 +12,5 @@ export 'src/divider/divider_shortcut_event.dart'; export 'src/emoji_picker/emoji_menu_item.dart'; // Math Equation export 'src/math_ equation/math_equation_node_widget.dart'; + +export 'src/extensions/theme_extension.dart'; diff --git a/frontend/appflowy_flutter/test/bloc_test/board_test/group_by_multi_select_field_test.dart b/frontend/appflowy_flutter/test/bloc_test/board_test/group_by_multi_select_field_test.dart index d15a846b4e..af8a06d06d 100644 --- a/frontend/appflowy_flutter/test/bloc_test/board_test/group_by_multi_select_field_test.dart +++ b/frontend/appflowy_flutter/test/bloc_test/board_test/group_by_multi_select_field_test.dart @@ -1,4 +1,4 @@ -import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database_view/application/setting/group_bloc.dart'; import 'package:appflowy/plugins/database_view/board/application/board_bloc.dart'; import 'package:appflowy/plugins/database_view/grid/application/cell/select_option_editor_bloc.dart'; diff --git a/frontend/appflowy_flutter/test/bloc_test/board_test/util.dart b/frontend/appflowy_flutter/test/bloc_test/board_test/util.dart index fc6fe8c617..885c9bd6f4 100644 --- a/frontend/appflowy_flutter/test/bloc_test/board_test/util.dart +++ b/frontend/appflowy_flutter/test/bloc_test/board_test/util.dart @@ -1,4 +1,5 @@ -import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database_view/application/field/field_controller.dart'; import 'package:appflowy/plugins/database_view/application/field/field_editor_bloc.dart'; import 'package:appflowy/plugins/database_view/application/field/field_service.dart'; @@ -105,11 +106,9 @@ class BoardTestContext { ) async { final RowInfo rowInfo = rowInfos.last; final rowCache = _boardDataController.rowCache; - final fieldController = _boardDataController.fieldController; final rowDataController = RowDataController( rowInfo: rowInfo, - fieldController: fieldController, rowCache: rowCache, ); @@ -122,7 +121,6 @@ class BoardTestContext { return CellControllerBuilder( cellId: rowBloc.state.cellByFieldId[fieldId]!, cellCache: rowCache.cellCache, - delegate: rowDataController, ); } diff --git a/frontend/appflowy_flutter/test/bloc_test/grid_test/filter/create_filter_test.dart b/frontend/appflowy_flutter/test/bloc_test/grid_test/filter/create_filter_test.dart index a9d0ae30ea..4b66bec059 100644 --- a/frontend/appflowy_flutter/test/bloc_test/grid_test/filter/create_filter_test.dart +++ b/frontend/appflowy_flutter/test/bloc_test/grid_test/filter/create_filter_test.dart @@ -44,7 +44,7 @@ void main() { ); await gridResponseFuture(); - assert(context.fieldController.filterInfos.isEmpty); + expect(context.fieldController.filterInfos.length, 0); }); test('filter rows with condition: text is empty', () async { @@ -53,7 +53,7 @@ void main() { final gridController = DatabaseController(view: context.gridView); final gridBloc = GridBloc( view: context.gridView, - gridController: gridController, + databaseController: gridController, )..add(const GridEvent.initial()); await gridResponseFuture(); @@ -64,7 +64,7 @@ void main() { content: ""); await gridResponseFuture(); - assert(gridBloc.state.rowInfos.length == 3); + expect(gridBloc.state.rowInfos.length, 3); }); test('filter rows with condition: text is empty(After edit the row)', @@ -74,7 +74,7 @@ void main() { final gridController = DatabaseController(view: context.gridView); final gridBloc = GridBloc( view: context.gridView, - gridController: gridController, + databaseController: gridController, )..add(const GridEvent.initial()); await gridResponseFuture(); @@ -115,7 +115,7 @@ void main() { final gridController = DatabaseController(view: context.gridView); final gridBloc = GridBloc( view: context.gridView, - gridController: gridController, + databaseController: gridController, )..add(const GridEvent.initial()); await gridResponseFuture(); @@ -134,7 +134,7 @@ void main() { final gridController = DatabaseController(view: context.gridView); final gridBloc = GridBloc( view: context.gridView, - gridController: gridController, + databaseController: gridController, )..add(const GridEvent.initial()); await gridResponseFuture(); diff --git a/frontend/appflowy_flutter/test/bloc_test/grid_test/grid_bloc_test.dart b/frontend/appflowy_flutter/test/bloc_test/grid_test/grid_bloc_test.dart index 6250a8dced..5065406a99 100644 --- a/frontend/appflowy_flutter/test/bloc_test/grid_test/grid_bloc_test.dart +++ b/frontend/appflowy_flutter/test/bloc_test/grid_test/grid_bloc_test.dart @@ -20,7 +20,7 @@ void main() { "create a row", build: () => GridBloc( view: context.gridView, - gridController: DatabaseController(view: context.gridView)) + databaseController: DatabaseController(view: context.gridView)) ..add(const GridEvent.initial()), act: (bloc) => bloc.add(const GridEvent.createRow()), wait: const Duration(milliseconds: 300), @@ -33,7 +33,7 @@ void main() { "delete the last row", build: () => GridBloc( view: context.gridView, - gridController: DatabaseController(view: context.gridView)) + databaseController: DatabaseController(view: context.gridView)) ..add(const GridEvent.initial()), act: (bloc) async { await gridResponseFuture(); diff --git a/frontend/appflowy_flutter/test/bloc_test/grid_test/util.dart b/frontend/appflowy_flutter/test/bloc_test/grid_test/util.dart index a15592e59a..e81c233155 100644 --- a/frontend/appflowy_flutter/test/bloc_test/grid_test/util.dart +++ b/frontend/appflowy_flutter/test/bloc_test/grid_test/util.dart @@ -1,4 +1,5 @@ -import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller.dart'; +import 'package:appflowy/plugins/database_view/application/cell/cell_controller_builder.dart'; import 'package:appflowy/plugins/database_view/application/field/field_controller.dart'; import 'package:appflowy/plugins/database_view/application/field/field_editor_bloc.dart'; import 'package:appflowy/plugins/database_view/application/field/field_service.dart'; @@ -66,11 +67,9 @@ class GridTestContext { ) async { final RowInfo rowInfo = rowInfos[rowIndex]; final rowCache = gridController.rowCache; - final fieldController = gridController.fieldController; final rowDataController = RowDataController( rowInfo: rowInfo, - fieldController: fieldController, rowCache: rowCache, ); @@ -83,7 +82,6 @@ class GridTestContext { return CellControllerBuilder( cellId: rowBloc.state.cellByFieldId[fieldId]!, cellCache: rowCache.cellCache, - delegate: rowDataController, ); } diff --git a/frontend/appflowy_tauri/src/appflowy_app/App.tsx b/frontend/appflowy_tauri/src/appflowy_app/App.tsx index 38342048ab..efb4736264 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/App.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/App.tsx @@ -1,7 +1,6 @@ import { Routes, Route, BrowserRouter } from 'react-router-dom'; import { TestColors } from './components/TestColors/TestColors'; -import { Welcome } from './views/Welcome'; import { Provider } from 'react-redux'; import { store } from './stores/store'; import { DocumentPage } from './views/DocumentPage'; @@ -29,7 +28,6 @@ const App = () => { } /> } /> } /> - } /> }> }> diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/launch_splash.jpg b/frontend/appflowy_tauri/src/appflowy_app/assets/launch_splash.jpg new file mode 100644 index 0000000000..7e3bb9cee6 Binary files /dev/null and b/frontend/appflowy_tauri/src/appflowy_app/assets/launch_splash.jpg differ diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/TestAPI.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/TestAPI.tsx index b5354da161..e7057042f1 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/TestAPI.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/TestAPI.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import TestApiButton from './TestApiButton'; import { TestCreateGrid, TestCreateNewField, @@ -18,7 +17,7 @@ export const TestAPI = () => { return (
    - + {/**/} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/TestGrid.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/TestGrid.tsx index b4099252ce..6b1eb26c2f 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/TestGrid.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/TestGrid.tsx @@ -126,8 +126,10 @@ export const TestCreateSelectOptionInCell = () => { ); await cellController.subscribeChanged({ onCellChanged: (value) => { - const option: SelectOptionCellDataPB = value.unwrap(); - console.log(option); + if (value.some) { + const option: SelectOptionCellDataPB = value.unwrap(); + console.log(option); + } }, }); const backendSvc = new SelectOptionCellBackendService(cellController.cellIdentifier); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/ProtectedRoutes.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/auth/ProtectedRoutes.tsx index 4904194cb9..eb8cdac5a2 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/auth/ProtectedRoutes.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/auth/ProtectedRoutes.tsx @@ -1,16 +1,56 @@ -import { Navigate, Outlet, useLocation } from 'react-router-dom'; +import { Outlet } from 'react-router-dom'; import { useAuth } from './auth.hooks'; import { Screen } from '../layout/Screen'; +import { useEffect, useState } from 'react'; +import { GetStarted } from './GetStarted/GetStarted'; +import { AppflowyLogo } from '../_shared/svg/AppflowyLogo'; + export const ProtectedRoutes = () => { - const location = useLocation(); - const { currentUser } = useAuth(); + const { currentUser, checkUser } = useAuth(); + const [isLoading, setIsLoading] = useState(true); - return currentUser.isAuthenticated ? ( - - - - ) : ( - + useEffect(() => { + void checkUser().then(async (result) => { + await new Promise(() => + setTimeout(() => { + setIsLoading(false); + }, 1200) + ); + + if (result.err) { + throw new Error(result.val.msg); + } + + }); + }, []); + + if (isLoading) { + // It's better to make a fading effect to disappear the loading page + return ; + } else { + return ; + } +}; + +const StartLoading = () => { + return ( +
    +
    + +
    +
    ); }; + +const SplashScreen = ({ isAuthenticated }: { isAuthenticated: boolean }) => { + if (isAuthenticated) { + return ( + + + + ); + } else { + return ; + } +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts index 354bf29a57..21be5aa783 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts @@ -1,20 +1,46 @@ import { currentUserActions } from '../../stores/reducers/current-user/slice'; import { useAppDispatch, useAppSelector } from '../../stores/store'; import { UserProfilePB } from '../../../services/backend/events/flowy-user'; -import { AuthBackendService } from '../../stores/effects/user/user_bd_svc'; +import { AuthBackendService, UserBackendService } from '../../stores/effects/user/user_bd_svc'; import { FolderEventReadCurrentWorkspace } from '../../../services/backend/events/flowy-folder'; import { WorkspaceSettingPB } from '../../../services/backend/models/flowy-folder/workspace'; +import { Log } from '../../utils/log'; export const useAuth = () => { const dispatch = useAppDispatch(); const currentUser = useAppSelector((state) => state.currentUser); const authBackendService = new AuthBackendService(); + async function checkUser() { + const result = await UserBackendService.checkUser(); + if (result.ok) { + const userProfile = result.val; + const workspaceSetting = await _openWorkspace().then((r) => { + if (r.ok) { + return r.val; + } else { + return undefined; + } + }); + dispatch( + currentUserActions.checkUser({ + id: userProfile.id, + token: userProfile.token, + email: userProfile.email, + displayName: userProfile.name, + isAuthenticated: true, + workspaceSetting: workspaceSetting, + }) + ); + } + return result; + } + async function register(email: string, password: string, name: string): Promise { const authResult = await authBackendService.signUp({ email, password, name }); if (authResult.ok) { - const { id, token } = authResult.val; + const userProfile = authResult.val; // Get the workspace setting after user registered. The workspace setting // contains the latest visiting view and the current workspace data. const openWorkspaceResult = await _openWorkspace(); @@ -22,10 +48,10 @@ export const useAuth = () => { const workspaceSetting: WorkspaceSettingPB = openWorkspaceResult.val; dispatch( currentUserActions.updateUser({ - id: id, - token: token, - email, - displayName: name, + id: userProfile.id, + token: userProfile.token, + email: userProfile.email, + displayName: userProfile.name, isAuthenticated: true, workspaceSetting: workspaceSetting, }) @@ -33,7 +59,7 @@ export const useAuth = () => { } return authResult.val; } else { - console.error(authResult.val.msg); + Log.error(authResult.val.msg); throw new Error(authResult.val.msg); } } @@ -53,7 +79,7 @@ export const useAuth = () => { ); return result.val; } else { - console.error(result.val.msg); + Log.error(result.val.msg); throw new Error(result.val.msg); } } @@ -67,5 +93,5 @@ export const useAuth = () => { return FolderEventReadCurrentWorkspace(); } - return { currentUser, register, login, logout }; + return { currentUser, checkUser, register, login, logout }; }; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavigationFloatingPanel.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavigationFloatingPanel.tsx index 7ab84ab905..bada0dcc4e 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavigationFloatingPanel.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavigationFloatingPanel.tsx @@ -1,5 +1,5 @@ import { AppLogo } from '../AppLogo'; -import { Workspace } from '../Workspace'; +import { WorkspaceUser } from '../WorkspaceUser'; import { FolderItem } from './FolderItem'; import { PluginsButton } from './PluginsButton'; import { TrashButton } from './TrashButton'; @@ -52,7 +52,7 @@ export const NavigationFloatingPanel = ({
    - +
    {folders.map((folder, index) => ( diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavigationPanel.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavigationPanel.tsx index 119baf795f..44f79be47e 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavigationPanel.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavigationPanel.tsx @@ -1,12 +1,13 @@ -import { Workspace } from '../Workspace'; +import { WorkspaceUser } from '../WorkspaceUser'; import { AppLogo } from '../AppLogo'; import { FolderItem } from './FolderItem'; -import { PluginsButton } from './PluginsButton'; import { TrashButton } from './TrashButton'; import { NewFolderButton } from './NewFolderButton'; import { NavigationResizer } from './NavigationResizer'; import { IFolder } from '../../../stores/reducers/folders/slice'; import { IPage } from '../../../stores/reducers/pages/slice'; +import { useNavigate } from 'react-router-dom'; +import React from 'react'; export const NavigationPanel = ({ onCollapseNavigationClick, @@ -26,27 +27,22 @@ export const NavigationPanel = ({
    - - - -
    - {folders.map((folder, index) => ( - page.folderId === folder.id)} - onPageClick={onPageClick} - > - ))} -
    + +
    - + {/**/} + + + + + {/*Trash Button*/}
    + {/*New Folder Button*/}
    @@ -54,3 +50,47 @@ export const NavigationPanel = ({ ); }; + +type AppsContext = { + folders: IFolder[]; + pages: IPage[]; + onPageClick: (page: IPage) => void; +}; + +const WorkspaceApps: React.FC = ({ folders, pages, onPageClick }) => ( +
    + {folders.map((folder, index) => ( + page.folderId === folder.id)} + onPageClick={onPageClick} + > + ))} +
    +); + +export const TestBackendButton = () => { + const navigate = useNavigate(); + return ( + + ); +}; + +export const DesignSpec = () => { + const navigate = useNavigate(); + + return ( + + ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/Workspace.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceUser.tsx similarity index 93% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/Workspace.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceUser.tsx index 1cb35ea85c..c85e1c757b 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/Workspace.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceUser.tsx @@ -1,6 +1,6 @@ import { useAppSelector } from '../../stores/store'; -export const Workspace = () => { +export const WorkspaceUser = () => { const currentUser = useAppSelector((state) => state.currentUser); return ( diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_controller.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_controller.ts index 853bc2f006..1e5d45a9b7 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_controller.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_controller.ts @@ -13,7 +13,7 @@ type Callbacks = { onCellChanged: (value: Option) => void; onFieldChanged? export class CellController { private fieldBackendService: FieldBackendService; - private cellDataNotifier: CellDataNotifier>; + private cellDataNotifier: CellDataNotifier; private cellObserver: CellObserver; private readonly cacheKey: CellCacheKey; private readonly fieldNotifier: DatabaseFieldObserver; @@ -59,7 +59,7 @@ export class CellController { this.subscribeCallbacks = callbacks; this.cellDataNotifier.observer.subscribe((cellData) => { if (cellData !== null) { - callbacks.onCellChanged(cellData); + callbacks.onCellChanged(Some(cellData)); } }); }; @@ -95,8 +95,11 @@ export class CellController { private _loadCellData = () => { return this.cellDataLoader.loadData().then((result) => { if (result.ok) { - this.cellCache.insert(this.cacheKey, result.val); - this.cellDataNotifier.cellData = Some(result.val); + const cellData = result.val; + if (cellData.some) { + this.cellCache.insert(this.cacheKey, cellData.val); + this.cellDataNotifier.cellData = cellData; + } } else { this.cellCache.remove(this.cacheKey); this.cellDataNotifier.cellData = None; @@ -110,10 +113,10 @@ export class CellController { }; } -class CellDataNotifier extends ChangeNotifier { +class CellDataNotifier extends ChangeNotifier { _cellData: Option; - constructor(cellData: T) { + constructor(cellData: Option) { super(); this._cellData = Some(cellData); } diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_bd_svc.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_bd_svc.ts index c681cd5330..274a24b44c 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_bd_svc.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_bd_svc.ts @@ -1,5 +1,6 @@ import { nanoid } from '@reduxjs/toolkit'; import { + UserEventCheckUser, UserEventGetUserProfile, UserEventSignIn, UserEventSignOut, @@ -29,6 +30,10 @@ export class UserBackendService { return UserEventGetUserProfile(); }; + static checkUser = () => { + return UserEventCheckUser(); + }; + updateUserProfile = (params: { name?: string; password?: string; email?: string; openAIKey?: string }) => { const payload = UpdateUserProfilePayloadPB.fromObject({ id: this.userId }); diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/current-user/slice.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/current-user/slice.ts index b800297ed8..969925bdc3 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/current-user/slice.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/current-user/slice.ts @@ -12,17 +12,16 @@ export interface ICurrentUser { } const initialState: ICurrentUser | null = { - id: nanoid(8), - displayName: 'Me 😃', - email: `${nanoid(4)}@gmail.com`, - token: nanoid(8), - isAuthenticated: true, + isAuthenticated: false, }; export const currentUserSlice = createSlice({ name: 'currentUser', initialState: initialState, reducers: { + checkUser: (state, action: PayloadAction) => { + return action.payload; + }, updateUser: (state, action: PayloadAction) => { return action.payload; }, diff --git a/frontend/appflowy_tauri/src/appflowy_app/views/Welcome.tsx b/frontend/appflowy_tauri/src/appflowy_app/views/Welcome.tsx deleted file mode 100644 index 2e821cf931..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/views/Welcome.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { Link } from 'react-router-dom'; - -export const Welcome = () => { - return ( -
    -
    Welcome
    -
    - Color Palette -
    -
    - Testing API -
    -
    - ); -}; diff --git a/frontend/rust-lib/flowy-codegen/src/protobuf_file/mod.rs b/frontend/rust-lib/flowy-codegen/src/protobuf_file/mod.rs index 85794ae3c5..de0209cf49 100644 --- a/frontend/rust-lib/flowy-codegen/src/protobuf_file/mod.rs +++ b/frontend/rust-lib/flowy-codegen/src/protobuf_file/mod.rs @@ -132,7 +132,7 @@ fn generate_ts_protobuf_files( }; if result.is_err() { - panic!("Generate dart pb file failed with: {}, {:?}", path, result) + panic!("Generate ts pb file failed with: {}, {:?}", path, result) }; }); diff --git a/frontend/rust-lib/flowy-core/src/lib.rs b/frontend/rust-lib/flowy-core/src/lib.rs index d096c65e60..08ce100d7f 100644 --- a/frontend/rust-lib/flowy-core/src/lib.rs +++ b/frontend/rust-lib/flowy-core/src/lib.rs @@ -107,7 +107,7 @@ fn create_log_filter(level: String, with_crates: Vec) -> String { #[cfg(feature = "profiling")] filters.push(format!("runtime={}", level)); - filters.push(format!("tokio=trace,runtime=trace")); + filters.join(",") } diff --git a/frontend/rust-lib/flowy-database/src/event_handler.rs b/frontend/rust-lib/flowy-database/src/event_handler.rs index fdf08123dd..638c67f0fc 100644 --- a/frontend/rust-lib/flowy-database/src/event_handler.rs +++ b/frontend/rust-lib/flowy-database/src/event_handler.rs @@ -534,8 +534,8 @@ pub(crate) async fn get_groups_handler( ) -> DataResult { let params: DatabaseViewIdPB = data.into_inner(); let editor = manager.get_database_editor(¶ms.value).await?; - let group = editor.load_groups(¶ms.value).await?; - data_result_ok(group) + let groups = editor.load_groups(¶ms.value).await?; + data_result_ok(groups) } #[tracing::instrument(level = "debug", skip(data, manager), err)] diff --git a/frontend/rust-lib/flowy-database/src/manager.rs b/frontend/rust-lib/flowy-database/src/manager.rs index db0d894efe..44f6a761cd 100644 --- a/frontend/rust-lib/flowy-database/src/manager.rs +++ b/frontend/rust-lib/flowy-database/src/manager.rs @@ -203,28 +203,45 @@ impl DatabaseManager { database_id: &str, view_id: &str, ) -> FlowyResult> { - if let Some(database_editor) = self.editors_by_database_id.read().await.get(database_id) { - let user_id = self.database_user.user_id()?; - let (view_pad, view_rev_manager) = - make_database_view_revision_pad(view_id, self.database_user.clone()).await?; - - let view_editor = DatabaseViewEditor::from_pad( + let user = self.database_user.clone(); + let create_view_editor = |database_editor: Arc| async move { + let user_id = user.user_id()?; + let (view_pad, view_rev_manager) = make_database_view_revision_pad(view_id, user).await?; + return DatabaseViewEditor::from_pad( &user_id, database_editor.database_view_data.clone(), database_editor.cell_data_cache.clone(), view_rev_manager, view_pad, ) - .await?; - database_editor.open_view_editor(view_editor).await; - return Ok(database_editor.clone()); - } - // Lock the database_editors - let mut editors_by_database_id = self.editors_by_database_id.write().await; - let db_pool = self.database_user.db_pool()?; - let editor = self.make_database_rev_editor(view_id, db_pool).await?; - editors_by_database_id.insert(database_id.to_string(), editor.clone()); - Ok(editor) + .await; + }; + + let database_editor = self + .editors_by_database_id + .read() + .await + .get(database_id) + .cloned(); + + return match database_editor { + None => { + let mut editors_by_database_id = self.editors_by_database_id.write().await; + let db_pool = self.database_user.db_pool()?; + let database_editor = self.make_database_rev_editor(view_id, db_pool).await?; + editors_by_database_id.insert(database_id.to_string(), database_editor.clone()); + Ok(database_editor) + }, + Some(database_editor) => { + let is_open = database_editor.is_view_open(view_id).await; + if !is_open { + let database_view_editor = create_view_editor(database_editor.clone()).await?; + database_editor.open_view_editor(database_view_editor).await; + } + + Ok(database_editor) + }, + }; } #[tracing::instrument(level = "trace", skip(self, pool), err)] diff --git a/frontend/rust-lib/flowy-database/src/services/database/database_editor.rs b/frontend/rust-lib/flowy-database/src/services/database/database_editor.rs index 701ff72a98..c2740556e9 100644 --- a/frontend/rust-lib/flowy-database/src/services/database/database_editor.rs +++ b/frontend/rust-lib/flowy-database/src/services/database/database_editor.rs @@ -131,6 +131,9 @@ impl DatabaseEditor { self.database_views.number_of_views().await } + pub async fn is_view_open(&self, view_id: &str) -> bool { + self.database_views.is_view_exist(view_id).await + } /// Save the type-option data to disk and send a `DatabaseNotification::DidUpdateField` notification /// to dart side. /// diff --git a/frontend/rust-lib/flowy-database/src/services/database_view/editor_manager.rs b/frontend/rust-lib/flowy-database/src/services/database_view/editor_manager.rs index 3d68613a4e..303d54a681 100644 --- a/frontend/rust-lib/flowy-database/src/services/database_view/editor_manager.rs +++ b/frontend/rust-lib/flowy-database/src/services/database_view/editor_manager.rs @@ -1,3 +1,4 @@ +#![allow(clippy::while_let_loop)] use crate::entities::{ AlterFilterParams, AlterSortParams, CreateRowParams, DatabaseViewSettingPB, DeleteFilterParams, DeleteGroupParams, DeleteSortParams, InsertGroupParams, MoveGroupParams, RepeatedGroupPB, RowPB, @@ -60,12 +61,9 @@ impl DatabaseViews { } pub async fn close(&self, view_id: &str) { - if let Ok(mut view_editors) = self.view_editors.try_write() { - if let Some(view_editor) = view_editors.remove(view_id) { - view_editor.close().await; - } - } else { - tracing::error!("Try to get the lock of view_editors failed"); + if let Some(view_editor) = self.view_editors.write().await.remove(view_id) { + view_editor.close().await; + } } @@ -73,6 +71,10 @@ impl DatabaseViews { self.view_editors.read().await.values().len() } + pub async fn is_view_exist(&self, view_id: &str) -> bool { + self.view_editors.read().await.get(view_id).is_some() + } + pub async fn subscribe_view_changed( &self, view_id: &str, diff --git a/frontend/rust-lib/flowy-database/src/services/database_view/notifier.rs b/frontend/rust-lib/flowy-database/src/services/database_view/notifier.rs index 9133ef2aa7..8020aea218 100644 --- a/frontend/rust-lib/flowy-database/src/services/database_view/notifier.rs +++ b/frontend/rust-lib/flowy-database/src/services/database_view/notifier.rs @@ -1,3 +1,4 @@ +#![allow(clippy::while_let_loop)] use crate::entities::{ReorderAllRowsPB, ReorderSingleRowPB, RowsVisibilityChangesetPB}; use crate::notification::{send_notification, DatabaseNotification}; use crate::services::filter::FilterResultNotification; diff --git a/frontend/rust-lib/flowy-document/src/editor/queue.rs b/frontend/rust-lib/flowy-document/src/editor/queue.rs index cd94e099ad..588f510d6d 100644 --- a/frontend/rust-lib/flowy-document/src/editor/queue.rs +++ b/frontend/rust-lib/flowy-document/src/editor/queue.rs @@ -1,3 +1,4 @@ +#![allow(clippy::while_let_loop)] use crate::editor::document::Document; use crate::DocumentUser; use async_stream::stream; diff --git a/frontend/rust-lib/flowy-document/src/old_editor/queue.rs b/frontend/rust-lib/flowy-document/src/old_editor/queue.rs index bf2bb3895a..e1b06674f3 100644 --- a/frontend/rust-lib/flowy-document/src/old_editor/queue.rs +++ b/frontend/rust-lib/flowy-document/src/old_editor/queue.rs @@ -1,3 +1,4 @@ +#![allow(clippy::while_let_loop)] use crate::old_editor::web_socket::DeltaDocumentResolveOperations; use crate::DocumentUser; use async_stream::stream; diff --git a/frontend/rust-lib/flowy-document/src/services/persistence/rev_sqlite/document_snapshot.rs b/frontend/rust-lib/flowy-document/src/services/persistence/rev_sqlite/document_snapshot.rs index 40ec19eb07..719a47a15b 100644 --- a/frontend/rust-lib/flowy-document/src/services/persistence/rev_sqlite/document_snapshot.rs +++ b/frontend/rust-lib/flowy-document/src/services/persistence/rev_sqlite/document_snapshot.rs @@ -1,3 +1,4 @@ +#![allow(clippy::unused_unit)] use bytes::Bytes; use flowy_error::{internal_error, FlowyResult}; use flowy_revision::{RevisionSnapshotData, RevisionSnapshotPersistence}; diff --git a/frontend/rust-lib/flowy-revision/src/rev_queue.rs b/frontend/rust-lib/flowy-revision/src/rev_queue.rs index fc235432cf..6e848f27db 100644 --- a/frontend/rust-lib/flowy-revision/src/rev_queue.rs +++ b/frontend/rust-lib/flowy-revision/src/rev_queue.rs @@ -1,3 +1,4 @@ +#![allow(clippy::while_let_loop)] use crate::{RevIdCounter, RevisionMergeable, RevisionPersistence}; use async_stream::stream; use bytes::Bytes; diff --git a/frontend/scripts/makefile/tests.toml b/frontend/scripts/makefile/tests.toml index 146ee481b0..3f85eaa75e 100644 --- a/frontend/scripts/makefile/tests.toml +++ b/frontend/scripts/makefile/tests.toml @@ -1,23 +1,23 @@ [tasks.dart_unit_test] script = ''' -cargo make --profile test-macos dart_unit_test_impl +cargo make --profile test-macos run_dart_unit_test ''' script_runner = "@shell" [tasks.dart_unit_test.windows] script = ''' -cargo make --profile test-windows dart_unit_test_impl +cargo make --profile test-windows run_dart_unit_test ''' script_runner = "@shell" [tasks.dart_unit_test.linux] script = ''' -cargo make --profile test-linux dart_unit_test_impl +cargo make --profile test-linux run_dart_unit_test ''' script_runner = "@shell" -[tasks.dart_unit_test_impl] +[tasks.run_dart_unit_test] env = { RUST_LOG = "info" } dependencies = ["build_test_backend"] description = "Run flutter unit tests" diff --git a/shared-lib/flowy-server-sync/src/server_document/document_manager.rs b/shared-lib/flowy-server-sync/src/server_document/document_manager.rs index 1717699984..a0ff3dc672 100644 --- a/shared-lib/flowy-server-sync/src/server_document/document_manager.rs +++ b/shared-lib/flowy-server-sync/src/server_document/document_manager.rs @@ -291,12 +291,9 @@ impl DocumentCommandRunner { .expect("DocumentCommandRunner's receiver should only take one time"); let stream = stream! { - loop { - match receiver.recv().await { - Some(msg) => yield msg, - None => break, - } - } + while let Some(msg) = receiver.recv().await { + yield msg; + } }; stream.for_each(|msg| self.handle_message(msg)).await; } diff --git a/shared-lib/flowy-server-sync/src/server_folder/folder_manager.rs b/shared-lib/flowy-server-sync/src/server_folder/folder_manager.rs index 19ec82bd16..6784708dd3 100644 --- a/shared-lib/flowy-server-sync/src/server_folder/folder_manager.rs +++ b/shared-lib/flowy-server-sync/src/server_folder/folder_manager.rs @@ -251,12 +251,9 @@ impl FolderCommandRunner { .expect("FolderCommandRunner's receiver should only take one time"); let stream = stream! { - loop { - match receiver.recv().await { - Some(msg) => yield msg, - None => break, - } - } + while let Some(msg) = receiver.recv().await { + yield msg; + } }; stream.for_each(|msg| self.handle_message(msg)).await; }