| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 | 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-database/calendar_entities.pb.dart';import 'package:appflowy_backend/protobuf/flowy-database/field_entities.pbenum.dart';import 'package:appflowy_backend/protobuf/flowy-database/group.pb.dart';import 'package:appflowy_backend/protobuf/flowy-database/group_changeset.pb.dart';import 'package:appflowy_backend/protobuf/flowy-database/row_entities.pb.dart';import 'package:appflowy_backend/protobuf/flowy-database/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';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 addListener({    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({    String? 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>> moveRow({    required RowPB fromRow,    required String groupId,    RowPB? toRow,  }) {    return _databaseViewBackendSvc.moveRow(      fromRowId: fromRow.id,      toGroupId: groupId,      toRowId: toRow?.id,    );  }  Future<Either<Unit, FlowyError>> moveGroup({    required String fromGroupId,    required String toGroupId,  }) {    return _databaseViewBackendSvc.moveGroup(      fromGroupId: fromGroupId,      toGroupId: toGroupId,    );  }  Future<void> updateCalenderLayoutSetting(    CalendarLayoutSettingsPB 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();  }  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.addListener(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;  }}
 |