mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: support checklist filter
This commit is contained in:
parent
5ce8087c91
commit
553cfb3f5e
@ -197,6 +197,10 @@
|
|||||||
"is": "is"
|
"is": "is"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"checklistFilter": {
|
||||||
|
"isComplete": "is complete",
|
||||||
|
"isIncomplted": "is incomplete"
|
||||||
|
},
|
||||||
"singleSelectOptionFilter": {
|
"singleSelectOptionFilter": {
|
||||||
"is": "Is",
|
"is": "Is",
|
||||||
"isNot": "Is not",
|
"isNot": "Is not",
|
||||||
|
@ -522,7 +522,7 @@ class FieldInfo {
|
|||||||
case FieldType.MultiSelect:
|
case FieldType.MultiSelect:
|
||||||
case FieldType.RichText:
|
case FieldType.RichText:
|
||||||
case FieldType.SingleSelect:
|
case FieldType.SingleSelect:
|
||||||
// case FieldType.Checklist:
|
case FieldType.Checklist:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
@ -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: '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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 "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
|
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/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/date_filter.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.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';
|
import 'package:flowy_sdk/protobuf/flowy-grid/select_option_filter.pbserver.dart';
|
||||||
@ -50,4 +51,12 @@ class FilterInfo {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChecklistFilterPB? checklistFilter() {
|
||||||
|
if (filter.fieldType == FieldType.Checklist) {
|
||||||
|
return ChecklistFilterPB.fromBuffer(filter.data);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pbenum.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'choicechip/checkbox.dart';
|
import 'choicechip/checkbox.dart';
|
||||||
import 'choicechip/checklist.dart';
|
import 'choicechip/checklist/checklist.dart';
|
||||||
import 'choicechip/date.dart';
|
import 'choicechip/date.dart';
|
||||||
import 'choicechip/number.dart';
|
import 'choicechip/number.dart';
|
||||||
import 'choicechip/select_option/select_option.dart';
|
import 'choicechip/select_option/select_option.dart';
|
||||||
|
Loading…
Reference in New Issue
Block a user