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