switch to equatablevalue

This commit is contained in:
Fabian Freund
2025-02-28 14:43:50 +01:00
parent ef2a3505a1
commit 200c82a442
16 changed files with 82 additions and 130 deletions
+2 -10
View File
@@ -2,17 +2,9 @@ import 'dart:ui';
class EquatableImage {
final Image value;
final int? _imageHash;
final int _imageHash;
EquatableImage(this.value, {required int? hash}) : _imageHash = hash;
//For now we only calculate the has from encoded image on decoding
// static Future<EquatableImage> calculate(Image image) async {
// final imageHash = await image.calculateHash();
// return EquatableImage(image, hash: imageHash);
// }
EquatableImage(this.value, {required int hash}) : _imageHash = hash;
@override
int get hashCode => _imageHash.hashCode;
@@ -1,18 +0,0 @@
import 'package:fast_equatable/fast_equatable.dart';
class EquatableCollection<T> with FastEquatable {
final T collection;
final bool immutable;
EquatableCollection(this.collection, {required this.immutable})
: assert(
collection is Map || collection is Iterable || collection == null,
'Collection type not supported',
);
@override
bool get cacheHash => immutable;
@override
List<Object?> get hashParameters => [collection, immutable];
}
@@ -1,7 +1,7 @@
import 'dart:async';
import 'package:fast_equatable/fast_equatable.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:lensai/domain/entities/equatable_iterable.dart';
import 'package:lensai/features/geckoview/domain/providers.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -10,7 +10,7 @@ part 'tab_list.g.dart';
@Riverpod(keepAlive: true)
class TabList extends _$TabList {
@override
EquatableCollection<List<String>> build() {
EquatableValue<List<String>> build() {
final eventService = ref.watch(eventServiceProvider);
ref.listen(fireImmediately: true, engineReadyStateProvider, (
@@ -23,7 +23,7 @@ class TabList extends _$TabList {
});
final tabListSub = eventService.tabListEvents.listen((tabs) {
final equatableTabs = EquatableCollection(tabs, immutable: true);
final equatableTabs = EquatableValue(tabs);
if (equatableTabs != state) {
state = equatableTabs;
@@ -34,6 +34,6 @@ class TabList extends _$TabList {
unawaited(tabListSub.cancel());
});
return EquatableCollection([], immutable: true);
return EquatableValue([]);
}
}
@@ -6,12 +6,12 @@ part of 'tab_list.dart';
// RiverpodGenerator
// **************************************************************************
String _$tabListHash() => r'cdcbfd9c12e06f350d8284d48c81826b3aa57b6d';
String _$tabListHash() => r'2b98ccdf62c15670e0a12e38064e8186e6555cf8';
/// See also [TabList].
@ProviderFor(TabList)
final tabListProvider =
NotifierProvider<TabList, EquatableCollection<List<String>>>.internal(
NotifierProvider<TabList, EquatableValue<List<String>>>.internal(
TabList.new,
name: r'tabListProvider',
debugGetCreateSourceHash:
@@ -20,6 +20,6 @@ final tabListProvider =
allTransitiveDependencies: null,
);
typedef _$TabList = Notifier<EquatableCollection<List<String>>>;
typedef _$TabList = Notifier<EquatableValue<List<String>>>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
@@ -129,11 +129,10 @@ class TabRepository extends _$TabRepository {
ref.listen(tabListProvider, (previous, next) async {
//Only sync tabs if there has been a previous value or is not empty
final syncTabs =
next.collection.isNotEmpty ||
(previous?.collection.isNotEmpty ?? false);
next.value.isNotEmpty || (previous?.value.isNotEmpty ?? false);
if (syncTabs) {
await db.tabDao.syncTabs(retainTabIds: next.collection);
await db.tabDao.syncTabs(retainTabIds: next.value);
}
});
@@ -6,7 +6,7 @@ part of 'tab.dart';
// RiverpodGenerator
// **************************************************************************
String _$tabRepositoryHash() => r'e6f9d58982f3bbe389e3a3299bb498c6e8e101eb';
String _$tabRepositoryHash() => r'12e4fb7e9bf36a6df9f0a29df4b410f051d5c38e';
/// See also [TabRepository].
@ProviderFor(TabRepository)
@@ -1,6 +1,6 @@
import 'dart:async';
import 'package:lensai/domain/entities/equatable_iterable.dart';
import 'package:fast_equatable/fast_equatable.dart';
import 'package:lensai/features/bangs/data/models/bang_data.dart';
import 'package:lensai/features/bangs/domain/repositories/data.dart';
import 'package:lensai/features/geckoview/domain/entities/states/tab.dart';
@@ -71,7 +71,7 @@ class ShowFindInPage extends _$ShowFindInPage {
}
@Riverpod()
EquatableCollection<List<String>> availableTabIds(
EquatableValue<List<String>> availableTabIds(
Ref ref,
ContainerFilter containerFilter,
) {
@@ -82,31 +82,28 @@ EquatableCollection<List<String>> availableTabIds(
);
final tabList = ref.watch(tabListProvider);
return EquatableCollection(
containerTabs
?.where((tabId) => tabList.collection.contains(tabId))
.toList() ??
return EquatableValue(
containerTabs?.where((tabId) => tabList.value.contains(tabId)).toList() ??
[],
immutable: true,
);
}
@Riverpod()
EquatableCollection<Map<String, TabState>> availableTabStates(
EquatableValue<Map<String, TabState>> availableTabStates(
Ref ref,
ContainerFilter containerFilter,
) {
final availableTabs = ref.watch(availableTabIdsProvider(containerFilter));
final tabStates = ref.watch(tabStatesProvider);
return EquatableCollection({
for (final tabId in availableTabs.collection)
return EquatableValue({
for (final tabId in availableTabs.value)
if (tabStates.containsKey(tabId)) tabId: tabStates[tabId]!,
}, immutable: true);
});
}
@Riverpod()
EquatableCollection<List<String>> seamlessFilteredTabIds(
EquatableValue<List<String>> seamlessFilteredTabIds(
Ref ref,
TabSearchPartition searchPartition,
ContainerFilter containerFilter,
@@ -115,13 +112,12 @@ EquatableCollection<List<String>> seamlessFilteredTabIds(
ref
.watch(
tabSearchRepositoryProvider(searchPartition).select(
(value) => EquatableCollection(
(value) => EquatableValue(
value.valueOrNull?.map((tab) => tab.id).toList(),
immutable: true,
),
),
)
.collection;
.value;
final availableTabs = ref.watch(availableTabIdsProvider(containerFilter));
@@ -129,16 +125,13 @@ EquatableCollection<List<String>> seamlessFilteredTabIds(
return availableTabs;
}
return EquatableCollection(
tabSearchResults
.where((tab) => availableTabs.collection.contains(tab))
.toList(),
immutable: true,
return EquatableValue(
tabSearchResults.where((tab) => availableTabs.value.contains(tab)).toList(),
);
}
@Riverpod()
EquatableCollection<List<TabPreview>> seamlessFilteredTabPreviews(
EquatableValue<List<TabPreview>> seamlessFilteredTabPreviews(
Ref ref,
TabSearchPartition searchPartition,
ContainerFilter containerFilter,
@@ -146,20 +139,19 @@ EquatableCollection<List<TabPreview>> seamlessFilteredTabPreviews(
final tabSearchResults =
ref
.watch(
tabSearchRepositoryProvider(searchPartition).select(
(value) =>
EquatableCollection(value.valueOrNull, immutable: true),
),
tabSearchRepositoryProvider(
searchPartition,
).select((value) => EquatableValue(value.valueOrNull)),
)
.collection;
.value;
final availableTabStates = ref.watch(
availableTabStatesProvider(containerFilter),
);
if (tabSearchResults == null) {
return EquatableCollection(
availableTabStates.collection.values
return EquatableValue(
availableTabStates.value.values
.map(
(state) => TabPreview(
id: state.id,
@@ -171,25 +163,23 @@ EquatableCollection<List<TabPreview>> seamlessFilteredTabPreviews(
),
)
.toList(),
immutable: true,
);
}
return EquatableCollection(
return EquatableValue(
tabSearchResults
.where((tab) => availableTabStates.collection.containsKey(tab.id))
.where((tab) => availableTabStates.value.containsKey(tab.id))
.map((tab) {
return TabPreview(
id: tab.id,
title: tab.title ?? availableTabStates.collection[tab.id]!.title,
title: tab.title ?? availableTabStates.value[tab.id]!.title,
icon: null,
url: tab.cleanUrl ?? availableTabStates.collection[tab.id]!.url,
url: tab.cleanUrl ?? availableTabStates.value[tab.id]!.url,
highlightedUrl: tab.url,
content: tab.extractedContent ?? tab.fullContent,
);
})
.whereType<TabPreview>()
.toList(),
immutable: true,
);
}
@@ -6,7 +6,7 @@ part of 'providers.dart';
// RiverpodGenerator
// **************************************************************************
String _$availableTabIdsHash() => r'e5c5f25dea67f7739dfb1d0d9e4453079817a7bc';
String _$availableTabIdsHash() => r'ec3691063293e1c19c997f7b60178938ae631c7f';
/// Copied from Dart SDK
class _SystemHash {
@@ -34,7 +34,7 @@ class _SystemHash {
const availableTabIdsProvider = AvailableTabIdsFamily();
/// See also [availableTabIds].
class AvailableTabIdsFamily extends Family<EquatableCollection<List<String>>> {
class AvailableTabIdsFamily extends Family<EquatableValue<List<String>>> {
/// See also [availableTabIds].
const AvailableTabIdsFamily();
@@ -67,7 +67,7 @@ class AvailableTabIdsFamily extends Family<EquatableCollection<List<String>>> {
/// See also [availableTabIds].
class AvailableTabIdsProvider
extends AutoDisposeProvider<EquatableCollection<List<String>>> {
extends AutoDisposeProvider<EquatableValue<List<String>>> {
/// See also [availableTabIds].
AvailableTabIdsProvider(ContainerFilter containerFilter)
: this._internal(
@@ -98,8 +98,7 @@ class AvailableTabIdsProvider
@override
Override overrideWith(
EquatableCollection<List<String>> Function(AvailableTabIdsRef provider)
create,
EquatableValue<List<String>> Function(AvailableTabIdsRef provider) create,
) {
return ProviderOverride(
origin: this,
@@ -116,8 +115,7 @@ class AvailableTabIdsProvider
}
@override
AutoDisposeProviderElement<EquatableCollection<List<String>>>
createElement() {
AutoDisposeProviderElement<EquatableValue<List<String>>> createElement() {
return _AvailableTabIdsProviderElement(this);
}
@@ -139,13 +137,13 @@ class AvailableTabIdsProvider
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin AvailableTabIdsRef
on AutoDisposeProviderRef<EquatableCollection<List<String>>> {
on AutoDisposeProviderRef<EquatableValue<List<String>>> {
/// The parameter `containerFilter` of this provider.
ContainerFilter get containerFilter;
}
class _AvailableTabIdsProviderElement
extends AutoDisposeProviderElement<EquatableCollection<List<String>>>
extends AutoDisposeProviderElement<EquatableValue<List<String>>>
with AvailableTabIdsRef {
_AvailableTabIdsProviderElement(super.provider);
@@ -155,7 +153,7 @@ class _AvailableTabIdsProviderElement
}
String _$availableTabStatesHash() =>
r'ca42dc8cb88070fb58ab085ab6c9bcbf31ef4f0b';
r'f506b050e1dc4f50fbd2699d97af5a16a037bc4b';
/// See also [availableTabStates].
@ProviderFor(availableTabStates)
@@ -163,7 +161,7 @@ const availableTabStatesProvider = AvailableTabStatesFamily();
/// See also [availableTabStates].
class AvailableTabStatesFamily
extends Family<EquatableCollection<Map<String, TabState>>> {
extends Family<EquatableValue<Map<String, TabState>>> {
/// See also [availableTabStates].
const AvailableTabStatesFamily();
@@ -196,7 +194,7 @@ class AvailableTabStatesFamily
/// See also [availableTabStates].
class AvailableTabStatesProvider
extends AutoDisposeProvider<EquatableCollection<Map<String, TabState>>> {
extends AutoDisposeProvider<EquatableValue<Map<String, TabState>>> {
/// See also [availableTabStates].
AvailableTabStatesProvider(ContainerFilter containerFilter)
: this._internal(
@@ -228,7 +226,7 @@ class AvailableTabStatesProvider
@override
Override overrideWith(
EquatableCollection<Map<String, TabState>> Function(
EquatableValue<Map<String, TabState>> Function(
AvailableTabStatesRef provider,
)
create,
@@ -248,7 +246,7 @@ class AvailableTabStatesProvider
}
@override
AutoDisposeProviderElement<EquatableCollection<Map<String, TabState>>>
AutoDisposeProviderElement<EquatableValue<Map<String, TabState>>>
createElement() {
return _AvailableTabStatesProviderElement(this);
}
@@ -271,14 +269,13 @@ class AvailableTabStatesProvider
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin AvailableTabStatesRef
on AutoDisposeProviderRef<EquatableCollection<Map<String, TabState>>> {
on AutoDisposeProviderRef<EquatableValue<Map<String, TabState>>> {
/// The parameter `containerFilter` of this provider.
ContainerFilter get containerFilter;
}
class _AvailableTabStatesProviderElement
extends
AutoDisposeProviderElement<EquatableCollection<Map<String, TabState>>>
extends AutoDisposeProviderElement<EquatableValue<Map<String, TabState>>>
with AvailableTabStatesRef {
_AvailableTabStatesProviderElement(super.provider);
@@ -288,7 +285,7 @@ class _AvailableTabStatesProviderElement
}
String _$seamlessFilteredTabIdsHash() =>
r'2e29ca53c35be89972d3f9aea24198fe1feb9e05';
r'171e0b942735066e25894dc2c1d99c367bf0d01a';
/// See also [seamlessFilteredTabIds].
@ProviderFor(seamlessFilteredTabIds)
@@ -296,7 +293,7 @@ const seamlessFilteredTabIdsProvider = SeamlessFilteredTabIdsFamily();
/// See also [seamlessFilteredTabIds].
class SeamlessFilteredTabIdsFamily
extends Family<EquatableCollection<List<String>>> {
extends Family<EquatableValue<List<String>>> {
/// See also [seamlessFilteredTabIds].
const SeamlessFilteredTabIdsFamily();
@@ -332,7 +329,7 @@ class SeamlessFilteredTabIdsFamily
/// See also [seamlessFilteredTabIds].
class SeamlessFilteredTabIdsProvider
extends AutoDisposeProvider<EquatableCollection<List<String>>> {
extends AutoDisposeProvider<EquatableValue<List<String>>> {
/// See also [seamlessFilteredTabIds].
SeamlessFilteredTabIdsProvider(
TabSearchPartition searchPartition,
@@ -372,9 +369,7 @@ class SeamlessFilteredTabIdsProvider
@override
Override overrideWith(
EquatableCollection<List<String>> Function(
SeamlessFilteredTabIdsRef provider,
)
EquatableValue<List<String>> Function(SeamlessFilteredTabIdsRef provider)
create,
) {
return ProviderOverride(
@@ -393,8 +388,7 @@ class SeamlessFilteredTabIdsProvider
}
@override
AutoDisposeProviderElement<EquatableCollection<List<String>>>
createElement() {
AutoDisposeProviderElement<EquatableValue<List<String>>> createElement() {
return _SeamlessFilteredTabIdsProviderElement(this);
}
@@ -418,7 +412,7 @@ class SeamlessFilteredTabIdsProvider
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin SeamlessFilteredTabIdsRef
on AutoDisposeProviderRef<EquatableCollection<List<String>>> {
on AutoDisposeProviderRef<EquatableValue<List<String>>> {
/// The parameter `searchPartition` of this provider.
TabSearchPartition get searchPartition;
@@ -427,7 +421,7 @@ mixin SeamlessFilteredTabIdsRef
}
class _SeamlessFilteredTabIdsProviderElement
extends AutoDisposeProviderElement<EquatableCollection<List<String>>>
extends AutoDisposeProviderElement<EquatableValue<List<String>>>
with SeamlessFilteredTabIdsRef {
_SeamlessFilteredTabIdsProviderElement(super.provider);
@@ -440,7 +434,7 @@ class _SeamlessFilteredTabIdsProviderElement
}
String _$seamlessFilteredTabPreviewsHash() =>
r'6beefa943d298b81482276f12484e27346ff9907';
r'c59c2819eda4f132e1a0f5f7784ddb3b5ceec817';
/// See also [seamlessFilteredTabPreviews].
@ProviderFor(seamlessFilteredTabPreviews)
@@ -448,7 +442,7 @@ const seamlessFilteredTabPreviewsProvider = SeamlessFilteredTabPreviewsFamily();
/// See also [seamlessFilteredTabPreviews].
class SeamlessFilteredTabPreviewsFamily
extends Family<EquatableCollection<List<TabPreview>>> {
extends Family<EquatableValue<List<TabPreview>>> {
/// See also [seamlessFilteredTabPreviews].
const SeamlessFilteredTabPreviewsFamily();
@@ -487,7 +481,7 @@ class SeamlessFilteredTabPreviewsFamily
/// See also [seamlessFilteredTabPreviews].
class SeamlessFilteredTabPreviewsProvider
extends AutoDisposeProvider<EquatableCollection<List<TabPreview>>> {
extends AutoDisposeProvider<EquatableValue<List<TabPreview>>> {
/// See also [seamlessFilteredTabPreviews].
SeamlessFilteredTabPreviewsProvider(
TabSearchPartition searchPartition,
@@ -527,7 +521,7 @@ class SeamlessFilteredTabPreviewsProvider
@override
Override overrideWith(
EquatableCollection<List<TabPreview>> Function(
EquatableValue<List<TabPreview>> Function(
SeamlessFilteredTabPreviewsRef provider,
)
create,
@@ -548,8 +542,7 @@ class SeamlessFilteredTabPreviewsProvider
}
@override
AutoDisposeProviderElement<EquatableCollection<List<TabPreview>>>
createElement() {
AutoDisposeProviderElement<EquatableValue<List<TabPreview>>> createElement() {
return _SeamlessFilteredTabPreviewsProviderElement(this);
}
@@ -573,7 +566,7 @@ class SeamlessFilteredTabPreviewsProvider
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin SeamlessFilteredTabPreviewsRef
on AutoDisposeProviderRef<EquatableCollection<List<TabPreview>>> {
on AutoDisposeProviderRef<EquatableValue<List<TabPreview>>> {
/// The parameter `searchPartition` of this provider.
TabSearchPartition get searchPartition;
@@ -582,7 +575,7 @@ mixin SeamlessFilteredTabPreviewsRef
}
class _SeamlessFilteredTabPreviewsProviderElement
extends AutoDisposeProviderElement<EquatableCollection<List<TabPreview>>>
extends AutoDisposeProviderElement<EquatableValue<List<TabPreview>>>
with SeamlessFilteredTabPreviewsRef {
_SeamlessFilteredTabPreviewsProviderElement(super.provider);
@@ -1,5 +1,5 @@
import 'package:fast_equatable/fast_equatable.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:lensai/domain/entities/equatable_iterable.dart';
import 'package:lensai/features/geckoview/features/tabs/domain/providers.dart';
import 'package:lensai/features/tor/domain/services/tor_proxy.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -23,18 +23,17 @@ class ProxySettingsReplucation extends _$ProxySettingsReplucation {
ref.listen(
containersWithCountProvider.select(
(value) => EquatableCollection(
(value) => EquatableValue(
value.valueOrNull
?.where((container) => container.metadata.useProxy)
.map((container) => container.metadata.contextualIdentity)
.nonNulls
.toList(),
immutable: true,
),
),
(previous, next) async {
if (next.collection != null) {
for (final contextId in next.collection!) {
if (next.value != null) {
for (final contextId in next.value!) {
await _service.addContainerProxy(contextId);
}
}
@@ -7,7 +7,7 @@ part of 'proxy_settings_replucation.dart';
// **************************************************************************
String _$proxySettingsReplucationHash() =>
r'08cb19e895056d4ddbf0399b23a9d159a0c86915';
r'b00fc9a9c8b385a0f8a5830e9ba1c329d340c0ae';
/// See also [ProxySettingsReplucation].
@ProviderFor(ProxySettingsReplucation)
@@ -119,7 +119,7 @@ class BrowserScreen extends HookConsumerWidget {
: null;
final tabCount = ref.read(
tabListProvider.select((tabs) => tabs.collection.length),
tabListProvider.select((tabs) => tabs.value.length),
);
//Don't do anything if a child route is active
@@ -226,7 +226,7 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
);
useEffect(() {
final index = filteredTabIds.collection.indexWhere(
final index = filteredTabIds.value.indexWhere(
(webView) => webView == activeTab,
);
@@ -248,7 +248,7 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
}, [filteredTabIds, activeTab]);
final tabs = useMemoized(() {
return filteredTabIds.collection
return filteredTabIds.value
.mapIndexed(
(index, tabId) => CustomDraggable(
key: Key(tabId),
@@ -320,7 +320,7 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
containerRepositoryProvider.notifier,
);
final tabId = filteredTabIds.collection[oldIndex];
final tabId = filteredTabIds.value[oldIndex];
final containerId = await ref
.read(tabDataRepositoryProvider.notifier)
.containerTabId(tabId);
@@ -331,14 +331,14 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
containerId,
);
} else if (newIndex >=
filteredTabIds.collection.length - 1) {
filteredTabIds.value.length - 1) {
key = await containerRepository.getTrailingOrderKey(
containerId,
);
} else {
final orderAfterIndex = newIndex;
key = await containerRepository.getOrderKeyAfterTab(
filteredTabIds.collection[orderAfterIndex],
filteredTabIds.value[orderAfterIndex],
containerId,
);
}
@@ -19,7 +19,7 @@ class TabsActionButton extends HookConsumerWidget {
final theme = Theme.of(context);
final tabCount = ref.watch(
tabListProvider.select((tabs) => tabs.collection.length),
tabListProvider.select((tabs) => tabs.value.length),
);
return InkWell(
@@ -44,7 +44,7 @@ class TabSearch extends HookConsumerWidget {
: ContainerFilterDisabled(),
),
)
.collection;
.value;
useListenableCallback(searchTextController, () async {
await ref
@@ -1,9 +1,9 @@
import 'package:fast_equatable/fast_equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/core/routing/routes.dart';
import 'package:lensai/domain/entities/equatable_iterable.dart';
import 'package:lensai/extensions/nullable.dart';
import 'package:lensai/features/geckoview/features/preferences/data/repositories/preference_settings.dart';
import 'package:lensai/features/geckoview/features/tabs/utils/setting_groups_serializer.dart';
@@ -25,7 +25,7 @@ class WebEngineHardeningScreen extends HookConsumerWidget {
(element) => element.isActive,
) ??
false,
[EquatableCollection(preferenceGroups.valueOrNull, immutable: true)],
[EquatableValue(preferenceGroups.valueOrNull)],
);
final theme = Theme.of(context);
@@ -1,7 +1,7 @@
import 'package:fast_equatable/fast_equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/domain/entities/equatable_iterable.dart';
import 'package:lensai/extensions/nullable.dart';
import 'package:lensai/features/web_feed/data/models/feed_filter.dart';
import 'package:lensai/features/web_feed/domain/providers.dart';
@@ -60,15 +60,12 @@ class FeedArticleListScreen extends HookConsumerWidget {
builder: (context, ref, child) {
final tags = ref.watch(
articleFilterProvider.select(
(value) => EquatableCollection(
value.tags ?? const {},
immutable: false,
),
(value) => EquatableValue(value.tags ?? const {}),
),
);
return FeedArticleCard(
selectedTags: tags.collection,
selectedTags: tags.value,
onTagSelected: (tagId, value) {
if (value) {
ref