import 'dart:collection'; import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/block_entities.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid/grid_entities.pb.dart'; import 'dart:async'; import 'package:dartz/dartz.dart'; import 'block/block_cache.dart'; import 'field/field_controller.dart'; import 'prelude.dart'; import 'row/row_cache.dart'; typedef OnFieldsChanged = void Function(UnmodifiableListView); typedef OnGridChanged = void Function(GridPB); typedef OnRowsChanged = void Function( List rowInfos, RowsChangedReason, ); typedef ListenOnRowChangedCondition = bool Function(); class GridDataController { final String gridId; final GridFFIService _gridFFIService; final GridFieldController fieldController; // key: the block id final LinkedHashMap _blocks; UnmodifiableMapView get blocks => UnmodifiableMapView(_blocks); OnRowsChanged? _onRowChanged; OnFieldsChanged? _onFieldsChanged; OnGridChanged? _onGridChanged; List get rowInfos { final List rows = []; for (var block in _blocks.values) { rows.addAll(block.rows); } return rows; } GridDataController({required ViewPB view}) : gridId = view.id, // ignore: prefer_collection_literals _blocks = LinkedHashMap(), _gridFFIService = GridFFIService(gridId: view.id), fieldController = GridFieldController(gridId: view.id); void addListener({ required OnGridChanged onGridChanged, required OnRowsChanged onRowsChanged, required OnFieldsChanged onFieldsChanged, }) { _onGridChanged = onGridChanged; _onRowChanged = onRowsChanged; _onFieldsChanged = onFieldsChanged; fieldController.addListener(onFields: (fields) { _onFieldsChanged?.call(UnmodifiableListView(fields)); }); } Future> loadData() async { final result = await _gridFFIService.loadGrid(); return Future( () => result.fold( (grid) async { _initialBlocks(grid.blocks); _onGridChanged?.call(grid); return await fieldController.loadFields(fieldIds: grid.fields); }, (err) => right(err), ), ); } void createRow() { _gridFFIService.createRow(); } Future dispose() async { await _gridFFIService.closeGrid(); await fieldController.dispose(); for (final blockCache in _blocks.values) { blockCache.dispose(); } } void _initialBlocks(List blocks) { for (final block in blocks) { if (_blocks[block.id] != null) { Log.warn("Initial duplicate block's cache: ${block.id}"); return; } final cache = GridBlockCache( gridId: gridId, block: block, fieldController: fieldController, ); cache.addListener( onRowsChanged: (reason) { _onRowChanged?.call(rowInfos, reason); }, ); _blocks[block.id] = cache; } } }