mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
@ -39,7 +39,7 @@ class GroupController {
|
||||
void startListening() {
|
||||
_listener.start(onGroupChanged: (result) {
|
||||
result.fold(
|
||||
(GroupChangesetPB changeset) {
|
||||
(GroupRowsNotificationPB changeset) {
|
||||
for (final deletedRow in changeset.deletedRows) {
|
||||
group.rows.removeWhere((rowPB) => rowPB.id == deletedRow);
|
||||
delegate.removeRow(group, deletedRow);
|
||||
|
@ -8,7 +8,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/group.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/group_changeset.pb.dart';
|
||||
|
||||
typedef UpdateGroupNotifiedValue = Either<GroupChangesetPB, FlowyError>;
|
||||
typedef UpdateGroupNotifiedValue = Either<GroupRowsNotificationPB, FlowyError>;
|
||||
|
||||
class GroupListener {
|
||||
final GroupPB group;
|
||||
@ -34,7 +34,7 @@ class GroupListener {
|
||||
case GridNotification.DidUpdateGroup:
|
||||
result.fold(
|
||||
(payload) => _groupNotifier?.value =
|
||||
left(GroupChangesetPB.fromBuffer(payload)),
|
||||
left(GroupRowsNotificationPB.fromBuffer(payload)),
|
||||
(error) => _groupNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
|
@ -0,0 +1,206 @@
|
||||
import 'package:app_flowy/plugins/grid/application/filter/filter_listener.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_filter.pbenum.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/date_filter.pbenum.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/number_filter.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/text_filter.pbserver.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_service.dart';
|
||||
|
||||
part 'filter_bloc.freezed.dart';
|
||||
|
||||
class GridFilterBloc extends Bloc<GridFilterEvent, GridFilterState> {
|
||||
final String viewId;
|
||||
final FilterFFIService _ffiService;
|
||||
final FilterListener _listener;
|
||||
GridFilterBloc({required this.viewId})
|
||||
: _ffiService = FilterFFIService(viewId: viewId),
|
||||
_listener = FilterListener(viewId: viewId),
|
||||
super(GridFilterState.initial()) {
|
||||
on<GridFilterEvent>(
|
||||
(event, emit) async {
|
||||
event.when(
|
||||
initial: () async {
|
||||
_startListening();
|
||||
await _loadFilters();
|
||||
},
|
||||
deleteFilter: (
|
||||
String fieldId,
|
||||
String filterId,
|
||||
FieldType fieldType,
|
||||
) {
|
||||
_ffiService.deleteFilter(
|
||||
fieldId: fieldId,
|
||||
filterId: filterId,
|
||||
fieldType: fieldType,
|
||||
);
|
||||
},
|
||||
didReceiveFilters: (filters) {
|
||||
emit(state.copyWith(filters: filters));
|
||||
},
|
||||
createCheckboxFilter: (
|
||||
String fieldId,
|
||||
CheckboxFilterCondition condition,
|
||||
) {
|
||||
_ffiService.createCheckboxFilter(
|
||||
fieldId: fieldId,
|
||||
condition: condition,
|
||||
);
|
||||
},
|
||||
createNumberFilter: (
|
||||
String fieldId,
|
||||
NumberFilterCondition condition,
|
||||
String content,
|
||||
) {
|
||||
_ffiService.createNumberFilter(
|
||||
fieldId: fieldId,
|
||||
condition: condition,
|
||||
content: content,
|
||||
);
|
||||
},
|
||||
createTextFilter: (
|
||||
String fieldId,
|
||||
TextFilterCondition condition,
|
||||
String content,
|
||||
) {
|
||||
_ffiService.createTextFilter(
|
||||
fieldId: fieldId,
|
||||
condition: condition,
|
||||
);
|
||||
},
|
||||
createDateFilter: (
|
||||
String fieldId,
|
||||
DateFilterCondition condition,
|
||||
int timestamp,
|
||||
) {
|
||||
_ffiService.createDateFilter(
|
||||
fieldId: fieldId,
|
||||
condition: condition,
|
||||
timestamp: timestamp,
|
||||
);
|
||||
},
|
||||
createDateFilterInRange: (
|
||||
String fieldId,
|
||||
DateFilterCondition condition,
|
||||
int start,
|
||||
int end,
|
||||
) {
|
||||
_ffiService.createDateFilter(
|
||||
fieldId: fieldId,
|
||||
condition: condition,
|
||||
start: start,
|
||||
end: end,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
_listener.start(onFilterChanged: (result) {
|
||||
result.fold(
|
||||
(changeset) {
|
||||
final List<FilterPB> filters = List.from(state.filters);
|
||||
|
||||
// Deletes the filters
|
||||
final deleteFilterIds =
|
||||
changeset.deleteFilters.map((e) => e.id).toList();
|
||||
filters.retainWhere(
|
||||
(element) => !deleteFilterIds.contains(element.id),
|
||||
);
|
||||
|
||||
// Inserts the new fitler if it's not exist
|
||||
for (final newFilter in changeset.insertFilters) {
|
||||
final index =
|
||||
filters.indexWhere((element) => element.id == newFilter.id);
|
||||
if (index == -1) {
|
||||
filters.add(newFilter);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isClosed) {
|
||||
add(GridFilterEvent.didReceiveFilters(filters));
|
||||
}
|
||||
},
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _loadFilters() async {
|
||||
final result = await _ffiService.getAllFilters();
|
||||
result.fold(
|
||||
(filters) {
|
||||
if (!isClosed) {
|
||||
add(GridFilterEvent.didReceiveFilters(filters));
|
||||
}
|
||||
},
|
||||
(err) => Log.error(err),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _listener.stop();
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class GridFilterEvent with _$GridFilterEvent {
|
||||
const factory GridFilterEvent.initial() = _Initial;
|
||||
const factory GridFilterEvent.didReceiveFilters(List<FilterPB> filters) =
|
||||
_DidReceiveFilters;
|
||||
|
||||
const factory GridFilterEvent.deleteFilter({
|
||||
required String fieldId,
|
||||
required String filterId,
|
||||
required FieldType fieldType,
|
||||
}) = _DeleteFilter;
|
||||
|
||||
const factory GridFilterEvent.createTextFilter({
|
||||
required String fieldId,
|
||||
required TextFilterCondition condition,
|
||||
required String content,
|
||||
}) = _CreateTextFilter;
|
||||
|
||||
const factory GridFilterEvent.createCheckboxFilter({
|
||||
required String fieldId,
|
||||
required CheckboxFilterCondition condition,
|
||||
}) = _CreateCheckboxFilter;
|
||||
|
||||
const factory GridFilterEvent.createNumberFilter({
|
||||
required String fieldId,
|
||||
required NumberFilterCondition condition,
|
||||
required String content,
|
||||
}) = _CreateCheckboxFitler;
|
||||
|
||||
const factory GridFilterEvent.createDateFilter({
|
||||
required String fieldId,
|
||||
required DateFilterCondition condition,
|
||||
required int start,
|
||||
}) = _CreateDateFitler;
|
||||
|
||||
const factory GridFilterEvent.createDateFilterInRange({
|
||||
required String fieldId,
|
||||
required DateFilterCondition condition,
|
||||
required int start,
|
||||
required int end,
|
||||
}) = _CreateDateFitlerInRange;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class GridFilterState with _$GridFilterState {
|
||||
const factory GridFilterState({
|
||||
required List<FilterPB> filters,
|
||||
}) = _GridFilterState;
|
||||
|
||||
factory GridFilterState.initial() => const GridFilterState(
|
||||
filters: [],
|
||||
);
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:app_flowy/core/grid_notification.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/dart_notification.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/filter_changeset.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
typedef UpdateFilterNotifiedValue
|
||||
= Either<FilterChangesetNotificationPB, FlowyError>;
|
||||
|
||||
class FilterListener {
|
||||
final String viewId;
|
||||
|
||||
PublishNotifier<UpdateFilterNotifiedValue>? _filterNotifier =
|
||||
PublishNotifier();
|
||||
GridNotificationListener? _listener;
|
||||
FilterListener({required this.viewId});
|
||||
|
||||
void start({
|
||||
required void Function(UpdateFilterNotifiedValue) onFilterChanged,
|
||||
}) {
|
||||
_filterNotifier?.addPublishListener(onFilterChanged);
|
||||
_listener = GridNotificationListener(
|
||||
objectId: viewId,
|
||||
handler: _handler,
|
||||
);
|
||||
}
|
||||
|
||||
void _handler(
|
||||
GridNotification ty,
|
||||
Either<Uint8List, FlowyError> result,
|
||||
) {
|
||||
switch (ty) {
|
||||
case GridNotification.DidUpdateFilter:
|
||||
result.fold(
|
||||
(payload) => _filterNotifier?.value =
|
||||
left(FilterChangesetNotificationPB.fromBuffer(payload)),
|
||||
(error) => _filterNotifier?.value = right(error),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
await _listener?.stop();
|
||||
_filterNotifier?.dispose();
|
||||
_filterNotifier = null;
|
||||
}
|
||||
}
|
@ -0,0 +1,202 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/dispatch/dispatch.dart';
|
||||
import 'package:flowy_sdk/log.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/checkbox_filter.pbserver.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/date_filter.pbserver.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/number_filter.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/select_option_filter.pbserver.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/setting_entities.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/text_filter.pb.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-grid/util.pb.dart';
|
||||
import 'package:fixnum/fixnum.dart' as $fixnum;
|
||||
|
||||
class FilterFFIService {
|
||||
final String viewId;
|
||||
const FilterFFIService({required this.viewId});
|
||||
|
||||
Future<Either<List<FilterPB>, FlowyError>> getAllFilters() {
|
||||
final payload = GridIdPB()..value = viewId;
|
||||
|
||||
return GridEventGetAllFilters(payload).send().then((result) {
|
||||
return result.fold(
|
||||
(repeated) => left(repeated.items),
|
||||
(r) => right(r),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> createTextFilter({
|
||||
required String fieldId,
|
||||
required TextFilterCondition condition,
|
||||
String content = "",
|
||||
}) {
|
||||
final filter = TextFilterPB()
|
||||
..condition = condition
|
||||
..content = content;
|
||||
|
||||
return createFilter(
|
||||
fieldId: fieldId,
|
||||
fieldType: FieldType.RichText,
|
||||
data: filter.writeToBuffer(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> createCheckboxFilter({
|
||||
required String fieldId,
|
||||
required CheckboxFilterCondition condition,
|
||||
}) {
|
||||
final filter = CheckboxFilterPB()..condition = condition;
|
||||
|
||||
return createFilter(
|
||||
fieldId: fieldId,
|
||||
fieldType: FieldType.Checkbox,
|
||||
data: filter.writeToBuffer(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> createNumberFilter({
|
||||
required String fieldId,
|
||||
required NumberFilterCondition condition,
|
||||
String content = "",
|
||||
}) {
|
||||
final filter = NumberFilterPB()
|
||||
..condition = condition
|
||||
..content = content;
|
||||
|
||||
return createFilter(
|
||||
fieldId: fieldId,
|
||||
fieldType: FieldType.Number,
|
||||
data: filter.writeToBuffer(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> createDateFilter({
|
||||
required String fieldId,
|
||||
required DateFilterCondition condition,
|
||||
int? start,
|
||||
int? end,
|
||||
int? timestamp,
|
||||
}) {
|
||||
var filter = DateFilterPB();
|
||||
if (timestamp != null) {
|
||||
filter.timestamp = $fixnum.Int64(timestamp);
|
||||
} else {
|
||||
if (start != null && end != null) {
|
||||
filter.start = $fixnum.Int64(start);
|
||||
filter.end = $fixnum.Int64(end);
|
||||
} else {
|
||||
throw Exception(
|
||||
"Start and end should not be null if the timestamp is null");
|
||||
}
|
||||
}
|
||||
|
||||
return createFilter(
|
||||
fieldId: fieldId,
|
||||
fieldType: FieldType.DateTime,
|
||||
data: filter.writeToBuffer(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> createURLFilter({
|
||||
required String fieldId,
|
||||
required TextFilterCondition condition,
|
||||
String content = "",
|
||||
}) {
|
||||
final filter = TextFilterPB()
|
||||
..condition = condition
|
||||
..content = content;
|
||||
|
||||
return createFilter(
|
||||
fieldId: fieldId,
|
||||
fieldType: FieldType.URL,
|
||||
data: filter.writeToBuffer(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> createSingleSelectFilter({
|
||||
required String fieldId,
|
||||
required SelectOptionCondition condition,
|
||||
List<String> optionIds = const [],
|
||||
}) {
|
||||
final filter = SelectOptionFilterPB()
|
||||
..condition = condition
|
||||
..optionIds.addAll(optionIds);
|
||||
|
||||
return createFilter(
|
||||
fieldId: fieldId,
|
||||
fieldType: FieldType.SingleSelect,
|
||||
data: filter.writeToBuffer(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> createMultiSelectFilter({
|
||||
required String fieldId,
|
||||
required SelectOptionCondition condition,
|
||||
List<String> optionIds = const [],
|
||||
}) {
|
||||
final filter = SelectOptionFilterPB()
|
||||
..condition = condition
|
||||
..optionIds.addAll(optionIds);
|
||||
|
||||
return createFilter(
|
||||
fieldId: fieldId,
|
||||
fieldType: FieldType.MultiSelect,
|
||||
data: filter.writeToBuffer(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> createFilter({
|
||||
required String fieldId,
|
||||
required FieldType fieldType,
|
||||
required List<int> data,
|
||||
}) {
|
||||
TextFilterCondition.DoesNotContain.value;
|
||||
|
||||
final insertFilterPayload = CreateFilterPayloadPB.create()
|
||||
..fieldId = fieldId
|
||||
..fieldType = fieldType
|
||||
..data = data;
|
||||
|
||||
final payload = GridSettingChangesetPB.create()
|
||||
..gridId = viewId
|
||||
..insertFilter = insertFilterPayload;
|
||||
return GridEventUpdateGridSetting(payload).send().then((result) {
|
||||
return result.fold(
|
||||
(l) => left(l),
|
||||
(err) {
|
||||
Log.error(err);
|
||||
return right(err);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<Either<Unit, FlowyError>> deleteFilter({
|
||||
required String fieldId,
|
||||
required String filterId,
|
||||
required FieldType fieldType,
|
||||
}) {
|
||||
TextFilterCondition.DoesNotContain.value;
|
||||
|
||||
final deleteFilterPayload = DeleteFilterPayloadPB.create()
|
||||
..fieldId = fieldId
|
||||
..filterId = filterId
|
||||
..fieldType = fieldType;
|
||||
|
||||
final payload = GridSettingChangesetPB.create()
|
||||
..gridId = viewId
|
||||
..deleteFilter = deleteFilterPayload;
|
||||
return GridEventUpdateGridSetting(payload).send().then((result) {
|
||||
return result.fold(
|
||||
(l) => left(l),
|
||||
(err) {
|
||||
Log.error(err);
|
||||
return right(err);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user