chore: optimaze create option progress

This commit is contained in:
appflowy 2022-05-18 20:11:49 +08:00
parent 1267c7524e
commit f3b87d419f
12 changed files with 227 additions and 314 deletions

View File

@ -1,101 +0,0 @@
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';
typedef MultiSelectTypeOptionContext = TypeOptionContext<MultiSelectTypeOption>;
class MultiSelectTypeOptionDataBuilder extends TypeOptionDataBuilder<MultiSelectTypeOption> {
@override
MultiSelectTypeOption fromBuffer(List<int> buffer) {
return MultiSelectTypeOption.fromBuffer(buffer);
}
}
class MultiSelectTypeOptionBloc extends Bloc<MultiSelectTypeOptionEvent, MultiSelectTypeOptionState> {
final TypeOptionService service;
MultiSelectTypeOptionBloc(MultiSelectTypeOptionContext typeOptionContext)
: service = TypeOptionService(
gridId: typeOptionContext.gridId,
fieldId: typeOptionContext.field.id,
),
super(MultiSelectTypeOptionState.initial(typeOptionContext.typeOption)) {
on<MultiSelectTypeOptionEvent>(
(event, emit) async {
await event.map(
createOption: (_CreateOption value) async {
final result = await service.newOption(name: 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)));
},
);
},
);
}
@override
Future<void> 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.updateOption(SelectOption option) = _UpdateOption;
const factory MultiSelectTypeOptionEvent.deleteOption(SelectOption option) = _DeleteOption;
}
@freezed
class MultiSelectTypeOptionState with _$MultiSelectTypeOptionState {
const factory MultiSelectTypeOptionState({
required MultiSelectTypeOption typeOption,
}) = _MultiSelectTypeOptionState;
factory MultiSelectTypeOptionState.initial(MultiSelectTypeOption typeOption) => MultiSelectTypeOptionState(
typeOption: typeOption,
);
}

View File

@ -0,0 +1,77 @@
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
import 'dart:async';
import 'package:protobuf/protobuf.dart';
import 'select_option_type_option_bloc.dart';
import 'type_option_service.dart';
class MultiSelectTypeOptionContext extends TypeOptionContext<MultiSelectTypeOption> with SelectOptionTypeOptionAction {
final TypeOptionService service;
MultiSelectTypeOptionContext({
required MultiSelectTypeOptionDataBuilder dataBuilder,
required GridFieldContext fieldContext,
}) : service = TypeOptionService(
gridId: fieldContext.gridId,
fieldId: fieldContext.field.id,
),
super(dataBuilder: dataBuilder, fieldContext: fieldContext);
@override
List<SelectOption> Function(SelectOption) get deleteOption {
return (SelectOption option) {
typeOption.freeze();
typeOption = typeOption.rebuild((typeOption) {
final index = typeOption.options.indexWhere((element) => element.id == option.id);
if (index != -1) {
typeOption.options.removeAt(index);
}
});
return typeOption.options;
};
}
@override
Future<List<SelectOption>> Function(String) get insertOption {
return (String optionName) {
return service.newOption(name: optionName).then((result) {
return result.fold(
(option) {
typeOption.freeze();
typeOption = typeOption.rebuild((typeOption) {
typeOption.options.insert(0, option);
});
return typeOption.options;
},
(err) {
Log.error(err);
return typeOption.options;
},
);
});
};
}
@override
List<SelectOption> Function(SelectOption) get udpateOption {
return (SelectOption option) {
typeOption.freeze();
typeOption = typeOption.rebuild((typeOption) {
final index = typeOption.options.indexWhere((element) => element.id == option.id);
if (index != -1) {
typeOption.options[index] = option;
}
});
return typeOption.options;
};
}
}
class MultiSelectTypeOptionDataBuilder extends TypeOptionDataBuilder<MultiSelectTypeOption> {
@override
MultiSelectTypeOption fromBuffer(List<int> buffer) {
return MultiSelectTypeOption.fromBuffer(buffer);
}
}

View File

