provider lifecycle hardenings

This commit is contained in:
Fabian Freund
2026-05-06 18:32:45 +02:00
parent d5f977a3d2
commit fee15cb8d2
25 changed files with 246 additions and 94 deletions
@@ -60,7 +60,7 @@ class AppInitializationService extends _$AppInitializationService {
} }
Future<void> initialize() async { Future<void> initialize() async {
state = await Result.fromAsync(() async { final result = await Result.fromAsync(() async {
final errors = <ErrorMessage>[]; final errors = <ErrorMessage>[];
await ref.read(formatProvider.future); await ref.read(formatProvider.future);
@@ -80,6 +80,12 @@ class AppInitializationService extends _$AppInitializationService {
return (initialized: true, stage: null, errors: errors); return (initialized: true, stage: null, errors: errors);
}); });
if (!ref.mounted) {
return;
}
state = result;
} }
@override @override
@@ -54,7 +54,7 @@ final class AppInitializationServiceProvider
} }
String _$appInitializationServiceHash() => String _$appInitializationServiceHash() =>
r'c26f968d53b4ea18f7be890c9610c9bf9d300322'; r'bf4c472d5b0b758d703b8ca40763cf2a59712a4c';
abstract class _$AppInitializationService abstract class _$AppInitializationService
extends extends
@@ -80,7 +80,14 @@ class AddonDetails extends _$AddonDetails {
Future<void> _run(Future<AddonInfo?> Function() action) async { Future<void> _run(Future<AddonInfo?> Function() action) async {
state = const AsyncLoading<AddonInfo?>(); state = const AsyncLoading<AddonInfo?>();
state = await AsyncValue.guard(action);
final result = await AsyncValue.guard(action);
if (!ref.mounted) {
return;
}
state = result;
ref.invalidate(addonListProvider); ref.invalidate(addonListProvider);
} }
@@ -239,6 +246,10 @@ class AddonUpdateCheck extends _$AddonUpdateCheck {
: AddonUpdateRunDone(attempt?.message); : AddonUpdateRunDone(attempt?.message);
}); });
if (!ref.mounted) {
return result.value ?? const AddonUpdateRunFailed();
}
state = result; state = result;
ref.invalidate(addonDetailsProvider(addonId)); ref.invalidate(addonDetailsProvider(addonId));
ref.invalidate(lastAddonUpdateAttemptProvider(addonId)); ref.invalidate(lastAddonUpdateAttemptProvider(addonId));
@@ -261,26 +272,40 @@ class AddonList extends _$AddonList {
} }
Future<void> install(AddonInfo addon) async { Future<void> install(AddonInfo addon) async {
ref.read(addonBusyIdsProvider.notifier).add(addon.id); final busyIds = ref.read(addonBusyIdsProvider.notifier);
busyIds.add(addon.id);
try { try {
await _service.installAddon(Uri.parse(addon.downloadUrl)); await _service.installAddon(Uri.parse(addon.downloadUrl));
if (!ref.mounted) {
return;
}
ref.invalidate(addonDetailsProvider(addon.id)); ref.invalidate(addonDetailsProvider(addon.id));
ref.invalidateSelf(); ref.invalidateSelf();
await future; await future;
} finally { } finally {
ref.read(addonBusyIdsProvider.notifier).remove(addon.id); busyIds.remove(addon.id);
} }
} }
Future<void> uninstall(AddonInfo addon) async { Future<void> uninstall(AddonInfo addon) async {
ref.read(addonBusyIdsProvider.notifier).add(addon.id); final busyIds = ref.read(addonBusyIdsProvider.notifier);
busyIds.add(addon.id);
try { try {
await _service.uninstallAddon(addon.id); await _service.uninstallAddon(addon.id);
if (!ref.mounted) {
return;
}
ref.invalidate(addonDetailsProvider(addon.id)); ref.invalidate(addonDetailsProvider(addon.id));
ref.invalidateSelf(); ref.invalidateSelf();
await future; await future;
} finally { } finally {
ref.read(addonBusyIdsProvider.notifier).remove(addon.id); busyIds.remove(addon.id);
} }
} }
@@ -292,8 +317,15 @@ class AddonList extends _$AddonList {
@Riverpod() @Riverpod()
class AddonBusyIds extends _$AddonBusyIds { class AddonBusyIds extends _$AddonBusyIds {
void add(String id) => state = {...state, id}; void add(String id) {
void remove(String id) => state = {...state}..remove(id); if (!ref.mounted) return;
state = {...state, id};
}
void remove(String id) {
if (!ref.mounted) return;
state = {...state}..remove(id);
}
@override @override
Set<String> build() => const {}; Set<String> build() => const {};
@@ -329,9 +361,16 @@ class PinnedAddonIds extends _$PinnedAddonIds {
class BulkAddonUpdate extends _$BulkAddonUpdate { class BulkAddonUpdate extends _$BulkAddonUpdate {
Future<void> triggerAll() async { Future<void> triggerAll() async {
state = const AsyncLoading(); state = const AsyncLoading();
state = await AsyncValue.guard(() async {
final result = await AsyncValue.guard(() async {
await ref.read(addonServiceProvider).triggerAllAddonUpdates(); await ref.read(addonServiceProvider).triggerAllAddonUpdates();
}); });
if (!ref.mounted) {
return;
}
state = result;
} }
@override @override
@@ -50,7 +50,7 @@ final class AddonDetailsProvider
} }
} }
String _$addonDetailsHash() => r'26b4a33e9d17aced1d3fb5c6ff28921f611ca5b0'; String _$addonDetailsHash() => r'b8b7b0620d618488227fb6b8ba889279b933b707';
final class AddonDetailsFamily extends $Family final class AddonDetailsFamily extends $Family
with with
@@ -658,7 +658,7 @@ final class AddonUpdateCheckProvider
} }
} }
String _$addonUpdateCheckHash() => r'4ef375b5cd9b0eb89fbbdaff3f93a35482191af5'; String _$addonUpdateCheckHash() => r'496cf9756101c9c36fa4b90f1fd92627aee92177';
final class AddonUpdateCheckFamily extends $Family final class AddonUpdateCheckFamily extends $Family
with with
@@ -739,7 +739,7 @@ final class AddonListProvider
AddonList create() => AddonList(); AddonList create() => AddonList();
} }
String _$addonListHash() => r'7625b69a433d073e186571453e39b557d8b48a5d'; String _$addonListHash() => r'1e60b0b04702e6ed6143ccf19d9e4140d1a1deec';
abstract class _$AddonList extends $AsyncNotifier<List<AddonInfo>> { abstract class _$AddonList extends $AsyncNotifier<List<AddonInfo>> {
FutureOr<List<AddonInfo>> build(); FutureOr<List<AddonInfo>> build();
@@ -791,7 +791,7 @@ final class AddonBusyIdsProvider
} }
} }
String _$addonBusyIdsHash() => r'6f9761320d42b2b5132936797617fdb13b718dd3'; String _$addonBusyIdsHash() => r'aac8fd8784ba296129ffdb17c2d1355c1546140f';
abstract class _$AddonBusyIds extends $Notifier<Set<String>> { abstract class _$AddonBusyIds extends $Notifier<Set<String>> {
Set<String> build(); Set<String> build();
@@ -895,7 +895,7 @@ final class BulkAddonUpdateProvider
} }
} }
String _$bulkAddonUpdateHash() => r'2605711734f9eb6af70e721a4337556a7ea85512'; String _$bulkAddonUpdateHash() => r'ec3cdc136c0ad4a6687516bf14912fd5b96b11b4';
abstract class _$BulkAddonUpdate extends $Notifier<AsyncValue<void>> { abstract class _$BulkAddonUpdate extends $Notifier<AsyncValue<void>> {
AsyncValue<void> build(); AsyncValue<void> build();
@@ -39,6 +39,10 @@ class WebExtensionsState extends _$WebExtensionsState {
: _imageCache = LRUCache(50, onEvict: (image) => image.dispose()); : _imageCache = LRUCache(50, onEvict: (image) => image.dispose());
void _onExtensionUpdate(ExtensionDataEvent event) { void _onExtensionUpdate(ExtensionDataEvent event) {
if (!ref.mounted) {
return;
}
final ExtensionDataEvent(:extensionId, :data) = event; final ExtensionDataEvent(:extensionId, :data) = event;
if (data != null) { if (data != null) {
@@ -81,6 +85,11 @@ class WebExtensionsState extends _$WebExtensionsState {
final image = await tryDecodeImage(bytes); final image = await tryDecodeImage(bytes);
if (!ref.mounted) {
image?.dispose();
return;
}
if (image != null) { if (image != null) {
// set() will evict the old entry via onEvict callback, which handles disposal // set() will evict the old entry via onEvict callback, which handles disposal
_imageCache.set(extensionId, image); _imageCache.set(extensionId, image);
@@ -185,13 +194,13 @@ class WebExtensionsState extends _$WebExtensionsState {
}, },
); );
ref.onDispose(() async { ref.onDispose(() {
// Dispose all cached images // Dispose all cached images
_imageCache.clear(); _imageCache.clear();
// Cancel all stream subscriptions // Cancel all stream subscriptions
for (final sub in subscriptions) { for (final sub in subscriptions) {
await sub.cancel(); unawaited(sub.cancel());
} }
}); });
@@ -62,7 +62,7 @@ final class WebExtensionsStateProvider
} }
String _$webExtensionsStateHash() => String _$webExtensionsStateHash() =>
r'13cbbea409b5b643eb6766689f6b23a4d1701204'; r'e067323e9a0a466e46e0c4d529667950ee62d4ca';
final class WebExtensionsStateFamily extends $Family final class WebExtensionsStateFamily extends $Family
with with
@@ -784,8 +784,9 @@ class TabRepository extends _$TabRepository {
final containerSiteAssignementSub = eventSerivce.siteAssignementEvent.listen( final containerSiteAssignementSub = eventSerivce.siteAssignementEvent.listen(
(event) async { (event) async {
if (event.tabId != null) { final tabId = event.tabId;
final tabState = ref.read(tabStatesProvider)[event.tabId]; if (tabId != null) {
final tabState = ref.read(tabStatesProvider)[tabId];
if (tabState != null) { if (tabState != null) {
final uri = Uri.parse(event.url); final uri = Uri.parse(event.url);
final originUri = event.originUrl.mapNotNull(Uri.parse); final originUri = event.originUrl.mapNotNull(Uri.parse);
@@ -793,52 +794,81 @@ class TabRepository extends _$TabRepository {
final targetContainerId = await ref final targetContainerId = await ref
.read(containerRepositoryProvider.notifier) .read(containerRepositoryProvider.notifier)
.siteAssignedContainerId(Uri.parse(uri.origin)); .siteAssignedContainerId(Uri.parse(uri.origin));
if (!ref.mounted) {
return;
}
final containerData = await targetContainerId.mapNotNull( final containerData = await targetContainerId.mapNotNull(
(id) => ref (id) => ref
.read(containerRepositoryProvider.notifier) .read(containerRepositoryProvider.notifier)
.getContainerData(id), .getContainerData(id),
); );
if (!ref.mounted) {
return;
}
if (containerData != null) { if (containerData != null) {
final currentTabState = ref.read(tabStatesProvider)[tabId];
if (currentTabState == null) {
logger.w('Could not get tab for assignement ${event.url}');
return;
}
final tabIsEmpty = final tabIsEmpty =
tabState.url == TabState.defaultUrl && currentTabState.url == TabState.defaultUrl &&
tabState.historyState.items.isEmpty; currentTabState.historyState.items.isEmpty;
if (event.blocked || tabIsEmpty) { if (event.blocked || tabIsEmpty) {
await addTab( await addTab(
url: uri, url: uri,
tabMode: tabState.tabMode, tabMode: currentTabState.tabMode,
containerSelection: TabContainerSelection.specific( containerSelection: TabContainerSelection.specific(
containerData, containerData,
), ),
parentId: tabState.id, parentId: currentTabState.id,
selectTab: true, selectTab: true,
); );
if (tabState.historyState.items.isEmpty) { if (!ref.mounted) {
await closeTab(tabState.id); return;
}
if (currentTabState.historyState.items.isEmpty) {
await closeTab(currentTabState.id);
} }
} else { } else {
final tabContainerId = await ref final tabContainerId = await ref
.read(tabDataRepositoryProvider.notifier) .read(tabDataRepositoryProvider.notifier)
.getTabContainerId(tabState.id); .getTabContainerId(currentTabState.id);
if (!ref.mounted) {
return;
}
final latestTabState = ref.read(tabStatesProvider)[tabId];
if (latestTabState == null) {
logger.w('Could not get tab for assignement ${event.url}');
return;
}
if (targetContainerId != tabContainerId) { if (targetContainerId != tabContainerId) {
if (originUri == null) { if (originUri == null) {
await ref await ref
.read(tabDataRepositoryProvider.notifier) .read(tabDataRepositoryProvider.notifier)
.assignContainer(tabState.id, containerData); .assignContainer(latestTabState.id, containerData);
} else if (tabState.url == originUri) { } else if (latestTabState.url == originUri) {
await ref await ref
.read(tabDataRepositoryProvider.notifier) .read(tabDataRepositoryProvider.notifier)
.assignContainer( .assignContainer(
tabState.id, latestTabState.id,
containerData, containerData,
closeOldTab: false, closeOldTab: false,
); );
} else { } else {
logger.w( logger.w(
'Could not match origin url for assignment ${tabState.url} to request ${event.originUrl}', 'Could not match origin url for assignment ${latestTabState.url} to request ${event.originUrl}',
); );
} }
} }
@@ -41,7 +41,7 @@ final class TabRepositoryProvider
} }
} }
String _$tabRepositoryHash() => r'a68941d373f348201e3d8b1cab26343acca3852f'; String _$tabRepositoryHash() => r'f458baaf9061143a668470dccd57f32664728951';
abstract class _$TabRepository extends $Notifier<void> { abstract class _$TabRepository extends $Notifier<void> {
void build(); void build();
@@ -42,20 +42,24 @@ class AllowUnsignedExtensions extends _$AllowUnsignedExtensions {
await fixator.unregister(_signatureRequiredPref); await fixator.unregister(_signatureRequiredPref);
await GeckoPrefService().applyPrefs({_signatureRequiredPref: true}); await GeckoPrefService().applyPrefs({_signatureRequiredPref: true});
} }
if (!ref.mounted) {
return;
}
state = AsyncData(allow); state = AsyncData(allow);
} }
@override @override
FutureOr<bool> build() async { FutureOr<bool> build() async {
final fixator = ref.read(preferenceFixatorProvider.notifier);
final prefs = await GeckoPrefService().getPrefs([_signatureRequiredPref]); final prefs = await GeckoPrefService().getPrefs([_signatureRequiredPref]);
final pref = prefs[_signatureRequiredPref]; final pref = prefs[_signatureRequiredPref];
final allowUnsigned = pref?.value == false; final allowUnsigned = pref?.value == false;
// Re-register with fixator to prevent Gecko from resetting // Re-register with fixator to prevent Gecko from resetting
if (allowUnsigned) { if (allowUnsigned) {
await ref await fixator.register(_signatureRequiredPref, false);
.read(preferenceFixatorProvider.notifier)
.register(_signatureRequiredPref, false);
} }
return allowUnsigned; return allowUnsigned;
@@ -68,6 +72,10 @@ class AddonAutoUpdate extends _$AddonAutoUpdate {
final service = ref.read(addonServiceProvider); final service = ref.read(addonServiceProvider);
await service.setAddonAutoUpdateEnabled(enabled: enabled); await service.setAddonAutoUpdateEnabled(enabled: enabled);
if (!ref.mounted) {
return;
}
state = AsyncData(enabled); state = AsyncData(enabled);
} }
@@ -34,7 +34,7 @@ final class AllowUnsignedExtensionsProvider
} }
String _$allowUnsignedExtensionsHash() => String _$allowUnsignedExtensionsHash() =>
r'3b67f51cabf8e2f37d992320c5b93bb61c6dbe1f'; r'ba282219edc9149da886371d873d2a5ee0dfb6dd';
abstract class _$AllowUnsignedExtensions extends $AsyncNotifier<bool> { abstract class _$AllowUnsignedExtensions extends $AsyncNotifier<bool> {
FutureOr<bool> build(); FutureOr<bool> build();
@@ -78,7 +78,7 @@ final class AddonAutoUpdateProvider
AddonAutoUpdate create() => AddonAutoUpdate(); AddonAutoUpdate create() => AddonAutoUpdate();
} }
String _$addonAutoUpdateHash() => r'89791e8b771da715b068bbdbe5c3c24a3dad4194'; String _$addonAutoUpdateHash() => r'0a2661316d1d1345e81df2552925eda3c2bf6ec1';
abstract class _$AddonAutoUpdate extends $AsyncNotifier<bool> { abstract class _$AddonAutoUpdate extends $AsyncNotifier<bool> {
FutureOr<bool> build(); FutureOr<bool> build();
@@ -31,11 +31,16 @@ class PreferenceChangeListener extends _$PreferenceChangeListener {
Stream<GeckoPref> build() async* { Stream<GeckoPref> build() async* {
final events = ref.watch(eventServiceProvider); final events = ref.watch(eventServiceProvider);
ref.onDispose(() {
unawaited(GeckoPrefService().stopObserveChanges());
});
await GeckoPrefService().startObserveChanges(); await GeckoPrefService().startObserveChanges();
ref.onDispose(() async { if (!ref.mounted) {
await GeckoPrefService().stopObserveChanges(); await GeckoPrefService().stopObserveChanges();
}); return;
}
yield* events.prefUpdateEvent; yield* events.prefUpdateEvent;
} }
@@ -34,7 +34,7 @@ final class PreferenceChangeListenerProvider
} }
String _$preferenceChangeListenerHash() => String _$preferenceChangeListenerHash() =>
r'de9759b32a6a6b9bab5656137a365da78633f866'; r'baf2abe1948998a043a8473547a225582dbc8c7b';
abstract class _$PreferenceChangeListener extends $StreamNotifier<GeckoPref> { abstract class _$PreferenceChangeListener extends $StreamNotifier<GeckoPref> {
Stream<GeckoPref> build(); Stream<GeckoPref> build();
@@ -243,8 +243,8 @@ class _PreferenceRepository extends _$PreferenceRepository {
_prefSubject = BehaviorSubject<Map<String, GeckoPref>>(); _prefSubject = BehaviorSubject<Map<String, GeckoPref>>();
ref.onDispose(() async { ref.onDispose(() {
await _prefSubject.close(); unawaited(_prefSubject.close());
}); });
ref.onAddListener(() { ref.onAddListener(() {
@@ -260,19 +260,21 @@ class _PreferenceRepository extends _$PreferenceRepository {
@Riverpod() @Riverpod()
class UnifiedPreferenceSettingsRepository class UnifiedPreferenceSettingsRepository
extends _$UnifiedPreferenceSettingsRepository { extends _$UnifiedPreferenceSettingsRepository {
Map<String, PreferenceSettingGroup>? _statelessGroups;
Future<void> apply() async { Future<void> apply() async {
final preferenceRepository = ref.read( final preferenceRepository = ref.read(
_preferenceRepositoryProvider(partition).notifier, _preferenceRepositoryProvider(partition).notifier,
); );
_statelessGroups = await ref.read( final groups = await ref.read(
_preferenceSettingGroupsProvider(partition).future, _preferenceSettingGroupsProvider(partition).future,
); );
if (!ref.mounted) {
return;
}
final prefs = { final prefs = {
for (final group in _statelessGroups!.values) for (final group in groups.values)
...Map.fromEntries( ...Map.fromEntries(
group.settings.entries group.settings.entries
.where((e) => !e.value.requireUserOptIn) .where((e) => !e.value.requireUserOptIn)
@@ -288,11 +290,15 @@ class UnifiedPreferenceSettingsRepository
_preferenceRepositoryProvider(partition).notifier, _preferenceRepositoryProvider(partition).notifier,
); );
_statelessGroups = await ref.read( final groups = await ref.read(
_preferenceSettingGroupsProvider(partition).future, _preferenceSettingGroupsProvider(partition).future,
); );
final prefs = _statelessGroups!.values if (!ref.mounted) {
return;
}
final prefs = groups.values
.map((group) => group.settings.keys) .map((group) => group.settings.keys)
.flattened .flattened
.toList(); .toList();
@@ -304,14 +310,13 @@ class UnifiedPreferenceSettingsRepository
Stream<Map<String, PreferenceSettingGroup>> build( Stream<Map<String, PreferenceSettingGroup>> build(
PreferencePartition partition, PreferencePartition partition,
) async* { ) async* {
_statelessGroups = await ref.watch( final prefStream = ref.watch(_preferenceRepositoryProvider(partition));
final groups = await ref.watch(
_preferenceSettingGroupsProvider(partition).future, _preferenceSettingGroupsProvider(partition).future,
); );
final prefStream = ref.watch(_preferenceRepositoryProvider(partition));
yield* prefStream.map( yield* prefStream.map(
(prefs) => _statelessGroups!.map( (prefs) => groups.map(
(groupName, group) => MapEntry( (groupName, group) => MapEntry(
groupName, groupName,
group.copyWith.settings( group.copyWith.settings(
@@ -329,26 +334,28 @@ class UnifiedPreferenceSettingsRepository
@Riverpod() @Riverpod()
class PreferenceSettingsGroupRepository class PreferenceSettingsGroupRepository
extends _$PreferenceSettingsGroupRepository { extends _$PreferenceSettingsGroupRepository {
PreferenceSettingGroup? _statelessSettingGroup;
Future<void> apply({List<String>? filter}) async { Future<void> apply({List<String>? filter}) async {
final preferenceRepository = ref.read( final preferenceRepository = ref.read(
_preferenceRepositoryProvider(partition).notifier, _preferenceRepositoryProvider(partition).notifier,
); );
_statelessSettingGroup ??= await ref.read( final settingGroup = await ref.read(
_preferenceSettingGroupProvider(partition, groupName).future, _preferenceSettingGroupProvider(partition, groupName).future,
); );
if (!ref.mounted) {
return;
}
final prefs = Map.fromEntries( final prefs = Map.fromEntries(
filter?.map( filter?.map(
(e) => MapEntry( (e) => MapEntry(
e, e,
_statelessSettingGroup!.settings[e]?.value ?? settingGroup.settings[e]?.value ??
(throw Exception('Preference not part of group')), (throw Exception('Preference not part of group')),
), ),
) ?? ) ??
_statelessSettingGroup!.settings.entries settingGroup.settings.entries
.where((e) => !e.value.requireUserOptIn) .where((e) => !e.value.requireUserOptIn)
.map((e) => MapEntry(e.key, e.value.value)), .map((e) => MapEntry(e.key, e.value.value)),
); );
@@ -361,19 +368,21 @@ class PreferenceSettingsGroupRepository
_preferenceRepositoryProvider(partition).notifier, _preferenceRepositoryProvider(partition).notifier,
); );
_statelessSettingGroup ??= await ref.read( final settingGroup = await ref.read(
_preferenceSettingGroupProvider(partition, groupName).future, _preferenceSettingGroupProvider(partition, groupName).future,
); );
if (!ref.mounted) {
return;
}
if (filter != null && if (filter != null &&
filter.any( filter.any((value) => !settingGroup.settings.containsKey(value))) {
(value) => !_statelessSettingGroup!.settings.containsKey(value),
)) {
throw Exception('Preference not part of group'); throw Exception('Preference not part of group');
} }
await preferenceRepository.resetPrefs( await preferenceRepository.resetPrefs(
filter ?? _statelessSettingGroup!.settings.keys.toList(), filter ?? settingGroup.settings.keys.toList(),
); );
} }
@@ -382,15 +391,14 @@ class PreferenceSettingsGroupRepository
PreferencePartition partition, PreferencePartition partition,
String groupName, String groupName,
) async* { ) async* {
_statelessSettingGroup = await ref.watch( final prefStream = ref.watch(_preferenceRepositoryProvider(partition));
final settingGroup = await ref.watch(
_preferenceSettingGroupProvider(partition, groupName).future, _preferenceSettingGroupProvider(partition, groupName).future,
); );
final prefStream = ref.watch(_preferenceRepositoryProvider(partition));
yield* prefStream.map( yield* prefStream.map(
(prefs) => _statelessSettingGroup!.copyWith.settings( (prefs) => settingGroup.copyWith.settings(
_statelessSettingGroup!.settings.map( settingGroup.settings.map(
(key, value) => MapEntry(key, value.copyWith.current(prefs[key])), (key, value) => MapEntry(key, value.copyWith.current(prefs[key])),
), ),
), ),
@@ -334,7 +334,7 @@ final class _PreferenceRepositoryProvider
} }
String _$_preferenceRepositoryHash() => String _$_preferenceRepositoryHash() =>
r'c416d7684f1e8f24dd9e5fc98ecb6312c07d938f'; r'ca5ccbe1c3f5f1576e3e7ba359c3cd1324281a97';
final class _PreferenceRepositoryFamily extends $Family final class _PreferenceRepositoryFamily extends $Family
with with
@@ -441,7 +441,7 @@ final class UnifiedPreferenceSettingsRepositoryProvider
} }
String _$unifiedPreferenceSettingsRepositoryHash() => String _$unifiedPreferenceSettingsRepositoryHash() =>
r'03c8c2f247974ff32bba14b4b34857a0f14f6f37'; r'c544b5815ec676ddb6cde1ffeb8c9ef4a7dc4469';
final class UnifiedPreferenceSettingsRepositoryFamily extends $Family final class UnifiedPreferenceSettingsRepositoryFamily extends $Family
with with
@@ -554,7 +554,7 @@ final class PreferenceSettingsGroupRepositoryProvider
} }
String _$preferenceSettingsGroupRepositoryHash() => String _$preferenceSettingsGroupRepositoryHash() =>
r'b5c439643ec78094122c33983e58d0977641832a'; r'c8335b39c4c9fa95c4d415231b103a2feff97d26';
final class PreferenceSettingsGroupRepositoryFamily extends $Family final class PreferenceSettingsGroupRepositoryFamily extends $Family
with with
@@ -44,6 +44,7 @@ Future<void> showPwaInstallDialog(BuildContext context, WidgetRef ref) async {
url: url, url: url,
); );
if (!context.mounted) return;
if (config == null) return; if (config == null) return;
final name = config.name; final name = config.name;
@@ -106,6 +107,7 @@ Future<void> showShortcutInstallDialog(
showAppOption: showAppOption, showAppOption: showAppOption,
); );
if (!context.mounted) return;
if (config == null) return; if (config == null) return;
final name = config.name; final name = config.name;
@@ -34,7 +34,15 @@ class ReaderableScreenController extends _$ReaderableScreenController {
final eventChange = ref.read(eventServiceProvider).readerableEvents.first; final eventChange = ref.read(eventServiceProvider).readerableEvents.first;
final toggle = _service.toggleReaderView(enable); final toggle = _service.toggleReaderView(enable);
state = await AsyncValue.guard(() => Future.wait([toggle, eventChange])); final result = await AsyncValue.guard(
() => Future.wait([toggle, eventChange]),
);
if (!ref.mounted) {
return;
}
state = result;
} }
@override @override
@@ -35,7 +35,7 @@ final class ReaderableScreenControllerProvider
} }
String _$readerableScreenControllerHash() => String _$readerableScreenControllerHash() =>
r'62b2167ff8f9fe21c3cf3339e135a50aa684160c'; r'46206cfb431c5d36dc6351a8fc52c3b98b86dd0f';
abstract class _$ReaderableScreenController extends $AsyncNotifier<void> { abstract class _$ReaderableScreenController extends $AsyncNotifier<void> {
FutureOr<void> build(); FutureOr<void> build();
@@ -38,7 +38,7 @@ class TabSearchRepository extends _$TabSearchRepository {
String ellipsis = '', String ellipsis = '',
}) async { }) async {
if (input.isNotEmpty) { if (input.isNotEmpty) {
state = await AsyncValue.guard(() async { final result = await AsyncValue.guard(() async {
return ( return (
query: input, query: input,
results: await ref results: await ref
@@ -55,6 +55,12 @@ class TabSearchRepository extends _$TabSearchRepository {
.get(), .get(),
); );
}); });
if (!ref.mounted) {
return;
}
state = result;
} else { } else {
state = const AsyncValue.data(null); state = const AsyncValue.data(null);
} }
@@ -55,7 +55,7 @@ final class TabSearchRepositoryProvider
} }
String _$tabSearchRepositoryHash() => String _$tabSearchRepositoryHash() =>
r'473057d6a9f1e76d6b7fec13f710189d758bc924'; r'a05922f7c487105f46abbd272fbc2ed72994c332';
final class TabSearchRepositoryFamily extends $Family final class TabSearchRepositoryFamily extends $Family
with with
@@ -33,11 +33,18 @@ class SaveGeneralSettingsController extends _$SaveGeneralSettingsController {
Future<void> save(UpdateGeneralSettingsFunc updateSettings) async { Future<void> save(UpdateGeneralSettingsFunc updateSettings) async {
state = const AsyncLoading(); state = const AsyncLoading();
state = await AsyncValue.guard(
final result = await AsyncValue.guard(
() => ref () => ref
.read(generalSettingsRepositoryProvider.notifier) .read(generalSettingsRepositoryProvider.notifier)
.updateSettings(updateSettings), .updateSettings(updateSettings),
); );
if (!ref.mounted) {
return;
}
state = result;
} }
} }
@@ -50,11 +57,18 @@ class SaveEngineSettingsController extends _$SaveEngineSettingsController {
Future<void> save(UpdateEngineSettingsFunc updateSettings) async { Future<void> save(UpdateEngineSettingsFunc updateSettings) async {
state = const AsyncLoading(); state = const AsyncLoading();
state = await AsyncValue.guard(
final result = await AsyncValue.guard(
() => ref () => ref
.read(engineSettingsRepositoryProvider.notifier) .read(engineSettingsRepositoryProvider.notifier)
.updateSettings(updateSettings), .updateSettings(updateSettings),
); );
if (!ref.mounted) {
return;
}
state = result;
} }
} }
@@ -67,10 +81,17 @@ class SaveTorSettingsController extends _$SaveTorSettingsController {
Future<void> save(UpdateTorSettingsFunc updateSettings) async { Future<void> save(UpdateTorSettingsFunc updateSettings) async {
state = const AsyncLoading(); state = const AsyncLoading();
state = await AsyncValue.guard(
final result = await AsyncValue.guard(
() => ref () => ref
.read(torSettingsRepositoryProvider.notifier) .read(torSettingsRepositoryProvider.notifier)
.updateSettings(updateSettings), .updateSettings(updateSettings),
); );
if (!ref.mounted) {
return;
}
state = result;
} }
} }
@@ -35,7 +35,7 @@ final class SaveGeneralSettingsControllerProvider
} }
String _$saveGeneralSettingsControllerHash() => String _$saveGeneralSettingsControllerHash() =>
r'b75c9c52452a9cb687f69dad1523f1ed5205847e'; r'9a6d663234c1a1750bf90bf29b4664e4d86861c0';
abstract class _$SaveGeneralSettingsController extends $AsyncNotifier<void> { abstract class _$SaveGeneralSettingsController extends $AsyncNotifier<void> {
FutureOr<void> build(); FutureOr<void> build();
@@ -81,7 +81,7 @@ final class SaveEngineSettingsControllerProvider
} }
String _$saveEngineSettingsControllerHash() => String _$saveEngineSettingsControllerHash() =>
r'd3d74a6aadc89532b1d81c424d92efbfa43002d5'; r'fbe89c8f9772e50fc8ada3d41a233ba5650a7982';
abstract class _$SaveEngineSettingsController extends $AsyncNotifier<void> { abstract class _$SaveEngineSettingsController extends $AsyncNotifier<void> {
FutureOr<void> build(); FutureOr<void> build();
@@ -126,7 +126,7 @@ final class SaveTorSettingsControllerProvider
} }
String _$saveTorSettingsControllerHash() => String _$saveTorSettingsControllerHash() =>
r'd8aa04d7ae60c97bdad6c072f894189509dd35d3'; r'bc8bac2f2d777025fa3e2c85badcbc095a9e3ad1';
abstract class _$SaveTorSettingsController extends $AsyncNotifier<void> { abstract class _$SaveTorSettingsController extends $AsyncNotifier<void> {
FutureOr<void> build(); FutureOr<void> build();
@@ -31,11 +31,9 @@ part 'providers.g.dart';
@Riverpod(keepAlive: true) @Riverpod(keepAlive: true)
Future<KagiSourceService> kagiSourceService(Ref ref) async { Future<KagiSourceService> kagiSourceService(Ref ref) async {
final db = ref.watch(smallWebDatabaseProvider);
final categories = await ref.watch(kagiCategoriesProvider.future); final categories = await ref.watch(kagiCategoriesProvider.future);
return KagiSourceService( return KagiSourceService(db, categories.remap);
ref.watch(smallWebDatabaseProvider),
categories.remap,
);
} }
@Riverpod(keepAlive: true) @Riverpod(keepAlive: true)
@@ -45,13 +43,11 @@ WanderSourceService wanderSourceService(Ref ref) {
@Riverpod(keepAlive: true) @Riverpod(keepAlive: true)
Future<SmallWebDiscoverService> smallWebDiscoverService(Ref ref) async { Future<SmallWebDiscoverService> smallWebDiscoverService(Ref ref) async {
final db = ref.watch(smallWebDatabaseProvider);
final wanderService = ref.watch(wanderSourceServiceProvider);
final kagiService = await ref.watch(kagiSourceServiceProvider.future); final kagiService = await ref.watch(kagiSourceServiceProvider.future);
return SmallWebDiscoverService( return SmallWebDiscoverService(db, kagiService, wanderService);
ref.watch(smallWebDatabaseProvider),
kagiService,
ref.watch(wanderSourceServiceProvider),
);
} }
@Riverpod() @Riverpod()
@@ -48,7 +48,7 @@ final class KagiSourceServiceProvider
} }
} }
String _$kagiSourceServiceHash() => r'7f0cca556d22cc65356660f3e59c976a35d40d37'; String _$kagiSourceServiceHash() => r'5e8aef50353b959d681675feba9eba75bb91bdff';
@ProviderFor(wanderSourceService) @ProviderFor(wanderSourceService)
final wanderSourceServiceProvider = WanderSourceServiceProvider._(); final wanderSourceServiceProvider = WanderSourceServiceProvider._();
@@ -138,7 +138,7 @@ final class SmallWebDiscoverServiceProvider
} }
String _$smallWebDiscoverServiceHash() => String _$smallWebDiscoverServiceHash() =>
r'14fba9eb1c2aa51fa1fac2c2c764ea400603e8ca'; r'eefefdc1b22c6f7e57e61a0c1dafa2b8080ab150';
@ProviderFor(smallWebRecentVisits) @ProviderFor(smallWebRecentVisits)
final smallWebRecentVisitsProvider = SmallWebRecentVisitsFamily._(); final smallWebRecentVisitsProvider = SmallWebRecentVisitsFamily._();
@@ -28,7 +28,8 @@ part 'fetch_articles.g.dart';
class FetchArticlesController extends _$FetchArticlesController { class FetchArticlesController extends _$FetchArticlesController {
Future<void> fetchAllArticles() async { Future<void> fetchAllArticles() async {
state = const AsyncLoading(); state = const AsyncLoading();
state = await AsyncValue.guard(() async {
final result = await AsyncValue.guard(() async {
final feedRepository = ref.read(feedRepositoryProvider.notifier); final feedRepository = ref.read(feedRepositoryProvider.notifier);
final feeds = await feedRepository.getAllFeeds(); final feeds = await feedRepository.getAllFeeds();
@@ -52,11 +53,18 @@ class FetchArticlesController extends _$FetchArticlesController {
}).toList(), }).toList(),
); );
}); });
if (!ref.mounted) {
return;
}
state = result;
} }
Future<void> fetchFeedArticles(Uri uri) async { Future<void> fetchFeedArticles(Uri uri) async {
state = const AsyncLoading(); state = const AsyncLoading();
state = await AsyncValue.guard(() async {
final result = await AsyncValue.guard(() async {
final feedRepository = ref.read(feedRepositoryProvider.notifier); final feedRepository = ref.read(feedRepositoryProvider.notifier);
final result = await ref.read(feedReaderProvider.notifier).parseFeed(uri); final result = await ref.read(feedReaderProvider.notifier).parseFeed(uri);
@@ -64,6 +72,12 @@ class FetchArticlesController extends _$FetchArticlesController {
await feedRepository.upsertArticles(result.articleData); await feedRepository.upsertArticles(result.articleData);
await feedRepository.touchFeedFetched(uri); await feedRepository.touchFeedFetched(uri);
}); });
if (!ref.mounted) {
return;
}
state = result;
} }
@override @override
@@ -42,7 +42,7 @@ final class FetchArticlesControllerProvider
} }
String _$fetchArticlesControllerHash() => String _$fetchArticlesControllerHash() =>
r'5dcb9de003bc911d365c6a8ef107bf1bf446755b'; r'b8a1813f3497af731f20a48693b700eb3d9aeceb';
abstract class _$FetchArticlesController extends $Notifier<AsyncValue<void>> { abstract class _$FetchArticlesController extends $Notifier<AsyncValue<void>> {
AsyncValue<void> build(); AsyncValue<void> build();