diff --git a/frontend/app_flowy/assets/images/grid/checkmark.svg b/frontend/app_flowy/assets/images/grid/checkmark.svg new file mode 100644 index 0000000000..f9c848f713 --- /dev/null +++ b/frontend/app_flowy/assets/images/grid/checkmark.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/app_flowy/lib/startup/deps_resolver.dart b/frontend/app_flowy/lib/startup/deps_resolver.dart index 8bab77150a..4cb0fda104 100644 --- a/frontend/app_flowy/lib/startup/deps_resolver.dart +++ b/frontend/app_flowy/lib/startup/deps_resolver.dart @@ -221,8 +221,8 @@ void _resolveGridDeps(GetIt getIt) { (typeOption, fieldId) => SingleSelectTypeOptionBloc(typeOption, fieldId), ); - getIt.registerFactoryParam( - (typeOption, _) => MultiSelectTypeOptionBloc(typeOption), + getIt.registerFactoryParam( + (typeOption, fieldId) => MultiSelectTypeOptionBloc(typeOption, fieldId), ); getIt.registerFactoryParam( diff --git a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/multi_select_bloc.dart b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/multi_select_bloc.dart index 75ac80c47c..0a96f5f69b 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/field/type_option/multi_select_bloc.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/field/type_option/multi_select_bloc.dart @@ -1,18 +1,37 @@ -import 'dart:typed_data'; +import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'dart:async'; +import 'package:protobuf/protobuf.dart'; +import 'type_option_service.dart'; part 'multi_select_bloc.freezed.dart'; class MultiSelectTypeOptionBloc extends Bloc { - MultiSelectTypeOptionBloc(MultiSelectTypeOption typeOption) : super(MultiSelectTypeOptionState.initial(typeOption)) { + final TypeOptionService service; + + MultiSelectTypeOptionBloc(MultiSelectTypeOption typeOption, String fieldId) + : service = TypeOptionService(fieldId: fieldId), + super(MultiSelectTypeOptionState.initial(typeOption)) { on( (event, emit) async { await event.map( - createOption: (_CreateOption value) {}, - updateOptions: (_UpdateOptions value) async {}, + createOption: (_CreateOption value) async { + final result = await service.createOption(value.optionName); + result.fold( + (option) { + emit(state.copyWith(typeOption: _insertOption(option))); + }, + (err) => Log.error(err), + ); + }, + updateOption: (_UpdateOption value) async { + emit(state.copyWith(typeOption: _updateOption(value.option))); + }, + deleteOption: (_DeleteOption value) { + emit(state.copyWith(typeOption: _deleteOption(value.option))); + }, ); }, ); @@ -22,12 +41,40 @@ class MultiSelectTypeOptionBloc extends Bloc close() async { return super.close(); } + + MultiSelectTypeOption _insertOption(SelectOption option) { + state.typeOption.freeze(); + return state.typeOption.rebuild((typeOption) { + typeOption.options.insert(0, option); + }); + } + + MultiSelectTypeOption _updateOption(SelectOption option) { + state.typeOption.freeze(); + return state.typeOption.rebuild((typeOption) { + final index = typeOption.options.indexWhere((element) => element.id == option.id); + if (index != -1) { + typeOption.options[index] = option; + } + }); + } + + MultiSelectTypeOption _deleteOption(SelectOption option) { + state.typeOption.freeze(); + return state.typeOption.rebuild((typeOption) { + final index = typeOption.options.indexWhere((element) => element.id == option.id); + if (index != -1) { + typeOption.options.removeAt(index); + } + }); + } } @freezed class MultiSelectTypeOptionEvent with _$MultiSelectTypeOptionEvent { const factory MultiSelectTypeOptionEvent.createOption(String optionName) = _CreateOption; - const factory MultiSelectTypeOptionEvent.updateOptions(List options) = _UpdateOptions; + const factory MultiSelectTypeOptionEvent.updateOption(SelectOption option) = _UpdateOption; + const factory MultiSelectTypeOptionEvent.deleteOption(SelectOption option) = _DeleteOption; } @freezed diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_tyep_switcher.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_tyep_switcher.dart index ce34097118..e8eb5caee2 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_tyep_switcher.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_tyep_switcher.dart @@ -156,7 +156,7 @@ TypeOptionBuilder _makeTypeOptionBuild({ case FieldType.SingleSelect: return SingleSelectTypeOptionBuilder(field.id, data, overlayDelegate, dataDelegate); case FieldType.MultiSelect: - return MultiSelectTypeOptionBuilder(data, overlayDelegate); + return MultiSelectTypeOptionBuilder(field.id, data, overlayDelegate, dataDelegate); case FieldType.Number: return NumberTypeOptionBuilder(data, overlayDelegate, dataDelegate); case FieldType.RichText: diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart index 43cf2597fa..23881e9310 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/type_option/multi_select.dart @@ -8,26 +8,45 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'option_pannel.dart'; class MultiSelectTypeOptionBuilder extends TypeOptionBuilder { - MultiSelectTypeOption typeOption; - TypeOptionOverlayDelegate delegate; + MultiSelectTypeOptionWidget _widget; - MultiSelectTypeOptionBuilder(TypeOptionData typeOptionData, this.delegate) - : typeOption = MultiSelectTypeOption.fromBuffer(typeOptionData); + MultiSelectTypeOptionBuilder( + String fieldId, + TypeOptionData typeOptionData, + TypeOptionOverlayDelegate overlayDelegate, + TypeOptionDataDelegate dataDelegate, + ) : _widget = MultiSelectTypeOptionWidget( + fieldId: fieldId, + typeOption: MultiSelectTypeOption.fromBuffer(typeOptionData), + overlayDelegate: overlayDelegate, + dataDelegate: dataDelegate, + ); @override - Widget? get customWidget => MultiSelectTypeOptionWidget(typeOption, delegate); + Widget? get customWidget => _widget; } class MultiSelectTypeOptionWidget extends TypeOptionWidget { + final String fieldId; final MultiSelectTypeOption typeOption; final TypeOptionOverlayDelegate overlayDelegate; - const MultiSelectTypeOptionWidget(this.typeOption, this.overlayDelegate, {Key? key}) : super(key: key); + final TypeOptionDataDelegate dataDelegate; + const MultiSelectTypeOptionWidget({ + required this.fieldId, + required this.typeOption, + required this.overlayDelegate, + required this.dataDelegate, + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => getIt(param1: typeOption), - child: BlocBuilder( + create: (context) => getIt(param1: typeOption, param2: fieldId), + child: BlocConsumer( + listener: (context, state) { + dataDelegate.didUpdateTypeOptionData(state.typeOption.writeToBuffer()); + }, builder: (context, state) { return OptionPannel( options: state.typeOption.options, @@ -37,9 +56,14 @@ class MultiSelectTypeOptionWidget extends TypeOptionWidget { createOptionCallback: (name) { context.read().add(MultiSelectTypeOptionEvent.createOption(name)); }, - updateOptionCallback: (updateOption) {}, - deleteOptionCallback: (deleteOption) {}, + updateOptionCallback: (updateOption) { + context.read().add(MultiSelectTypeOptionEvent.updateOption(updateOption)); + }, + deleteOptionCallback: (deleteOption) { + context.read().add(MultiSelectTypeOptionEvent.deleteOption(deleteOption)); + }, overlayDelegate: overlayDelegate, + key: ValueKey(state.typeOption.hashCode), ); }, ),