veilidchat/lib/tools/state_follower.dart
2024-02-24 22:27:59 -05:00

59 lines
1.8 KiB
Dart

import 'dart:async';
import 'package:async_tools/async_tools.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
// Mixin that automatically keeps two blocs/cubits in sync with each other
// Useful for having a BlocMapCubit 'follow' the state of another input cubit.
// As the state of the input cubit changes, the BlocMapCubit can add/remove
// mapped Cubits that automatically process the input state reactively.
//
// S = Input state type
// K = Key derived from elements of input state
// V = Value derived from elements of input state
abstract mixin class StateFollower<S extends Object, K, V> {
void follow({
required S initialInputState,
required Stream<S> stream,
}) {
//
_lastInputStateMap = getStateMap(initialInputState);
_subscription = stream.listen(_updateFollow);
}
Future<void> close() async {
await _subscription.cancel();
}
IMap<K, V> getStateMap(S state);
Future<void> removeFromState(K key);
Future<void> updateState(K key, V value);
void _updateFollow(S newInputState) {
_singleStateProcessor.updateState(getStateMap(newInputState),
closure: (newStateMap) async {
for (final k in _lastInputStateMap.keys) {
if (!newStateMap.containsKey(k)) {
// deleted
await removeFromState(k);
}
}
for (final newEntry in newStateMap.entries) {
final v = _lastInputStateMap.get(newEntry.key);
if (v == null || v != newEntry.value) {
// added or changed
await updateState(newEntry.key, newEntry.value);
}
}
// Keep this state map for the next time
_lastInputStateMap = newStateMap;
});
}
late IMap<K, V> _lastInputStateMap;
final SingleStateProcessor<IMap<K, V>> _singleStateProcessor =
SingleStateProcessor();
late final StreamSubscription<S> _subscription;
}