import 'dart:collection'; import 'package:app_flowy/plugins/grid/application/block/block_cache.dart'; import 'package:app_flowy/plugins/grid/application/field/field_cache.dart'; import 'package:app_flowy/plugins/grid/application/grid_service.dart'; import 'package:app_flowy/plugins/grid/application/row/row_cache.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart'; import 'dart:async'; import 'package:dartz/dartz.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/protobuf.dart'; typedef OnFieldsChanged = void Function(UnmodifiableListView); typedef OnGridChanged = void Function(GridPB); typedef DidLoadGroups = void Function(List); typedef OnRowsChanged = void Function( List, RowsChangedReason, ); typedef OnError = void Function(FlowyError); class BoardDataController { final String gridId; final GridFFIService _gridFFIService; final GridFieldCache fieldCache; // key: the block id final LinkedHashMap _blocks; LinkedHashMap get blocks => _blocks; OnFieldsChanged? _onFieldsChanged; OnGridChanged? _onGridChanged; DidLoadGroups? _didLoadGroup; OnRowsChanged? _onRowsChanged; OnError? _onError; List get rowInfos { final List rows = []; for (var block in _blocks.values) { rows.addAll(block.rows); } return rows; } BoardDataController({required ViewPB view}) : gridId = view.id, _blocks = LinkedHashMap.new(), _gridFFIService = GridFFIService(gridId: view.id), fieldCache = GridFieldCache(gridId: view.id); void addListener({ OnGridChanged? onGridChanged, OnFieldsChanged? onFieldsChanged, DidLoadGroups? didLoadGroups, OnRowsChanged? onRowsChanged, OnError? onError, }) { _onGridChanged = onGridChanged; _onFieldsChanged = onFieldsChanged; _didLoadGroup = didLoadGroups; _onRowsChanged = onRowsChanged; _onError = onError; fieldCache.addListener(onFields: (fields) { _onFieldsChanged?.call(UnmodifiableListView(fields)); }); } Future> loadData() async { final result = await _gridFFIService.loadGrid(); return Future( () => result.fold( (grid) async { _onGridChanged?.call(grid); return await _loadFields(grid).then((result) { return result.fold( (l) { _loadGroups(grid.blocks); return left(l); }, (err) => right(err), ); }); }, (err) => right(err), ), ); } Future> createBoardCard(String groupId) { return _gridFFIService.createBoardCard(groupId); } Future dispose() async { await _gridFFIService.closeGrid(); await fieldCache.dispose(); for (final blockCache in _blocks.values) { blockCache.dispose(); } } Future> _loadFields(GridPB grid) async { final result = await _gridFFIService.getFields(fieldIds: grid.fields); return Future( () => result.fold( (fields) { fieldCache.fields = fields.items; _onFieldsChanged?.call(UnmodifiableListView(fieldCache.fields)); return left(unit); }, (err) => right(err), ), ); } Future _loadGroups(List blocks) async { for (final block in blocks) { final cache = GridBlockCache( gridId: gridId, block: block, fieldCache: fieldCache, ); cache.addListener(onRowsChanged: (reason) { _onRowsChanged?.call(rowInfos, reason); }); _blocks[block.id] = cache; } final result = await _gridFFIService.loadGroups(); return Future( () => result.fold( (groups) { _didLoadGroup?.call(groups.items); }, (err) => _onError?.call(err), ), ); } }