123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 |
- import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
- import 'package:appflowy/plugins/database_view/application/layout/calendar_setting_listener.dart';
- import 'package:appflowy/plugins/database_view/application/view/view_cache.dart';
- import 'package:appflowy_backend/log.dart';
- import 'package:appflowy_backend/protobuf/flowy-database2/calendar_entities.pb.dart';
- import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
- import 'package:appflowy_backend/protobuf/flowy-database2/group.pb.dart';
- import 'package:appflowy_backend/protobuf/flowy-database2/group_changeset.pb.dart';
- import 'package:appflowy_backend/protobuf/flowy-database2/row_entities.pb.dart';
- import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pb.dart';
- import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
- import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
- import 'package:collection/collection.dart';
- import 'dart:async';
- import 'package:dartz/dartz.dart';
- import 'database_view_service.dart';
- import 'defines.dart';
- import 'layout/layout_setting_listener.dart';
- import 'row/row_cache.dart';
- import 'group/group_listener.dart';
- import 'row/row_service.dart';
- typedef OnGroupByField = void Function(List<GroupPB>);
- typedef OnUpdateGroup = void Function(List<GroupPB>);
- typedef OnDeleteGroup = void Function(List<String>);
- typedef OnInsertGroup = void Function(InsertedGroupPB);
- class GroupCallbacks {
- final OnGroupByField? onGroupByField;
- final OnUpdateGroup? onUpdateGroup;
- final OnDeleteGroup? onDeleteGroup;
- final OnInsertGroup? onInsertGroup;
- GroupCallbacks({
- this.onGroupByField,
- this.onUpdateGroup,
- this.onDeleteGroup,
- this.onInsertGroup,
- });
- }
- class LayoutCallbacks {
- final void Function(LayoutSettingPB) onLayoutChanged;
- final void Function(LayoutSettingPB) onLoadLayout;
- LayoutCallbacks({
- required this.onLayoutChanged,
- required this.onLoadLayout,
- });
- }
- class CalendarLayoutCallbacks {
- final void Function(LayoutSettingPB) onCalendarLayoutChanged;
- CalendarLayoutCallbacks({required this.onCalendarLayoutChanged});
- }
- class DatabaseCallbacks {
- OnDatabaseChanged? onDatabaseChanged;
- OnFieldsChanged? onFieldsChanged;
- OnFiltersChanged? onFiltersChanged;
- OnRowsChanged? onRowsChanged;
- OnRowsDeleted? onRowsDeleted;
- OnRowsUpdated? onRowsUpdated;
- OnRowsCreated? onRowsCreated;
- DatabaseCallbacks({
- this.onDatabaseChanged,
- this.onRowsChanged,
- this.onFieldsChanged,
- this.onFiltersChanged,
- this.onRowsUpdated,
- this.onRowsDeleted,
- this.onRowsCreated,
- });
- }
- class DatabaseController {
- final String viewId;
- final DatabaseViewBackendService _databaseViewBackendSvc;
- final FieldController fieldController;
- late DatabaseViewCache _viewCache;
- final DatabaseLayoutPB layoutType;
- // Callbacks
- DatabaseCallbacks? _databaseCallbacks;
- GroupCallbacks? _groupCallbacks;
- LayoutCallbacks? _layoutCallbacks;
- CalendarLayoutCallbacks? _calendarLayoutCallbacks;
- // Getters
- RowCache get rowCache => _viewCache.rowCache;
- // Listener
- final DatabaseGroupListener groupListener;
- final DatabaseLayoutListener layoutListener;
- final DatabaseCalendarLayoutListener calendarLayoutListener;
- DatabaseController({required ViewPB view, required this.layoutType})
- : viewId = view.id,
- _databaseViewBackendSvc = DatabaseViewBackendService(viewId: view.id),
- fieldController = FieldController(viewId: view.id),
- groupListener = DatabaseGroupListener(view.id),
- layoutListener = DatabaseLayoutListener(view.id),
- calendarLayoutListener = DatabaseCalendarLayoutListener(view.id) {
- _viewCache = DatabaseViewCache(
- viewId: viewId,
- fieldController: fieldController,
- );
- _listenOnRowsChanged();
- _listenOnFieldsChanged();
- _listenOnGroupChanged();
- _listenOnLayoutChanged();
- if (layoutType == DatabaseLayoutPB.Calendar) {
- _listenOnCalendarLayoutChanged();
- }
- }
- void setListener({
- DatabaseCallbacks? onDatabaseChanged,
- LayoutCallbacks? onLayoutChanged,
- GroupCallbacks? onGroupChanged,
- CalendarLayoutCallbacks? onCalendarLayoutChanged,
- }) {
- _layoutCallbacks = onLayoutChanged;
- _databaseCallbacks = onDatabaseChanged;
- _groupCallbacks = onGroupChanged;
- _calendarLayoutCallbacks = onCalendarLayoutChanged;
- }
- Future<Either<Unit, FlowyError>> open() async {
- return _databaseViewBackendSvc.openGrid().then((result) {
- return result.fold(
- (database) async {
- _databaseCallbacks?.onDatabaseChanged?.call(database);
- _viewCache.rowCache.setInitialRows(database.rows);
- return await fieldController
- .loadFields(
- fieldIds: database.fields,
- )
- .then(
- (result) {
- return result.fold(
- (l) => Future(() async {
- await _loadGroups();
- await _loadLayoutSetting();
- return left(l);
- }),
- (err) => right(err),
- );
- },
- );
- },
- (err) => right(err),
- );
- });
- }
- Future<Either<RowPB, FlowyError>> createRow({
- RowId? startRowId,
- String? groupId,
- void Function(RowDataBuilder builder)? withCells,
- }) {
- Map<String, String>? cellDataByFieldId;
- if (withCells != null) {
- final rowBuilder = RowDataBuilder();
- withCells(rowBuilder);
- cellDataByFieldId = rowBuilder.build();
- }
- return _databaseViewBackendSvc.createRow(
- startRowId: startRowId,
- groupId: groupId,
- cellDataByFieldId: cellDataByFieldId,
- );
- }
- Future<Either<Unit, FlowyError>> moveGroupRow({
- required RowPB fromRow,
- required String groupId,
- RowPB? toRow,
- }) {
- return _databaseViewBackendSvc.moveGroupRow(
- fromRowId: fromRow.id,
- toGroupId: groupId,
- toRowId: toRow?.id,
- );
- }
- Future<Either<Unit, FlowyError>> moveRow({
- required RowPB fromRow,
- required RowPB toRow,
- }) {
- return _databaseViewBackendSvc.moveRow(
- fromRowId: fromRow.id,
- toRowId: toRow.id,
- );
- }
- Future<Either<Unit, FlowyError>> moveGroup({
- required String fromGroupId,
- required String toGroupId,
- }) {
- return _databaseViewBackendSvc.moveGroup(
- fromGroupId: fromGroupId,
- toGroupId: toGroupId,
- );
- }
- Future<void> updateCalenderLayoutSetting(
- CalendarLayoutSettingPB layoutSetting,
- ) async {
- await _databaseViewBackendSvc
- .updateLayoutSetting(calendarLayoutSetting: layoutSetting)
- .then((result) {
- result.fold((l) => null, (r) => Log.error(r));
- });
- }
- Future<void> dispose() async {
- await _databaseViewBackendSvc.closeView();
- await fieldController.dispose();
- await groupListener.stop();
- await _viewCache.dispose();
- _databaseCallbacks = null;
- _groupCallbacks = null;
- _layoutCallbacks = null;
- _calendarLayoutCallbacks = null;
- }
- Future<void> _loadGroups() async {
- final result = await _databaseViewBackendSvc.loadGroups();
- return Future(
- () => result.fold(
- (groups) {
- _groupCallbacks?.onGroupByField?.call(groups.items);
- },
- (err) => Log.error(err),
- ),
- );
- }
- Future<void> _loadLayoutSetting() async {
- _databaseViewBackendSvc.getLayoutSetting(layoutType).then((result) {
- result.fold(
- (l) {
- _layoutCallbacks?.onLoadLayout(l);
- },
- (r) => Log.error(r),
- );
- });
- }
- void _listenOnRowsChanged() {
- final callbacks = DatabaseViewCallbacks(
- onRowsChanged: (rows, rowByRowId, reason) {
- _databaseCallbacks?.onRowsChanged?.call(rows, rowByRowId, reason);
- },
- onRowsDeleted: (ids) {
- _databaseCallbacks?.onRowsDeleted?.call(ids);
- },
- onRowsUpdated: (ids) {
- _databaseCallbacks?.onRowsUpdated?.call(ids);
- },
- onRowsCreated: (ids) {
- _databaseCallbacks?.onRowsCreated?.call(ids);
- },
- );
- _viewCache.setListener(callbacks);
- }
- void _listenOnFieldsChanged() {
- fieldController.addListener(
- onReceiveFields: (fields) {
- _databaseCallbacks?.onFieldsChanged?.call(UnmodifiableListView(fields));
- },
- onFilters: (filters) {
- _databaseCallbacks?.onFiltersChanged?.call(filters);
- },
- );
- }
- void _listenOnGroupChanged() {
- groupListener.start(
- onNumOfGroupsChanged: (result) {
- result.fold(
- (changeset) {
- if (changeset.updateGroups.isNotEmpty) {
- _groupCallbacks?.onUpdateGroup?.call(changeset.updateGroups);
- }
- if (changeset.deletedGroups.isNotEmpty) {
- _groupCallbacks?.onDeleteGroup?.call(changeset.deletedGroups);
- }
- for (final insertedGroup in changeset.insertedGroups) {
- _groupCallbacks?.onInsertGroup?.call(insertedGroup);
- }
- },
- (r) => Log.error(r),
- );
- },
- onGroupByNewField: (result) {
- result.fold(
- (groups) {
- _groupCallbacks?.onGroupByField?.call(groups);
- },
- (r) => Log.error(r),
- );
- },
- );
- }
- void _listenOnLayoutChanged() {
- layoutListener.start(
- onLayoutChanged: (result) {
- result.fold(
- (l) {
- _layoutCallbacks?.onLayoutChanged(l);
- },
- (r) => Log.error(r),
- );
- },
- );
- }
- void _listenOnCalendarLayoutChanged() {
- calendarLayoutListener.start(
- onCalendarLayoutChanged: (result) {
- result.fold(
- (l) {
- _calendarLayoutCallbacks?.onCalendarLayoutChanged(l);
- },
- (r) => Log.error(r),
- );
- },
- );
- }
- }
- class RowDataBuilder {
- final _cellDataByFieldId = <String, String>{};
- void insertText(FieldInfo fieldInfo, String text) {
- assert(fieldInfo.fieldType == FieldType.RichText);
- _cellDataByFieldId[fieldInfo.field.id] = text;
- }
- void insertNumber(FieldInfo fieldInfo, int num) {
- assert(fieldInfo.fieldType == FieldType.Number);
- _cellDataByFieldId[fieldInfo.field.id] = num.toString();
- }
- void insertDate(FieldInfo fieldInfo, DateTime date) {
- assert(fieldInfo.fieldType == FieldType.DateTime);
- final timestamp = date.millisecondsSinceEpoch ~/ 1000;
- _cellDataByFieldId[fieldInfo.field.id] = timestamp.toString();
- }
- Map<String, String> build() {
- return _cellDataByFieldId;
- }
- }
|