Files
Client/lib/state/app/modules/settings/bloc/settings_cubit.dart
T

89 lines
2.8 KiB
Dart

import 'dart:async';
import 'dart:developer';
import 'package:easy_debounce/easy_debounce.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import '../../../../../storage/settings.dart';
import '../../../../../view/pages/settings/data/default_settings.dart';
import '../../app_modules.dart';
class SettingsCubit extends HydratedCubit<Settings> {
static const _debounceTag = 'settings_persist';
bool _emitScheduled = false;
SettingsCubit() : super(DefaultSettings.get());
Settings val({bool write = false}) {
if (write) {
// Defer the emit until the synchronous mutation on the returned object
// has finished. Without this scheduleMicrotask the cubit emits a copy
// captured *before* the assignment runs, so listeners (and HydratedBloc
// persistence) see the old value on the first emit.
if (!_emitScheduled) {
_emitScheduled = true;
scheduleMicrotask(() {
_emitScheduled = false;
_emitFreshInstance();
});
}
EasyDebounce.debounce(_debounceTag, const Duration(milliseconds: 500), _emitFreshInstance);
}
return state;
}
void _emitFreshInstance() {
try {
emit(Settings.fromJson(state.toJson()));
} catch (e) {
log('Failed to refresh settings state: $e');
}
}
Future<void> reset() async {
emit(DefaultSettings.get());
}
@override
Settings fromJson(Map<String, dynamic> json) {
try {
return _appendNewModules(Settings.fromJson(json));
} catch (_) {
try {
return _appendNewModules(Settings.fromJson(_mergeSettings(json, DefaultSettings.get().toJson())));
} catch (_) {
return DefaultSettings.get();
}
}
}
// Modules added in newer app versions won't appear in a previously persisted
// moduleOrder. Append any enum value that is neither ordered nor hidden so it
// becomes visible in the "Mehr" menu without forcing a full settings reset.
Settings _appendNewModules(Settings s) {
final order = s.modulesSettings.moduleOrder;
final hidden = s.modulesSettings.hiddenModules;
final missing = Modules.values.where((m) => !order.contains(m) && !hidden.contains(m));
if (missing.isEmpty) return s;
s.modulesSettings.moduleOrder = [...order, ...missing];
return s;
}
@override
Map<String, dynamic>? toJson(Settings state) => state.toJson();
Map<String, dynamic> _mergeSettings(Map<String, dynamic> oldMap, Map<String, dynamic> newMap) {
final merged = Map<String, dynamic>.from(newMap);
oldMap.forEach((key, value) {
if (merged.containsKey(key)) {
if (value is Map<String, dynamic> && merged[key] is Map<String, dynamic>) {
merged[key] = _mergeSettings(value, merged[key]);
} else {
merged[key] = value;
}
}
});
return merged;
}
}