@ -5,26 +5,41 @@ import 'dart:async';
import 'package:dartz/dartz.dart';
part 'select_option_type_option_bloc.freezed.dart';
class SelectOptionTypeOptionBloc extends Bloc<SelectOptionTypeOptionEvent, SelectOptionTyepOptionState> {
SelectOptionTypeOptionBloc({required List<SelectOption> options})
: super(SelectOptionTyepOptionState.initial(options)) {
abstract class SelectOptionTypeOptionAction {
Future<List<SelectOption>> Function(String) get insertOption;
List<SelectOption> Function(SelectOption) get deleteOption;
List<SelectOption> Function(SelectOption) get udpateOption;
}
class SelectOptionTypeOptionBloc extends Bloc<SelectOptionTypeOptionEvent, SelectOptionTypeOptionState> {
final SelectOptionTypeOptionAction typeOptionAction;
SelectOptionTypeOptionBloc({
required List<SelectOption> options,
required this.typeOptionAction,
}) : super(SelectOptionTypeOptionState.initial(options)) {
on<SelectOptionTypeOptionEvent>(
(event, emit) async {
await event.map(
createOption: (_CreateOption value) async {
emit(state.copyWith(isEditingOption: true, newOptionName: Some(value.optionName)));
await event.when(
createOption: (optionName) async {
final List<SelectOption> options = await typeOptionAction.insertOption(optionName);
emit(state.copyWith(options: options));
},
addingOption: (_AddingOption value) {
addingOption: () {
emit(state.copyWith(isEditingOption: true, newOptionName: none()));
},
endAddingOption: (_EndAddingOption value) {
endAddingOption: () {
emit(state.copyWith(isEditingOption: false, newOptionName: none()));
},
updateOption: (_UpdateOption value) {
emit(state.copyWith(updateOption: Some(value.option)));
updateOption: (option) {
final List<SelectOption> options = typeOptionAction.udpateOption(option);
emit(state.copyWith(options: options));
},
deleteOption: (_DeleteOption value) {
emit(state.copyWith(deleteOption: Some(value.option)));
deleteOption: (option) {
final List<SelectOption> options = typeOptionAction.deleteOption(option);
emit(state.copyWith(options: options));
},
);
},
@ -47,20 +62,16 @@ class SelectOptionTypeOptionEvent with _$SelectOptionTypeOptionEvent {
}
@freezed
class SelectOptionTyepOptionState with _$SelectOptionTyepOptionState {
const factory SelectOptionTyepOptionState({
class SelectOptionTypeOptionState with _$SelectOptionTypeOptionState {
const factory SelectOptionTypeOptionState({
required List<SelectOption> options,
required bool isEditingOption,
required Option<String> newOptionName,
required Option<SelectOption> updateOption,
required Option<SelectOption> deleteOption,
}) = _SelectOptionTyepOptionState;
factory SelectOptionTyepOptionState.initial(List<SelectOption> options) => SelectOptionTyepOptionState(
factory SelectOptionTypeOptionState.initial(List<SelectOption> options) => SelectOptionTypeOptionState(
options: options,
isEditingOption: false,
newOptionName: none(),
updateOption: none(),
deleteOption: none(),
);
}

View File

@ -1,99 +0,0 @@
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 'single_select_bloc.freezed.dart';
typedef SingleSelectTypeOptionContext = TypeOptionContext<SingleSelectTypeOption>;
class SingleSelectTypeOptionDataBuilder extends TypeOptionDataBuilder<SingleSelectTypeOption> {
@override
SingleSelectTypeOption fromBuffer(List<int> buffer) {
return SingleSelectTypeOption.fromBuffer(buffer);
}
}
class SingleSelectTypeOptionBloc extends Bloc<SingleSelectTypeOptionEvent, SingleSelectTypeOptionState> {
final TypeOptionService service;
SingleSelectTypeOptionBloc(
SingleSelectTypeOptionContext typeOptionContext,
) : service = TypeOptionService(gridId: typeOptionContext.gridId, fieldId: typeOptionContext.field.id),
super(SingleSelectTypeOptionState.initial(typeOptionContext.typeOption)) {
on<SingleSelectTypeOptionEvent>(
(event, emit) async {
await event.map(
createOption: (_CreateOption value) async {
final result = await service.newOption(name: 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)));
},
);
},
);
}
@override
Future<void> close() async {
return super.close();
}
SingleSelectTypeOption _insertOption(SelectOption option) {
state.typeOption.freeze();
return state.typeOption.rebuild((typeOption) {
typeOption.options.insert(0, option);
});
}
SingleSelectTypeOption _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;
}
});
}
SingleSelectTypeOption _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 SingleSelectTypeOptionEvent with _$SingleSelectTypeOptionEvent {
const factory SingleSelectTypeOptionEvent.createOption(String optionName) = _CreateOption;
const factory SingleSelectTypeOptionEvent.updateOption(SelectOption option) = _UpdateOption;
const factory SingleSelectTypeOptionEvent.deleteOption(SelectOption option) = _DeleteOption;
}
@freezed
class SingleSelectTypeOptionState with _$SingleSelectTypeOptionState {
const factory SingleSelectTypeOptionState({
required SingleSelectTypeOption typeOption,
}) = _SingleSelectTypeOptionState;
factory SingleSelectTypeOptionState.initial(SingleSelectTypeOption typeOption) => SingleSelectTypeOptionState(
typeOption: typeOption,
);
}

View File

@ -0,0 +1,78 @@
import 'package:app_flowy/workspace/application/grid/field/field_service.dart';
import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/selection_type_option.pb.dart';
import 'dart:async';
import 'package:protobuf/protobuf.dart';
import 'select_option_type_option_bloc.dart';
import 'type_option_service.dart';
class SingleSelectTypeOptionContext extends TypeOptionContext<SingleSelectTypeOption>
with SelectOptionTypeOptionAction {
final TypeOptionService service;
SingleSelectTypeOptionContext({
required SingleSelectTypeOptionDataBuilder dataBuilder,
required GridFieldContext fieldContext,
}) : service = TypeOptionService(
gridId: fieldContext.gridId,
fieldId: fieldContext.field.id,
),
super(dataBuilder: dataBuilder, fieldContext: fieldContext);
@override
List<SelectOption> Function(SelectOption) get deleteOption {
return (SelectOption option) {
typeOption.freeze();
typeOption = typeOption.rebuild((typeOption) {
final index = typeOption.options.indexWhere((element) => element.id == option.id);
if (index != -1) {
typeOption.options.removeAt(index);
}
});
return typeOption.options;
};
}
@override
Future<List<SelectOption>> Function(String) get insertOption {
return (String optionName) {
return service.newOption(name: optionName).then((result) {
return result.fold(
(option) {
typeOption.freeze();
typeOption = typeOption.rebuild((typeOption) {
typeOption.options.insert(0, option);
});
return typeOption.options;
},
(err) {
Log.error(err);
return typeOption.options;
},
);
});
};
}
@override
List<SelectOption> Function(SelectOption) get udpateOption {
return (SelectOption option) {
typeOption.freeze();
typeOption = typeOption.rebuild((typeOption) {
final index = typeOption.options.indexWhere((element) => element.id == option.id);
if (index != -1) {
typeOption.options[index] = option;
}
});
return typeOption.options;
};
}
}
class SingleSelectTypeOptionDataBuilder extends TypeOptionDataBuilder<SingleSelectTypeOption> {
@override
SingleSelectTypeOption fromBuffer(List<int> buffer) {
return SingleSelectTypeOption.fromBuffer(buffer);
}
}

View File

@ -64,7 +64,7 @@ class TypeOptionContext<T extends GeneratedMessage> {
set typeOption(T typeOption) {
_fieldContext.typeOptionData = typeOption.writeToBuffer();
_typeOptionObject = null;
_typeOptionObject = typeOption;
}
}

View File

@ -13,7 +13,7 @@ export 'field/field_editor_pannel_bloc.dart';
// Field Type Option
export 'field/type_option/date_bloc.dart';
export 'field/type_option/number_bloc.dart';
export 'field/type_option/single_select_bloc.dart';
export 'field/type_option/single_select_type_option.dart';
// Cell
export 'cell/text_cell_bloc.dart';

View File

@ -7,6 +7,7 @@ class InputTextField extends StatefulWidget {
final void Function(String)? onDone;
final void Function(String)? onChanged;
final void Function() onCanceled;
final bool autoClearWhenDone;
final String text;
const InputTextField({
@ -14,6 +15,7 @@ class InputTextField extends StatefulWidget {
this.onDone,
required this.onCanceled,
this.onChanged,
this.autoClearWhenDone = false,
Key? key,
}) : super(key: key);
@ -57,6 +59,10 @@ class _InputTextFieldState extends State<InputTextField> {
if (widget.onDone != null) {
widget.onDone!(_controller.text);
}
if (widget.autoClearWhenDone) {
_controller.text = "";
}
},
);
}

View File

@ -1,6 +1,6 @@
import 'dart:typed_data';
import 'package:app_flowy/workspace/application/grid/field/type_option/multi_select_bloc.dart';
import 'package:app_flowy/workspace/application/grid/field/type_option/multi_select_type_option.dart';
import 'package:app_flowy/workspace/application/grid/field/type_option/type_option_service.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/type_option/checkbox.dart';
import 'package:dartz/dartz.dart' show Either;

View File

@ -1,7 +1,6 @@
import 'package:app_flowy/workspace/application/grid/field/type_option/multi_select_bloc.dart';
import 'package:app_flowy/workspace/application/grid/field/type_option/multi_select_type_option.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'select_option.dart';
@ -32,32 +31,12 @@ class MultiSelectTypeOptionWidget extends TypeOptionWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => MultiSelectTypeOptionBloc(typeOptionContext),
child: BlocConsumer<MultiSelectTypeOptionBloc, MultiSelectTypeOptionState>(
listener: (context, state) {
typeOptionContext.typeOption = state.typeOption;
},
builder: (context, state) {
return SelectOptionTypeOptionWidget(
options: state.typeOption.options,
beginEdit: () {
overlayDelegate.hideOverlay(context);
},
createSelectOptionCallback: (name) {
context.read<MultiSelectTypeOptionBloc>().add(MultiSelectTypeOptionEvent.createOption(name));
},
updateSelectOptionCallback: (option) {
context.read<MultiSelectTypeOptionBloc>().add(MultiSelectTypeOptionEvent.updateOption(option));
},
deleteSelectOptionCallback: (option) {
context.read<MultiSelectTypeOptionBloc>().add(MultiSelectTypeOptionEvent.deleteOption(option));
},
overlayDelegate: overlayDelegate,
// key: ValueKey(state.typeOption.hashCode),
);
},
),
return SelectOptionTypeOptionWidget(
options: typeOptionContext.typeOption.options,
beginEdit: () => overlayDelegate.hideOverlay(context),
overlayDelegate: overlayDelegate,
typeOptionAction: typeOptionContext,
// key: ValueKey(state.typeOption.hashCode),
);
}
}

View File

@ -19,45 +19,22 @@ import 'select_option_editor.dart';
class SelectOptionTypeOptionWidget extends StatelessWidget {
final List<SelectOption> options;
final VoidCallback beginEdit;
final Function(String optionName) createSelectOptionCallback;
final Function(SelectOption) updateSelectOptionCallback;
final Function(SelectOption) deleteSelectOptionCallback;
final TypeOptionOverlayDelegate overlayDelegate;
final SelectOptionTypeOptionAction typeOptionAction;
const SelectOptionTypeOptionWidget({
required this.options,
required this.beginEdit,
required this.createSelectOptionCallback,
required this.updateSelectOptionCallback,
required this.deleteSelectOptionCallback,
required this.overlayDelegate,
required this.typeOptionAction,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => SelectOptionTypeOptionBloc(options: options),
child: BlocConsumer<SelectOptionTypeOptionBloc, SelectOptionTyepOptionState>(
listener: (context, state) {
if (state.isEditingOption) {
beginEdit();
}
state.newOptionName.fold(
() => null,
(optionName) => createSelectOptionCallback(optionName),
);
state.updateOption.fold(
() => null,
(updateOption) => updateSelectOptionCallback(updateOption),
);
state.deleteOption.fold(
() => null,
(deleteOption) => deleteSelectOptionCallback(deleteOption),
);
},
create: (context) => SelectOptionTypeOptionBloc(options: options, typeOptionAction: typeOptionAction),
child: BlocBuilder<SelectOptionTypeOptionBloc, SelectOptionTypeOptionState>(
builder: (context, state) {
List<Widget> children = [
const TypeOptionSeparator(),
@ -83,7 +60,7 @@ class OptionTitle extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<SelectOptionTypeOptionBloc, SelectOptionTyepOptionState>(
return BlocBuilder<SelectOptionTypeOptionBloc, SelectOptionTypeOptionState>(
builder: (context, state) {
List<Widget> children = [FlowyText.medium(LocaleKeys.grid_field_optionTitle.tr(), fontSize: 12)];
if (state.options.isNotEmpty) {
@ -130,7 +107,7 @@ class _OptionList extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<SelectOptionTypeOptionBloc, SelectOptionTyepOptionState>(
return BlocBuilder<SelectOptionTypeOptionBloc, SelectOptionTypeOptionState>(
buildWhen: (previous, current) {
return previous.options != current.options;
},
@ -230,13 +207,19 @@ class _CreateOptionTextField extends StatelessWidget {
@override
Widget build(BuildContext context) {
return InputTextField(
text: "",
onCanceled: () {
context.read<SelectOptionTypeOptionBloc>().add(const SelectOptionTypeOptionEvent.endAddingOption());
},
onDone: (optionName) {
context.read<SelectOptionTypeOptionBloc>().add(SelectOptionTypeOptionEvent.createOption(optionName));
return BlocBuilder<SelectOptionTypeOptionBloc, SelectOptionTypeOptionState>(
builder: (context, state) {
final text = state.newOptionName.foldRight("", (a, previous) => a);
return InputTextField(
autoClearWhenDone: true,
text: text,
onCanceled: () {
context.read<SelectOptionTypeOptionBloc>().add(const SelectOptionTypeOptionEvent.endAddingOption());
},
onDone: (optionName) {
context.read<SelectOptionTypeOptionBloc>().add(SelectOptionTypeOptionEvent.createOption(optionName));
},
);
},
);
}

View File

@ -1,7 +1,6 @@
import 'package:app_flowy/workspace/application/grid/field/type_option/single_select_bloc.dart';
import 'package:app_flowy/workspace/application/grid/field/type_option/single_select_type_option.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_editor_pannel.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'select_option.dart';
class SingleSelectTypeOptionBuilder extends TypeOptionBuilder {
@ -31,32 +30,12 @@ class SingleSelectTypeOptionWidget extends TypeOptionWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => SingleSelectTypeOptionBloc(typeOptionContext),
child: BlocConsumer<SingleSelectTypeOptionBloc, SingleSelectTypeOptionState>(
listener: (context, state) {
typeOptionContext.typeOption = state.typeOption;
},
builder: (context, state) {
return SelectOptionTypeOptionWidget(
options: state.typeOption.options,
beginEdit: () {
overlayDelegate.hideOverlay(context);
},
createSelectOptionCallback: (name) {
context.read<SingleSelectTypeOptionBloc>().add(SingleSelectTypeOptionEvent.createOption(name));
},
updateSelectOptionCallback: (option) {
context.read<SingleSelectTypeOptionBloc>().add(SingleSelectTypeOptionEvent.updateOption(option));
},
deleteSelectOptionCallback: (option) {
context.read<SingleSelectTypeOptionBloc>().add(SingleSelectTypeOptionEvent.deleteOption(option));
},
overlayDelegate: overlayDelegate,
// key: ValueKey(state.typeOption.hashCode),
);
},
),
return SelectOptionTypeOptionWidget(
options: typeOptionContext.typeOption.options,
beginEdit: () => overlayDelegate.hideOverlay(context),
overlayDelegate: overlayDelegate,
typeOptionAction: typeOptionContext,
// key: ValueKey(state.typeOption.hashCode),
);
}
}