added quick tab switcher mode setting

This commit is contained in:
Fabian Freund
2026-01-06 11:05:16 +01:00
parent 01b6077d19
commit 7a51e6e9cb
10 changed files with 291 additions and 91 deletions
@@ -33,13 +33,14 @@ import 'package:weblibre/features/geckoview/features/tabs/data/entities/containe
import 'package:weblibre/features/geckoview/features/tabs/data/entities/tab_entity.dart';
import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart';
import 'package:weblibre/features/geckoview/features/tabs/domain/providers.dart';
import 'package:weblibre/features/geckoview/features/tabs/domain/providers/selected_container.dart';
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/gecko_inference.dart';
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/tab_search.dart';
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
part 'providers.g.dart';
typedef FifoTab = (TabState, ContainerData?);
typedef TabStateWirthContainer = (TabState, ContainerData?);
@Riverpod(keepAlive: true)
class SelectedBangTrigger extends _$SelectedBangTrigger {
@@ -85,7 +86,7 @@ class SelectedBangData extends _$SelectedBangData {
}
@Riverpod()
EquatableValue<List<DefaultTabEntity>> availableTabIds(
EquatableValue<List<DefaultTabEntity>> containerTabEntities(
Ref ref,
ContainerFilter containerFilter,
) {
@@ -130,11 +131,13 @@ EquatableValue<List<DefaultTabEntity>> availableTabIds(
}
@Riverpod()
EquatableValue<Map<String, TabState>> availableTabStates(
EquatableValue<Map<String, TabState>> containerTabStates(
Ref ref,
ContainerFilter containerFilter,
) {
final availableTabs = ref.watch(availableTabIdsProvider(containerFilter));
final availableTabs = ref.watch(
containerTabEntitiesProvider(containerFilter),
);
final tabStates = ref.watch(tabStatesProvider);
return EquatableValue({
@@ -145,7 +148,7 @@ EquatableValue<Map<String, TabState>> availableTabStates(
}
@Riverpod(keepAlive: true)
EquatableValue<List<FifoTab>> fifoTabStates(Ref ref) {
EquatableValue<List<TabStateWirthContainer>> fifoTabStates(Ref ref) {
final containerData = ref
.watch(watchContainersWithCountProvider.select((value) => value.value))
.mapNotNull(
@@ -153,7 +156,7 @@ EquatableValue<List<FifoTab>> fifoTabStates(Ref ref) {
);
final sortedTabs = ref.watch(
watchgetTabsFifoProvider.select((value) => value.value),
watchTabsFifoProvider.select((value) => value.value),
);
final tabStates = ref.watch(tabStatesProvider);
@@ -171,6 +174,39 @@ EquatableValue<List<FifoTab>> fifoTabStates(Ref ref) {
]);
}
@Riverpod(keepAlive: true)
EquatableValue<List<TabStateWirthContainer>>
selectedContainerTabStatesWithContainer(Ref ref) {
final filter = ref.watch(
selectedContainerProvider.select(
(value) => ContainerFilterById(containerId: value),
),
);
final containerData = ref
.watch(watchContainersWithCountProvider.select((value) => value.value))
.mapNotNull(
(value) => Map.fromEntries(value.map((c) => MapEntry(c.id, c))),
);
final sortedTabs = ref.watch(
containerTabEntitiesProvider(filter).select((value) => value.value),
);
final tabStates = ref.watch(tabStatesProvider);
return EquatableValue([
for (final tabEntity in sortedTabs)
if (tabStates.containsKey(tabEntity.tabId))
(
tabStates[tabEntity.tabId]!,
tabEntity.containerId.mapNotNull(
(containerId) => containerData?[containerId],
),
),
]);
}
@Riverpod()
EquatableValue<List<TabEntity>> suggestedTabEntities(
Ref ref,
@@ -244,31 +280,29 @@ EquatableValue<List<TabEntity>> seamlessFilteredTabEntities(
)
.value;
final availableTabs = ref.watch(availableTabIdsProvider(containerFilter));
final availableTabs = ref.watch(
containerTabEntitiesProvider(containerFilter),
);
if (tabSearchResults == null) {
if (groupTrees) {
final trees = ref.watch(
watchTabTreesProvider.select(
(value) => EquatableValue(
value.value
?.map(
(tree) {
// Find the container ID for the latest tab
final containerForTab = availableTabs.value
.where((t) => t.tabId == tree.latestTabId)
.firstOrNull
?.containerId;
value.value?.map((tree) {
// Find the container ID for the latest tab
final containerForTab = availableTabs.value
.where((t) => t.tabId == tree.latestTabId)
.firstOrNull
?.containerId;
return TabTreeEntity(
tabId: tree.latestTabId,
containerId: containerForTab,
rootId: tree.rootTabId,
totalTabs: tree.totalTabs,
);
},
)
.toList() ??
return TabTreeEntity(
tabId: tree.latestTabId,
containerId: containerForTab,
rootId: tree.rootTabId,
totalTabs: tree.totalTabs,
);
}).toList() ??
[],
),
),
@@ -314,7 +348,7 @@ EquatableValue<List<TabPreview>> filteredTabPreviews(
.value;
final availableTabStates = ref.watch(
availableTabStatesProvider(containerFilter),
containerTabStatesProvider(containerFilter),
);
if (tabSearchResults == null) {
@@ -204,10 +204,10 @@ abstract class _$SelectedBangData extends $Notifier<BangData?> {
}
}
@ProviderFor(availableTabIds)
final availableTabIdsProvider = AvailableTabIdsFamily._();
@ProviderFor(containerTabEntities)
final containerTabEntitiesProvider = ContainerTabEntitiesFamily._();
final class AvailableTabIdsProvider
final class ContainerTabEntitiesProvider
extends
$FunctionalProvider<
EquatableValue<List<DefaultTabEntity>>,
@@ -215,23 +215,23 @@ final class AvailableTabIdsProvider
EquatableValue<List<DefaultTabEntity>>
>
with $Provider<EquatableValue<List<DefaultTabEntity>>> {
AvailableTabIdsProvider._({
required AvailableTabIdsFamily super.from,
ContainerTabEntitiesProvider._({
required ContainerTabEntitiesFamily super.from,
required ContainerFilter super.argument,
}) : super(
retry: null,
name: r'availableTabIdsProvider',
name: r'containerTabEntitiesProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$availableTabIdsHash();
String debugGetCreateSourceHash() => _$containerTabEntitiesHash();
@override
String toString() {
return r'availableTabIdsProvider'
return r'containerTabEntitiesProvider'
''
'($argument)';
}
@@ -245,7 +245,7 @@ final class AvailableTabIdsProvider
@override
EquatableValue<List<DefaultTabEntity>> create(Ref ref) {
final argument = this.argument as ContainerFilter;
return availableTabIds(ref, argument);
return containerTabEntities(ref, argument);
}
/// {@macro riverpod.override_with_value}
@@ -259,7 +259,7 @@ final class AvailableTabIdsProvider
@override
bool operator ==(Object other) {
return other is AvailableTabIdsProvider && other.argument == argument;
return other is ContainerTabEntitiesProvider && other.argument == argument;
}
@override
@@ -268,34 +268,35 @@ final class AvailableTabIdsProvider
}
}
String _$availableTabIdsHash() => r'fa7280a7a5f9c277faa71a30f5dd50afa30a20b1';
String _$containerTabEntitiesHash() =>
r'350b3e2a2672ab0a303344c7d8301af24c991ee5';
final class AvailableTabIdsFamily extends $Family
final class ContainerTabEntitiesFamily extends $Family
with
$FunctionalFamilyOverride<
EquatableValue<List<DefaultTabEntity>>,
ContainerFilter
> {
AvailableTabIdsFamily._()
ContainerTabEntitiesFamily._()
: super(
retry: null,
name: r'availableTabIdsProvider',
name: r'containerTabEntitiesProvider',
dependencies: null,
$allTransitiveDependencies: null,
isAutoDispose: true,
);
AvailableTabIdsProvider call(ContainerFilter containerFilter) =>
AvailableTabIdsProvider._(argument: containerFilter, from: this);
ContainerTabEntitiesProvider call(ContainerFilter containerFilter) =>
ContainerTabEntitiesProvider._(argument: containerFilter, from: this);
@override
String toString() => r'availableTabIdsProvider';
String toString() => r'containerTabEntitiesProvider';
}
@ProviderFor(availableTabStates)
final availableTabStatesProvider = AvailableTabStatesFamily._();
@ProviderFor(containerTabStates)
final containerTabStatesProvider = ContainerTabStatesFamily._();
final class AvailableTabStatesProvider
final class ContainerTabStatesProvider
extends
$FunctionalProvider<
EquatableValue<Map<String, TabState>>,
@@ -303,23 +304,23 @@ final class AvailableTabStatesProvider
EquatableValue<Map<String, TabState>>
>
with $Provider<EquatableValue<Map<String, TabState>>> {
AvailableTabStatesProvider._({
required AvailableTabStatesFamily super.from,
ContainerTabStatesProvider._({
required ContainerTabStatesFamily super.from,
required ContainerFilter super.argument,
}) : super(
retry: null,
name: r'availableTabStatesProvider',
name: r'containerTabStatesProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$availableTabStatesHash();
String debugGetCreateSourceHash() => _$containerTabStatesHash();
@override
String toString() {
return r'availableTabStatesProvider'
return r'containerTabStatesProvider'
''
'($argument)';
}
@@ -333,7 +334,7 @@ final class AvailableTabStatesProvider
@override
EquatableValue<Map<String, TabState>> create(Ref ref) {
final argument = this.argument as ContainerFilter;
return availableTabStates(ref, argument);
return containerTabStates(ref, argument);
}
/// {@macro riverpod.override_with_value}
@@ -347,7 +348,7 @@ final class AvailableTabStatesProvider
@override
bool operator ==(Object other) {
return other is AvailableTabStatesProvider && other.argument == argument;
return other is ContainerTabStatesProvider && other.argument == argument;
}
@override
@@ -356,29 +357,29 @@ final class AvailableTabStatesProvider
}
}
String _$availableTabStatesHash() =>
r'32f15b0f739495e11025bdc10d324764465b542d';
String _$containerTabStatesHash() =>
r'320f35a2605b53ab57b299caafd9b71804b67c6c';
final class AvailableTabStatesFamily extends $Family
final class ContainerTabStatesFamily extends $Family
with
$FunctionalFamilyOverride<
EquatableValue<Map<String, TabState>>,
ContainerFilter
> {
AvailableTabStatesFamily._()
ContainerTabStatesFamily._()
: super(
retry: null,
name: r'availableTabStatesProvider',
name: r'containerTabStatesProvider',
dependencies: null,
$allTransitiveDependencies: null,
isAutoDispose: true,
);
AvailableTabStatesProvider call(ContainerFilter containerFilter) =>
AvailableTabStatesProvider._(argument: containerFilter, from: this);
ContainerTabStatesProvider call(ContainerFilter containerFilter) =>
ContainerTabStatesProvider._(argument: containerFilter, from: this);
@override
String toString() => r'availableTabStatesProvider';
String toString() => r'containerTabStatesProvider';
}
@ProviderFor(fifoTabStates)
@@ -387,11 +388,11 @@ final fifoTabStatesProvider = FifoTabStatesProvider._();
final class FifoTabStatesProvider
extends
$FunctionalProvider<
EquatableValue<List<FifoTab>>,
EquatableValue<List<FifoTab>>,
EquatableValue<List<FifoTab>>
EquatableValue<List<TabStateWirthContainer>>,
EquatableValue<List<TabStateWirthContainer>>,
EquatableValue<List<TabStateWirthContainer>>
>
with $Provider<EquatableValue<List<FifoTab>>> {
with $Provider<EquatableValue<List<TabStateWirthContainer>>> {
FifoTabStatesProvider._()
: super(
from: null,
@@ -408,27 +409,85 @@ final class FifoTabStatesProvider
@$internal
@override
$ProviderElement<EquatableValue<List<FifoTab>>> $createElement(
$ProviderElement<EquatableValue<List<TabStateWirthContainer>>> $createElement(
$ProviderPointer pointer,
) => $ProviderElement(pointer);
@override
EquatableValue<List<FifoTab>> create(Ref ref) {
EquatableValue<List<TabStateWirthContainer>> create(Ref ref) {
return fifoTabStates(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(EquatableValue<List<FifoTab>> value) {
Override overrideWithValue(
EquatableValue<List<TabStateWirthContainer>> value,
) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<EquatableValue<List<FifoTab>>>(
value,
),
providerOverride:
$SyncValueProvider<EquatableValue<List<TabStateWirthContainer>>>(
value,
),
);
}
}
String _$fifoTabStatesHash() => r'0320c28b328d814ed358ec6a10ec770a1ed634ad';
String _$fifoTabStatesHash() => r'03298f30559d55e986d97a17d517b9a9abb89be4';
@ProviderFor(selectedContainerTabStatesWithContainer)
final selectedContainerTabStatesWithContainerProvider =
SelectedContainerTabStatesWithContainerProvider._();
final class SelectedContainerTabStatesWithContainerProvider
extends
$FunctionalProvider<
EquatableValue<List<TabStateWirthContainer>>,
EquatableValue<List<TabStateWirthContainer>>,
EquatableValue<List<TabStateWirthContainer>>
>
with $Provider<EquatableValue<List<TabStateWirthContainer>>> {
SelectedContainerTabStatesWithContainerProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'selectedContainerTabStatesWithContainerProvider',
isAutoDispose: false,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() =>
_$selectedContainerTabStatesWithContainerHash();
@$internal
@override
$ProviderElement<EquatableValue<List<TabStateWirthContainer>>> $createElement(
$ProviderPointer pointer,
) => $ProviderElement(pointer);
@override
EquatableValue<List<TabStateWirthContainer>> create(Ref ref) {
return selectedContainerTabStatesWithContainer(ref);
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(
EquatableValue<List<TabStateWirthContainer>> value,
) {
return $ProviderOverride(
origin: this,
providerOverride:
$SyncValueProvider<EquatableValue<List<TabStateWirthContainer>>>(
value,
),
);
}
}
String _$selectedContainerTabStatesWithContainerHash() =>
r'd22d16eba89040fbcf7932e5334155f00312957f';
@ProviderFor(suggestedTabEntities)
final suggestedTabEntitiesProvider = SuggestedTabEntitiesFamily._();
@@ -600,7 +659,7 @@ final class SeamlessFilteredTabEntitiesProvider
}
String _$seamlessFilteredTabEntitiesHash() =>
r'590288855aa93162a8f70d37f2b3cd94eabc85e5';
r'b0477aa6297183575264d54a82575dfb01fa4c32';
final class SeamlessFilteredTabEntitiesFamily extends $Family
with
@@ -704,7 +763,7 @@ final class FilteredTabPreviewsProvider
}
String _$filteredTabPreviewsHash() =>
r'9fbf6774433e62ce9b63ef66f6db3446e6182109';
r'225d67663d70aa0ad2a5544b75bfe7d6125edb58';
final class FilteredTabPreviewsFamily extends $Family
with
@@ -195,6 +195,12 @@ class BrowserTabBar extends HookConsumerWidget {
),
);
final quickTabSwitcherMode = ref.watch(
generalSettingsWithDefaultsProvider.select(
(value) => value.quickTabSwitcherMode,
),
);
final dragStartPosition = useRef(Offset.zero);
final toolbarHeight = useMemoized(() => getToolbarHeight());
@@ -256,7 +262,9 @@ class BrowserTabBar extends HookConsumerWidget {
Visibility(
visible: displayQuickTabSwitcher,
maintainState: true,
child: const QuickTabSwitcher(),
child: QuickTabSwitcher(
quickTabSwitcherMode: quickTabSwitcherMode,
),
),
if (showMainToolbar)
Visibility(
@@ -442,20 +450,30 @@ class ContextualToolbar extends HookConsumerWidget {
}
class QuickTabSwitcher extends HookConsumerWidget {
const QuickTabSwitcher();
final QuickTabSwitcherMode quickTabSwitcherMode;
const QuickTabSwitcher({required this.quickTabSwitcherMode});
@override
Widget build(BuildContext context, WidgetRef ref) {
final tabStates = ref.watch(fifoTabStatesProvider);
final selectedTabId = ref.watch(selectedTabProvider);
final tabStates = (switch (quickTabSwitcherMode) {
QuickTabSwitcherMode.lastUsedTabs => ref.watch(fifoTabStatesProvider),
QuickTabSwitcherMode.containerTabs => ref.watch(
selectedContainerTabStatesWithContainerProvider,
),
}).value.where((state) => state.$1.id != selectedTabId).toList();
final historyAsync = useCachedFuture(() {
if (tabStates.value.length <= 1) {
if (tabStates.isEmpty) {
return ref
.read(historyRepositoryProvider.notifier)
.getVisitsPaginated(count: 25);
}
return Future.value(<VisitInfo>[]);
}, [tabStates.value.length <= 1]);
}, [tabStates.isEmpty]);
final chipScrollController = useScrollController();
@@ -536,7 +554,7 @@ class QuickTabSwitcher extends HookConsumerWidget {
},
);
},
availableItems: tabStates.value
availableItems: tabStates
.map(
(state) => (
id: state.$1.id,
@@ -547,7 +565,6 @@ class QuickTabSwitcher extends HookConsumerWidget {
color: state.$2?.color,
),
)
.skip(1)
.followedBy(
(historyAsync.data ?? []).map((state) {
final url = Uri.parse(state.url);