provider lifecycle hardenings
This commit is contained in:
@@ -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();
|
||||
|
||||
+11
-3
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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();
|
||||
|
||||
+7
-2
@@ -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;
|
||||
}
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ final class PreferenceChangeListenerProvider
|
||||
}
|
||||
|
||||
String _$preferenceChangeListenerHash() =>
|
||||
r'de9759b32a6a6b9bab5656137a365da78633f866';
|
||||
r'baf2abe1948998a043a8473547a225582dbc8c7b';
|
||||
|
||||
abstract class _$PreferenceChangeListener extends $StreamNotifier<GeckoPref> {
|
||||
Stream<GeckoPref> build();
|
||||
|
||||
+35
-27
@@ -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])),
|
||||
),
|
||||
),
|
||||
|
||||
+3
-3
@@ -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
|
||||
|
||||
+2
@@ -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;
|
||||
|
||||
+9
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ final class ReaderableScreenControllerProvider
|
||||
}
|
||||
|
||||
String _$readerableScreenControllerHash() =>
|
||||
r'62b2167ff8f9fe21c3cf3339e135a50aa684160c';
|
||||
r'46206cfb431c5d36dc6351a8fc52c3b98b86dd0f';
|
||||
|
||||
abstract class _$ReaderableScreenController extends $AsyncNotifier<void> {
|
||||
FutureOr<void> build();
|
||||
|
||||
+7
-1
@@ -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);
|
||||
}
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ final class TabSearchRepositoryProvider
|
||||
}
|
||||
|
||||
String _$tabSearchRepositoryHash() =>
|
||||
r'473057d6a9f1e76d6b7fec13f710189d758bc924';
|
||||
r'a05922f7c487105f46abbd272fbc2ed72994c332';
|
||||
|
||||
final class TabSearchRepositoryFamily extends $Family
|
||||
with
|
||||
|
||||
Reference in New Issue
Block a user