chore: support checklist filter

This commit is contained in:
nathan 2022-11-30 16:43:57 +08:00
parent 5ce8087c91
commit 553cfb3f5e
8 changed files with 294 additions and 16 deletions

View File

@ -197,6 +197,10 @@
"is": "is"
}
},
"checklistFilter": {
"isComplete": "is complete",
"isIncomplted": "is incomplete"
},
"singleSelectOptionFilter": {
"is": "Is",
"isNot": "Is not",

View File

@ -522,7 +522,7 @@ class FieldInfo {
case FieldType.MultiSelect:
case FieldType.RichText:
case FieldType.SingleSelect:
// case FieldType.Checklist:
case FieldType.Checklist:
return true;
default:
return false;

View File

@ -0,0 +1,107 @@
import 'package:app_flowy/plugins/grid/presentation/widgets/filter/filter_info.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/checklist_filter.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/util.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
import 'filter_listener.dart';
import 'filter_service.dart';
part 'checklist_filter_bloc.freezed.dart';
class ChecklistFilterEditorBloc
extends Bloc<ChecklistFilterEditorEvent, ChecklistFilterEditorState> {
final FilterInfo filterInfo;
final FilterFFIService _ffiService;
// final SelectOptionFFIService _selectOptionService;
final FilterListener _listener;
ChecklistFilterEditorBloc({
required this.filterInfo,
}) : _ffiService = FilterFFIService(viewId: filterInfo.viewId),
// _selectOptionService =
// SelectOptionFFIService(cellId: cellController.cellId)
_listener = FilterListener(
viewId: filterInfo.viewId,
filterId: filterInfo.filter.id,
),
super(ChecklistFilterEditorState.initial(filterInfo)) {
on<ChecklistFilterEditorEvent>(
(event, emit) async {
event.when(
initial: () async {
_startListening();
},
updateCondition: (ChecklistFilterCondition condition) {
_ffiService.insertChecklistFilter(
filterId: filterInfo.filter.id,
fieldId: filterInfo.fieldInfo.id,
condition: condition,
);
},
delete: () {
_ffiService.deleteFilter(
fieldId: filterInfo.fieldInfo.id,
filterId: filterInfo.filter.id,
fieldType: filterInfo.fieldInfo.fieldType,
);
},
didReceiveFilter: (FilterPB filter) {
final filterInfo = state.filterInfo.copyWith(filter: filter);
final checklistFilter = filterInfo.checklistFilter()!;
emit(state.copyWith(
filterInfo: filterInfo,
filter: checklistFilter,
));
},
);
},
);
}
void _startListening() {
_listener.start(
onDeleted: () {
if (!isClosed) add(const ChecklistFilterEditorEvent.delete());
},
onUpdated: (filter) {
if (!isClosed) {
add(ChecklistFilterEditorEvent.didReceiveFilter(filter));
}
},
);
}
@override
Future<void> close() async {
await _listener.stop();
return super.close();
}
}
@freezed
class ChecklistFilterEditorEvent with _$ChecklistFilterEditorEvent {
const factory ChecklistFilterEditorEvent.initial() = _Initial;
const factory ChecklistFilterEditorEvent.didReceiveFilter(FilterPB filter) =
_DidReceiveFilter;
const factory ChecklistFilterEditorEvent.updateCondition(
ChecklistFilterCondition condition) = _UpdateCondition;
const factory ChecklistFilterEditorEvent.delete() = _Delete;
}
@freezed
class ChecklistFilterEditorState with _$ChecklistFilterEditorState {
const factory ChecklistFilterEditorState({
required FilterInfo filterInfo,
required ChecklistFilterPB filter,
required String filterDesc,
}) = _GridFilterState;
factory ChecklistFilterEditorState.initial(FilterInfo filterInfo) {
return ChecklistFilterEditorState(
filterInfo: filterInfo,
filter: filterInfo.checklistFilter()!,
filterDesc: '',
);
}
}

View File

@ -1,14 +0,0 @@
import 'package:app_flowy/plugins/grid/presentation/widgets/filter/filter_info.dart';
import 'package:flutter/material.dart';
import 'choicechip.dart';
class ChecklistFilterChoicechip extends StatelessWidget {
final FilterInfo filterInfo;
const ChecklistFilterChoicechip({required this.filterInfo, Key? key})
: super(key: key);
@override
Widget build(BuildContext context) {
return ChoiceChipButton(filterInfo: filterInfo);
}
}

View File

@ -0,0 +1,172 @@
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/plugins/grid/application/filter/checklist_filter_bloc.dart';
import 'package:app_flowy/plugins/grid/presentation/widgets/filter/condition_button.dart';
import 'package:app_flowy/plugins/grid/presentation/widgets/filter/disclosure_button.dart';
import 'package:app_flowy/plugins/grid/presentation/widgets/filter/filter_info.dart';
import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/checklist_filter.pbenum.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../choicechip.dart';
class ChecklistFilterChoicechip extends StatefulWidget {
final FilterInfo filterInfo;
const ChecklistFilterChoicechip({required this.filterInfo, Key? key})
: super(key: key);
@override
State<ChecklistFilterChoicechip> createState() =>
_ChecklistFilterChoicechipState();
}
class _ChecklistFilterChoicechipState extends State<ChecklistFilterChoicechip> {
late ChecklistFilterEditorBloc bloc;
late PopoverMutex popoverMutex;
@override
void initState() {
popoverMutex = PopoverMutex();
bloc = ChecklistFilterEditorBloc(filterInfo: widget.filterInfo);
bloc.add(const ChecklistFilterEditorEvent.initial());
super.initState();
}
@override
void dispose() {
bloc.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
return BlocProvider.value(
value: bloc,
child: BlocBuilder<ChecklistFilterEditorBloc, ChecklistFilterEditorState>(
builder: (blocContext, state) {
return AppFlowyPopover(
controller: PopoverController(),
constraints: BoxConstraints.loose(const Size(200, 160)),
direction: PopoverDirection.bottomWithCenterAligned,
popupBuilder: (BuildContext context) {
return ChecklistFilterEditor(
bloc: bloc,
popoverMutex: popoverMutex,
);
},
child: ChoiceChipButton(
filterInfo: widget.filterInfo,
filterDesc: state.filterDesc,
),
);
},
),
);
}
}
class ChecklistFilterEditor extends StatefulWidget {
final ChecklistFilterEditorBloc bloc;
final PopoverMutex popoverMutex;
const ChecklistFilterEditor(
{required this.bloc, required this.popoverMutex, Key? key})
: super(key: key);
@override
ChecklistState createState() => ChecklistState();
}
class ChecklistState extends State<ChecklistFilterEditor> {
@override
Widget build(BuildContext context) {
return BlocProvider.value(
value: widget.bloc,
child: BlocBuilder<ChecklistFilterEditorBloc, ChecklistFilterEditorState>(
builder: (context, state) {
return SizedBox(
height: 20,
child: Row(
children: [
FlowyText(state.filterInfo.fieldInfo.name),
const HSpace(4),
ChecklistFilterConditionList(
filterInfo: state.filterInfo,
),
const Spacer(),
DisclosureButton(
popoverMutex: widget.popoverMutex,
onAction: (action) {
switch (action) {
case FilterDisclosureAction.delete:
context
.read<ChecklistFilterEditorBloc>()
.add(const ChecklistFilterEditorEvent.delete());
break;
}
},
),
],
),
);
},
),
);
}
}
class ChecklistFilterConditionList extends StatelessWidget {
final FilterInfo filterInfo;
const ChecklistFilterConditionList({
required this.filterInfo,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final checklistFilter = filterInfo.checklistFilter()!;
return PopoverActionList<ConditionWrapper>(
asBarrier: true,
direction: PopoverDirection.bottomWithCenterAligned,
actions: ChecklistFilterCondition.values
.map((action) => ConditionWrapper(action))
.toList(),
buildChild: (controller) {
return ConditionButton(
conditionName: checklistFilter.condition.filterName,
onTap: () => controller.show(),
);
},
onSelected: (action, controller) async {
context
.read<ChecklistFilterEditorBloc>()
.add(ChecklistFilterEditorEvent.updateCondition(action.inner));
controller.close();
},
);
}
}
class ConditionWrapper extends ActionCell {
final ChecklistFilterCondition inner;
ConditionWrapper(this.inner);
@override
String get name => inner.filterName;
}
extension ChecklistFilterConditionExtension on ChecklistFilterCondition {
String get filterName {
switch (this) {
case ChecklistFilterCondition.IsComplete:
return LocaleKeys.grid_checklistFilter_isComplete.tr();
case ChecklistFilterCondition.IsIncomplete:
return LocaleKeys.grid_checklistFilter_isIncomplted.tr();
default:
return "";
}
}
}

View File

@ -1,5 +1,6 @@
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_filter.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/checklist_filter.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/date_filter.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/select_option_filter.pbserver.dart';
@ -50,4 +51,12 @@ class FilterInfo {
return null;
}
}
ChecklistFilterPB? checklistFilter() {
if (filter.fieldType == FieldType.Checklist) {
return ChecklistFilterPB.fromBuffer(filter.data);
} else {
return null;
}
}
}

View File

@ -2,7 +2,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pbenum.dart';
import 'package:flutter/material.dart';
import 'choicechip/checkbox.dart';
import 'choicechip/checklist.dart';
import 'choicechip/checklist/checklist.dart';
import 'choicechip/date.dart';
import 'choicechip/number.dart';
import 'choicechip/select_option/select_option.dart';