import 'dart:collection'; import 'package:app_flowy/plugins/grid/application/view/grid_view_cache.dart'; import 'package:app_flowy/plugins/grid/application/field/field_controller.dart'; import 'package:app_flowy/plugins/grid/application/grid_service.dart'; import 'package:app_flowy/plugins/grid/application/row/row_cache.dart'; import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'dart:async'; import 'package:dartz/dartz.dart'; import 'package:appflowy_backend/protobuf/flowy-database/protobuf.dart'; import 'board_listener.dart'; typedef OnFieldsChanged = void Function(UnmodifiableListView); typedef OnGridChanged = void Function(DatabasePB); typedef DidLoadGroups = void Function(List); typedef OnUpdatedGroup = void Function(List); typedef OnDeletedGroup = void Function(List); typedef OnInsertedGroup = void Function(InsertedGroupPB); typedef OnResetGroups = void Function(List); typedef OnRowsChanged = void Function( List, RowsChangedReason, ); typedef OnError = void Function(FlowyError); class BoardDataController { final String viewId; final DatabaseFFIService _databaseFFIService; final GridFieldController fieldController; final BoardListener _listener; late DatabaseViewCache _viewCache; OnFieldsChanged? _onFieldsChanged; OnGridChanged? _onGridChanged; DidLoadGroups? _didLoadGroup; OnRowsChanged? _onRowsChanged; OnError? _onError; List get rowInfos => _viewCache.rowInfos; GridRowCache get rowCache => _viewCache.rowCache; BoardDataController({required ViewPB view}) : viewId = view.id, _listener = BoardListener(view.id), _databaseFFIService = DatabaseFFIService(databaseId: view.id), fieldController = GridFieldController(databaseId: view.id) { // _viewCache = DatabaseViewCache( databaseId: view.id, fieldController: fieldController, ); _viewCache.addListener(onRowsChanged: (reason) { _onRowsChanged?.call(rowInfos, reason); }); } void addListener({ required OnGridChanged onGridChanged, OnFieldsChanged? onFieldsChanged, required DidLoadGroups didLoadGroups, OnRowsChanged? onRowsChanged, required OnUpdatedGroup onUpdatedGroup, required OnDeletedGroup onDeletedGroup, required OnInsertedGroup onInsertedGroup, required OnResetGroups onResetGroups, required OnError? onError, }) { _onGridChanged = onGridChanged; _onFieldsChanged = onFieldsChanged; _didLoadGroup = didLoadGroups; _onRowsChanged = onRowsChanged; _onError = onError; fieldController.addListener(onFields: (fields) { _onFieldsChanged?.call(UnmodifiableListView(fields)); }); _listener.start( onBoardChanged: (result) { result.fold( (changeset) { if (changeset.updateGroups.isNotEmpty) { onUpdatedGroup.call(changeset.updateGroups); } if (changeset.deletedGroups.isNotEmpty) { onDeletedGroup.call(changeset.deletedGroups); } for (final insertedGroup in changeset.insertedGroups) { onInsertedGroup.call(insertedGroup); } }, (e) => _onError?.call(e), ); }, onGroupByNewField: (result) { result.fold( (groups) => onResetGroups(groups), (e) => _onError?.call(e), ); }, ); } Future> openGrid() async { final result = await _databaseFFIService.openGrid(); return result.fold( (grid) async { _onGridChanged?.call(grid); return fieldController.loadFields(fieldIds: grid.fields).then((result) { return result.fold( (l) => Future(() async { await _loadGroups(); _viewCache.rowCache.initializeRows(grid.rows); return left(l); }), (err) => right(err), ); }); }, (err) => right(err), ); } Future> createBoardCard(String groupId, {String? startRowId}) { return _databaseFFIService.createBoardCard(groupId, startRowId); } Future dispose() async { await _viewCache.dispose(); await _databaseFFIService.closeGrid(); await fieldController.dispose(); } Future _loadGroups() async { final result = await _databaseFFIService.loadGroups(); return Future( () => result.fold( (groups) { _didLoadGroup?.call(groups.items); }, (err) => _onError?.call(err), ), ); } }