Skip to content

Commit bd32ce5

Browse files
authored
Feat: support single select option filter (#1494)
* feat: support select option filter * chore: select option filter ui * chore: support edit single select filter * chore: add flutter tests Co-authored-by: nathan <[email protected]>
1 parent c47f755 commit bd32ce5

File tree

18 files changed

+814
-43
lines changed

18 files changed

+814
-43
lines changed

frontend/app_flowy/assets/translations/en.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,18 @@
191191
"is": "is"
192192
}
193193
},
194+
"singleSelectOptionFilter": {
195+
"is": "Is",
196+
"isNot": "Is not",
197+
"isEmpty": "Is empty",
198+
"isNotEmpty": "Is not empty"
199+
},
200+
"multiSelectOptionFilter": {
201+
"contains": "Contains",
202+
"doesNotContain": "Does not contain",
203+
"isEmpty": "Is empty",
204+
"isNotEmpty": "Is not empty"
205+
},
194206
"field": {
195207
"hide": "Hide",
196208
"insertLeft": "Insert Left",

frontend/app_flowy/lib/plugins/grid/application/field/field_controller.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ class FieldInfo {
531531
case FieldType.Checkbox:
532532
// case FieldType.MultiSelect:
533533
case FieldType.RichText:
534-
// case FieldType.SingleSelect:
534+
case FieldType.SingleSelect:
535535
return true;
536536
default:
537537
return false;

frontend/app_flowy/lib/plugins/grid/application/filter/filter_create_bloc.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,10 @@ class GridCreateFilterBloc
9999
timestamp: timestamp,
100100
);
101101
case FieldType.MultiSelect:
102-
return _ffiService.insertSingleSelectFilter(
102+
return _ffiService.insertSelectOptionFilter(
103103
fieldId: fieldId,
104104
condition: SelectOptionCondition.OptionIs,
105+
fieldType: FieldType.MultiSelect,
105106
);
106107
case FieldType.Number:
107108
return _ffiService.insertNumberFilter(
@@ -116,9 +117,10 @@ class GridCreateFilterBloc
116117
content: '',
117118
);
118119
case FieldType.SingleSelect:
119-
return _ffiService.insertSingleSelectFilter(
120+
return _ffiService.insertSelectOptionFilter(
120121
fieldId: fieldId,
121122
condition: SelectOptionCondition.OptionIs,
123+
fieldType: FieldType.SingleSelect,
122124
);
123125
case FieldType.URL:
124126
return _ffiService.insertURLFilter(

frontend/app_flowy/lib/plugins/grid/application/filter/filter_service.dart

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -126,28 +126,11 @@ class FilterFFIService {
126126
);
127127
}
128128

129-
Future<Either<Unit, FlowyError>> insertSingleSelectFilter({
129+
Future<Either<Unit, FlowyError>> insertSelectOptionFilter({
130130
required String fieldId,
131-
String? filterId,
131+
required FieldType fieldType,
132132
required SelectOptionCondition condition,
133-
List<String> optionIds = const [],
134-
}) {
135-
final filter = SelectOptionFilterPB()
136-
..condition = condition
137-
..optionIds.addAll(optionIds);
138-
139-
return insertFilter(
140-
fieldId: fieldId,
141-
filterId: filterId,
142-
fieldType: FieldType.SingleSelect,
143-
data: filter.writeToBuffer(),
144-
);
145-
}
146-
147-
Future<Either<Unit, FlowyError>> insertMultiSelectFilter({
148-
required String fieldId,
149133
String? filterId,
150-
required SelectOptionCondition condition,
151134
List<String> optionIds = const [],
152135
}) {
153136
final filter = SelectOptionFilterPB()
@@ -157,7 +140,7 @@ class FilterFFIService {
157140
return insertFilter(
158141
fieldId: fieldId,
159142
filterId: filterId,
160-
fieldType: FieldType.MultiSelect,
143+
fieldType: fieldType,
161144
data: filter.writeToBuffer(),
162145
);
163146
}
@@ -168,8 +151,6 @@ class FilterFFIService {
168151
required FieldType fieldType,
169152
required List<int> data,
170153
}) {
171-
TextFilterCondition.DoesNotContain.value;
172-
173154
var insertFilterPayload = AlterFilterPayloadPB.create()
174155
..fieldId = fieldId
175156
..fieldType = fieldType
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
2+
import 'package:app_flowy/plugins/grid/presentation/widgets/filter/filter_info.dart';
3+
import 'package:app_flowy/plugins/grid/presentation/widgets/header/type_option/builder.dart';
4+
import 'package:flowy_sdk/log.dart';
5+
import 'package:flowy_sdk/protobuf/flowy-grid/select_option_filter.pbserver.dart';
6+
import 'package:flowy_sdk/protobuf/flowy-grid/util.pb.dart';
7+
import 'package:flutter_bloc/flutter_bloc.dart';
8+
import 'package:freezed_annotation/freezed_annotation.dart';
9+
import 'dart:async';
10+
import 'filter_listener.dart';
11+
import 'filter_service.dart';
12+
13+
part 'select_option_filter_bloc.freezed.dart';
14+
15+
class SelectOptionFilterEditorBloc
16+
extends Bloc<SelectOptionFilterEditorEvent, SelectOptionFilterEditorState> {
17+
final FilterInfo filterInfo;
18+
final FilterFFIService _ffiService;
19+
final FilterListener _listener;
20+
final SingleSelectTypeOptionContext typeOptionContext;
21+
22+
SelectOptionFilterEditorBloc({required this.filterInfo})
23+
: _ffiService = FilterFFIService(viewId: filterInfo.viewId),
24+
_listener = FilterListener(
25+
viewId: filterInfo.viewId,
26+
filterId: filterInfo.filter.id,
27+
),
28+
typeOptionContext = makeSingleSelectTypeOptionContext(
29+
gridId: filterInfo.viewId,
30+
fieldPB: filterInfo.field.field,
31+
),
32+
super(SelectOptionFilterEditorState.initial(filterInfo)) {
33+
on<SelectOptionFilterEditorEvent>(
34+
(event, emit) async {
35+
event.when(
36+
initial: () async {
37+
_startListening();
38+
_loadOptions();
39+
},
40+
updateCondition: (SelectOptionCondition condition) {
41+
_ffiService.insertSelectOptionFilter(
42+
filterId: filterInfo.filter.id,
43+
fieldId: filterInfo.field.id,
44+
condition: condition,
45+
optionIds: state.filter.optionIds,
46+
fieldType: state.filterInfo.field.fieldType,
47+
);
48+
},
49+
updateContent: (List<String> optionIds) {
50+
_ffiService.insertSelectOptionFilter(
51+
filterId: filterInfo.filter.id,
52+
fieldId: filterInfo.field.id,
53+
condition: state.filter.condition,
54+
optionIds: optionIds,
55+
fieldType: state.filterInfo.field.fieldType,
56+
);
57+
},
58+
delete: () {
59+
_ffiService.deleteFilter(
60+
fieldId: filterInfo.field.id,
61+
filterId: filterInfo.filter.id,
62+
fieldType: filterInfo.field.fieldType,
63+
);
64+
},
65+
didReceiveFilter: (FilterPB filter) {
66+
final filterInfo = state.filterInfo.copyWith(filter: filter);
67+
final selectOptionFilter = filterInfo.selectOptionFilter()!;
68+
emit(state.copyWith(
69+
filterInfo: filterInfo,
70+
filter: selectOptionFilter,
71+
));
72+
},
73+
updateFilterDescription: (String desc) {
74+
emit(state.copyWith(filterDesc: desc));
75+
},
76+
);
77+
},
78+
);
79+
}
80+
81+
void _startListening() {
82+
_listener.start(
83+
onDeleted: () {
84+
if (!isClosed) add(const SelectOptionFilterEditorEvent.delete());
85+
},
86+
onUpdated: (filter) {
87+
if (!isClosed) {
88+
add(SelectOptionFilterEditorEvent.didReceiveFilter(filter));
89+
}
90+
},
91+
);
92+
}
93+
94+
void _loadOptions() {
95+
typeOptionContext.loadTypeOptionData(
96+
onCompleted: (value) {
97+
if (!isClosed) {
98+
String filterDesc = '';
99+
for (final option in value.options) {
100+
if (state.filter.optionIds.contains(option.id)) {
101+
filterDesc += "${option.name} ";
102+
}
103+
}
104+
add(SelectOptionFilterEditorEvent.updateFilterDescription(
105+
filterDesc));
106+
}
107+
},
108+
onError: (error) => Log.error(error),
109+
);
110+
}
111+
112+
@override
113+
Future<void> close() async {
114+
await _listener.stop();
115+
return super.close();
116+
}
117+
}
118+
119+
@freezed
120+
class SelectOptionFilterEditorEvent with _$SelectOptionFilterEditorEvent {
121+
const factory SelectOptionFilterEditorEvent.initial() = _Initial;
122+
const factory SelectOptionFilterEditorEvent.didReceiveFilter(
123+
FilterPB filter) = _DidReceiveFilter;
124+
const factory SelectOptionFilterEditorEvent.updateCondition(
125+
SelectOptionCondition condition) = _UpdateCondition;
126+
const factory SelectOptionFilterEditorEvent.updateContent(
127+
List<String> optionIds) = _UpdateContent;
128+
const factory SelectOptionFilterEditorEvent.updateFilterDescription(
129+
String desc) = _UpdateDesc;
130+
const factory SelectOptionFilterEditorEvent.delete() = _Delete;
131+
}
132+
133+
@freezed
134+
class SelectOptionFilterEditorState with _$SelectOptionFilterEditorState {
135+
const factory SelectOptionFilterEditorState({
136+
required FilterInfo filterInfo,
137+
required SelectOptionFilterPB filter,
138+
required String filterDesc,
139+
}) = _GridFilterState;
140+
141+
factory SelectOptionFilterEditorState.initial(FilterInfo filterInfo) {
142+
return SelectOptionFilterEditorState(
143+
filterInfo: filterInfo,
144+
filter: filterInfo.selectOptionFilter()!,
145+
filterDesc: '',
146+
);
147+
}
148+
}

frontend/app_flowy/lib/plugins/grid/application/filter/select_option_filter_editor_bloc.dart

Whitespace-only changes.

0 commit comments

Comments
 (0)