respect pinned tabs and direction when sorting
This commit is contained in:
@@ -401,7 +401,7 @@ class TabRepository extends _$TabRepository {
|
|||||||
// anchored to the bar's mental model.
|
// anchored to the bar's mental model.
|
||||||
final newestFirst =
|
final newestFirst =
|
||||||
ref.read(generalSettingsWithDefaultsProvider).tabBarDirection ==
|
ref.read(generalSettingsWithDefaultsProvider).tabBarDirection ==
|
||||||
TabBarDirection.newestFirst;
|
TabDirection.newestFirst;
|
||||||
final TabDatabase tabDatabase = ref.read(tabDatabaseProvider);
|
final TabDatabase tabDatabase = ref.read(tabDatabaseProvider);
|
||||||
final definitions = tabDatabase.definitionsDrift;
|
final definitions = tabDatabase.definitionsDrift;
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ final class TabRepositoryProvider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$tabRepositoryHash() => r'84ecfed72f17c367fc7bf0100f6367d4e7609596';
|
String _$tabRepositoryHash() => r'a68941d373f348201e3d8b1cab26343acca3852f';
|
||||||
|
|
||||||
abstract class _$TabRepository extends $Notifier<void> {
|
abstract class _$TabRepository extends $Notifier<void> {
|
||||||
void build();
|
void build();
|
||||||
|
|||||||
@@ -227,11 +227,74 @@ selectedContainerTabStatesWithContainer(Ref ref) {
|
|||||||
.watch(groupedTabListItemsProvider(containerId: filter.containerId))
|
.watch(groupedTabListItemsProvider(containerId: filter.containerId))
|
||||||
.value;
|
.value;
|
||||||
|
|
||||||
|
final tabListDirection = ref.watch(
|
||||||
|
generalSettingsWithDefaultsProvider.select((s) => s.tabListDirection),
|
||||||
|
);
|
||||||
|
final tabBarDirection = ref.watch(
|
||||||
|
generalSettingsWithDefaultsProvider.select((s) => s.tabBarDirection),
|
||||||
|
);
|
||||||
|
final pinnedTabIds = ref.watch(
|
||||||
|
watchPinnedTabIdsProvider.select(
|
||||||
|
(value) => value.value ?? const <String>{},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final sortPinnedFirst = ref.watch(
|
||||||
|
tabViewFilterControllerProvider.select((v) => v.sortPinnedFirst),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Build an order map from groupedItems. When the tab bar direction differs
|
||||||
|
// from the tab list direction, reverse root group order within each
|
||||||
|
// pinned/unpinned partition so the bar respects the user's direction
|
||||||
|
// preference while keeping parent–child groups intact.
|
||||||
|
List<TabListItemEntity> orderedItems = groupedItems;
|
||||||
|
if (tabBarDirection != tabListDirection) {
|
||||||
|
// Split groupedItems into root groups (each: root + its children).
|
||||||
|
final rootGroups = <(bool isPinned, List<TabListItemEntity>)>[];
|
||||||
|
var currentGroup = <TabListItemEntity>[];
|
||||||
|
for (final item in groupedItems) {
|
||||||
|
final isRoot =
|
||||||
|
item is TabListStandaloneItem || item is TabListParentGroup;
|
||||||
|
if (isRoot && currentGroup.isNotEmpty) {
|
||||||
|
final isPinned =
|
||||||
|
sortPinnedFirst && pinnedTabIds.contains(currentGroup.first.tabId);
|
||||||
|
rootGroups.add((isPinned, currentGroup));
|
||||||
|
currentGroup = [];
|
||||||
|
}
|
||||||
|
currentGroup.add(item);
|
||||||
|
}
|
||||||
|
if (currentGroup.isNotEmpty) {
|
||||||
|
final isPinned =
|
||||||
|
sortPinnedFirst && pinnedTabIds.contains(currentGroup.first.tabId);
|
||||||
|
rootGroups.add((isPinned, currentGroup));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse root groups within each partition (pinned / unpinned).
|
||||||
|
final result = <TabListItemEntity>[];
|
||||||
|
if (sortPinnedFirst) {
|
||||||
|
final pinned = rootGroups
|
||||||
|
.where((g) => g.$1)
|
||||||
|
.toList()
|
||||||
|
.reversed
|
||||||
|
.expand((g) => g.$2);
|
||||||
|
final unpinned = rootGroups
|
||||||
|
.where((g) => !g.$1)
|
||||||
|
.toList()
|
||||||
|
.reversed
|
||||||
|
.expand((g) => g.$2);
|
||||||
|
result
|
||||||
|
..addAll(pinned)
|
||||||
|
..addAll(unpinned);
|
||||||
|
} else {
|
||||||
|
result.addAll(rootGroups.reversed.expand((g) => g.$2));
|
||||||
|
}
|
||||||
|
orderedItems = result;
|
||||||
|
}
|
||||||
|
|
||||||
final groupedOrder = {
|
final groupedOrder = {
|
||||||
for (var i = 0; i < groupedItems.length; i++) groupedItems[i].tabId: i,
|
for (var i = 0; i < orderedItems.length; i++) orderedItems[i].tabId: i,
|
||||||
};
|
};
|
||||||
|
|
||||||
final items = [
|
var items = [
|
||||||
for (final tabEntity in sortedTabs)
|
for (final tabEntity in sortedTabs)
|
||||||
if (tabStates.containsKey(tabEntity.tabId))
|
if (tabStates.containsKey(tabEntity.tabId))
|
||||||
(
|
(
|
||||||
@@ -243,11 +306,21 @@ selectedContainerTabStatesWithContainer(Ref ref) {
|
|||||||
];
|
];
|
||||||
|
|
||||||
items.sort((a, b) {
|
items.sort((a, b) {
|
||||||
final aIndex = groupedOrder[a.$1.id] ?? groupedItems.length;
|
final aIndex = groupedOrder[a.$1.id] ?? orderedItems.length;
|
||||||
final bIndex = groupedOrder[b.$1.id] ?? groupedItems.length;
|
final bIndex = groupedOrder[b.$1.id] ?? orderedItems.length;
|
||||||
return aIndex.compareTo(bIndex);
|
return aIndex.compareTo(bIndex);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Flat pinned-first: move all pinned tabs before unpinned regardless of
|
||||||
|
// hierarchy, preserving relative order within each partition.
|
||||||
|
if (sortPinnedFirst && pinnedTabIds.isNotEmpty) {
|
||||||
|
final pinned = items.where((i) => pinnedTabIds.contains(i.$1.id)).toList();
|
||||||
|
final unpinned = items
|
||||||
|
.where((i) => !pinnedTabIds.contains(i.$1.id))
|
||||||
|
.toList();
|
||||||
|
items = [...pinned, ...unpinned];
|
||||||
|
}
|
||||||
|
|
||||||
return EquatableValue(items);
|
return EquatableValue(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,12 +350,35 @@ EquatableValue<List<TabStateWithContainer>> quickTabSwitcherTabStates(
|
|||||||
generalSettingsWithDefaultsProvider.select((s) => s.tabBarDirection),
|
generalSettingsWithDefaultsProvider.select((s) => s.tabBarDirection),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final pinnedTabIds = ref.watch(
|
||||||
|
watchPinnedTabIdsProvider.select(
|
||||||
|
(value) => value.value ?? const <String>{},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final sortPinnedFirst = ref.watch(
|
||||||
|
tabViewFilterControllerProvider.select((v) => v.sortPinnedFirst),
|
||||||
|
);
|
||||||
|
|
||||||
return EquatableValue(switch (effectiveMode) {
|
return EquatableValue(switch (effectiveMode) {
|
||||||
QuickTabSwitcherMode.lastUsedTabs => () {
|
QuickTabSwitcherMode.lastUsedTabs => () {
|
||||||
final filtered = tabStates
|
final filtered = tabStates
|
||||||
.where((state) => state.$1.id != selectedTabId)
|
.where((state) => state.$1.id != selectedTabId)
|
||||||
.toList();
|
.toList();
|
||||||
return tabBarDirection == TabBarDirection.oldestFirst
|
if (sortPinnedFirst && pinnedTabIds.isNotEmpty) {
|
||||||
|
final pinned = filtered
|
||||||
|
.where((s) => pinnedTabIds.contains(s.$1.id))
|
||||||
|
.toList();
|
||||||
|
final unpinned = filtered
|
||||||
|
.where((s) => !pinnedTabIds.contains(s.$1.id))
|
||||||
|
.toList();
|
||||||
|
// Each partition is MRU-first from fifoTabStates. For oldestFirst,
|
||||||
|
// reverse each partition independently so pinned stays on top.
|
||||||
|
if (tabBarDirection == TabDirection.oldestFirst) {
|
||||||
|
return [...pinned.reversed, ...unpinned.reversed];
|
||||||
|
}
|
||||||
|
return [...pinned, ...unpinned];
|
||||||
|
}
|
||||||
|
return tabBarDirection == TabDirection.oldestFirst
|
||||||
? filtered.reversed.toList()
|
? filtered.reversed.toList()
|
||||||
: filtered;
|
: filtered;
|
||||||
}(),
|
}(),
|
||||||
@@ -605,7 +701,7 @@ EquatableValue<List<TabEntity>> seamlessFilteredTabEntities(
|
|||||||
// database order (ascending order_key) is oldest-first. Flip to
|
// database order (ascending order_key) is oldest-first. Flip to
|
||||||
// newest-first when the user selects that direction.
|
// newest-first when the user selects that direction.
|
||||||
List<TabEntity> applyDirection(List<TabEntity> entities) {
|
List<TabEntity> applyDirection(List<TabEntity> entities) {
|
||||||
return tabListDirection == TabListDirection.newestFirst
|
return tabListDirection == TabDirection.newestFirst
|
||||||
? entities.reversed.toList()
|
? entities.reversed.toList()
|
||||||
: entities;
|
: entities;
|
||||||
}
|
}
|
||||||
@@ -697,7 +793,7 @@ EquatableValue<List<TabPreview>> filteredTabPreviews(
|
|||||||
|
|
||||||
/// Grouped flat-list rendering for the list and grid views.
|
/// Grouped flat-list rendering for the list and grid views.
|
||||||
///
|
///
|
||||||
/// Parent rows always render before their descendants. [TabListDirection]
|
/// Parent rows always render before their descendants. [TabDirection]
|
||||||
/// applies both to root group ordering and to sibling ordering below each
|
/// applies both to root group ordering and to sibling ordering below each
|
||||||
/// parent, so parent-child pairs stay together while child order still follows
|
/// parent, so parent-child pairs stay together while child order still follows
|
||||||
/// the configured direction.
|
/// the configured direction.
|
||||||
@@ -867,7 +963,7 @@ EquatableValue<List<TabListItemEntity>> groupedTabListItems(
|
|||||||
// order_key ASC; newest-first reverses the group list. Pinned and
|
// order_key ASC; newest-first reverses the group list. Pinned and
|
||||||
// unpinned partitions are reversed independently so pinned-first stays
|
// unpinned partitions are reversed independently so pinned-first stays
|
||||||
// intact while still flipping the relative order within each partition.
|
// intact while still flipping the relative order within each partition.
|
||||||
if (sortField == null && tabListDirection == TabListDirection.newestFirst) {
|
if (sortField == null && tabListDirection == TabDirection.newestFirst) {
|
||||||
if (filterOptions.sortPinnedFirst) {
|
if (filterOptions.sortPinnedFirst) {
|
||||||
final pinned = groupRecords.where((g) => g.isPinned).toList()
|
final pinned = groupRecords.where((g) => g.isPinned).toList()
|
||||||
..sort((a, b) => b.rootOrderKey.compareTo(a.rootOrderKey));
|
..sort((a, b) => b.rootOrderKey.compareTo(a.rootOrderKey));
|
||||||
@@ -931,18 +1027,37 @@ EquatableValue<List<TabListItemEntity>> groupedTabListItems(
|
|||||||
.add(member);
|
.add(member);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Children are always sorted by storage `order_key` (optionally reversed
|
// Children are sorted by storage `order_key` (optionally reversed for
|
||||||
// for newest-first) — even when an explicit `sortField` (title/url/date)
|
// newest-first) — even when an explicit `sortField` is active. The
|
||||||
// is active. The explicit sort applies only to root groups; descendants
|
// explicit sort applies only to root groups; descendants remain in
|
||||||
// remain in insertion order so a parent's children stay grouped in the
|
// insertion order. When `sortPinnedFirst` is true, pinned children are
|
||||||
// order they were opened rather than alphabetically interleaving across
|
// sorted before unpinned siblings within each parent group.
|
||||||
// siblings.
|
|
||||||
for (final children in childrenByVisibleParent.values) {
|
for (final children in childrenByVisibleParent.values) {
|
||||||
|
if (filterOptions.sortPinnedFirst) {
|
||||||
|
final pinned = children
|
||||||
|
.where((c) => pinnedTabIds.contains(c.row.id))
|
||||||
|
.toList();
|
||||||
|
final unpinned = children
|
||||||
|
.where((c) => !pinnedTabIds.contains(c.row.id))
|
||||||
|
.toList();
|
||||||
|
final cmp = (_GroupedRow a, _GroupedRow b) =>
|
||||||
|
a.row.orderKey.compareTo(b.row.orderKey);
|
||||||
|
final directionCmp = tabListDirection == TabDirection.newestFirst
|
||||||
|
? (_GroupedRow a, _GroupedRow b) => -cmp(a, b)
|
||||||
|
: cmp;
|
||||||
|
pinned.sort(directionCmp);
|
||||||
|
unpinned.sort(directionCmp);
|
||||||
|
children
|
||||||
|
..clear()
|
||||||
|
..addAll(pinned)
|
||||||
|
..addAll(unpinned);
|
||||||
|
} else {
|
||||||
children.sort((a, b) {
|
children.sort((a, b) {
|
||||||
final cmp = a.row.orderKey.compareTo(b.row.orderKey);
|
final cmp = a.row.orderKey.compareTo(b.row.orderKey);
|
||||||
return tabListDirection == TabListDirection.newestFirst ? -cmp : cmp;
|
return tabListDirection == TabDirection.newestFirst ? -cmp : cmp;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void addChildren(String parentId) {
|
void addChildren(String parentId) {
|
||||||
for (final member
|
for (final member
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ final class CanManualTabReorderProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$canManualTabReorderHash() =>
|
String _$canManualTabReorderHash() =>
|
||||||
r'598bc67a750f45893ba555b9b7866499b1808896';
|
r'ba5d961933464b6e005d7945802908a9a4ae034b';
|
||||||
|
|
||||||
@ProviderFor(SelectedBangTrigger)
|
@ProviderFor(SelectedBangTrigger)
|
||||||
final selectedBangTriggerProvider = SelectedBangTriggerFamily._();
|
final selectedBangTriggerProvider = SelectedBangTriggerFamily._();
|
||||||
@@ -529,7 +529,7 @@ final class SelectedContainerTabStatesWithContainerProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$selectedContainerTabStatesWithContainerHash() =>
|
String _$selectedContainerTabStatesWithContainerHash() =>
|
||||||
r'6b630cdfc589af6961d4c96c90d09bd107560f15';
|
r'e6fd0e1e0ca4cfb0a0a46e8ff4e63d41032bfad9';
|
||||||
|
|
||||||
@ProviderFor(quickTabSwitcherTabStates)
|
@ProviderFor(quickTabSwitcherTabStates)
|
||||||
final quickTabSwitcherTabStatesProvider = QuickTabSwitcherTabStatesFamily._();
|
final quickTabSwitcherTabStatesProvider = QuickTabSwitcherTabStatesFamily._();
|
||||||
@@ -601,7 +601,7 @@ final class QuickTabSwitcherTabStatesProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$quickTabSwitcherTabStatesHash() =>
|
String _$quickTabSwitcherTabStatesHash() =>
|
||||||
r'32e928b4596ef2388f8c0e4cfdcf8f3563a1e24a';
|
r'3a1a195f6b2fc4cc54e689fab7bacaf4e6422196';
|
||||||
|
|
||||||
final class QuickTabSwitcherTabStatesFamily extends $Family
|
final class QuickTabSwitcherTabStatesFamily extends $Family
|
||||||
with
|
with
|
||||||
@@ -962,7 +962,7 @@ final class SeamlessFilteredTabEntitiesProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$seamlessFilteredTabEntitiesHash() =>
|
String _$seamlessFilteredTabEntitiesHash() =>
|
||||||
r'79abca3ad2753af4482c0e3bcfa317a6f336ca17';
|
r'83113c04be7932c60c7c97b70595fde0145f8040';
|
||||||
|
|
||||||
final class SeamlessFilteredTabEntitiesFamily extends $Family
|
final class SeamlessFilteredTabEntitiesFamily extends $Family
|
||||||
with
|
with
|
||||||
@@ -1097,7 +1097,7 @@ final class FilteredTabPreviewsFamily extends $Family
|
|||||||
|
|
||||||
/// Grouped flat-list rendering for the list and grid views.
|
/// Grouped flat-list rendering for the list and grid views.
|
||||||
///
|
///
|
||||||
/// Parent rows always render before their descendants. [TabListDirection]
|
/// Parent rows always render before their descendants. [TabDirection]
|
||||||
/// applies both to root group ordering and to sibling ordering below each
|
/// applies both to root group ordering and to sibling ordering below each
|
||||||
/// parent, so parent-child pairs stay together while child order still follows
|
/// parent, so parent-child pairs stay together while child order still follows
|
||||||
/// the configured direction.
|
/// the configured direction.
|
||||||
@@ -1109,7 +1109,7 @@ final groupedTabListItemsProvider = GroupedTabListItemsFamily._();
|
|||||||
|
|
||||||
/// Grouped flat-list rendering for the list and grid views.
|
/// Grouped flat-list rendering for the list and grid views.
|
||||||
///
|
///
|
||||||
/// Parent rows always render before their descendants. [TabListDirection]
|
/// Parent rows always render before their descendants. [TabDirection]
|
||||||
/// applies both to root group ordering and to sibling ordering below each
|
/// applies both to root group ordering and to sibling ordering below each
|
||||||
/// parent, so parent-child pairs stay together while child order still follows
|
/// parent, so parent-child pairs stay together while child order still follows
|
||||||
/// the configured direction.
|
/// the configured direction.
|
||||||
@@ -1126,7 +1126,7 @@ final class GroupedTabListItemsProvider
|
|||||||
with $Provider<EquatableValue<List<TabListItemEntity>>> {
|
with $Provider<EquatableValue<List<TabListItemEntity>>> {
|
||||||
/// Grouped flat-list rendering for the list and grid views.
|
/// Grouped flat-list rendering for the list and grid views.
|
||||||
///
|
///
|
||||||
/// Parent rows always render before their descendants. [TabListDirection]
|
/// Parent rows always render before their descendants. [TabDirection]
|
||||||
/// applies both to root group ordering and to sibling ordering below each
|
/// applies both to root group ordering and to sibling ordering below each
|
||||||
/// parent, so parent-child pairs stay together while child order still follows
|
/// parent, so parent-child pairs stay together while child order still follows
|
||||||
/// the configured direction.
|
/// the configured direction.
|
||||||
@@ -1186,11 +1186,11 @@ final class GroupedTabListItemsProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$groupedTabListItemsHash() =>
|
String _$groupedTabListItemsHash() =>
|
||||||
r'48c7c971d95934afb48aa19b7400e208ee2f10e4';
|
r'15106bfa42146270245c7178928a834237042c1c';
|
||||||
|
|
||||||
/// Grouped flat-list rendering for the list and grid views.
|
/// Grouped flat-list rendering for the list and grid views.
|
||||||
///
|
///
|
||||||
/// Parent rows always render before their descendants. [TabListDirection]
|
/// Parent rows always render before their descendants. [TabDirection]
|
||||||
/// applies both to root group ordering and to sibling ordering below each
|
/// applies both to root group ordering and to sibling ordering below each
|
||||||
/// parent, so parent-child pairs stay together while child order still follows
|
/// parent, so parent-child pairs stay together while child order still follows
|
||||||
/// the configured direction.
|
/// the configured direction.
|
||||||
@@ -1214,7 +1214,7 @@ final class GroupedTabListItemsFamily extends $Family
|
|||||||
|
|
||||||
/// Grouped flat-list rendering for the list and grid views.
|
/// Grouped flat-list rendering for the list and grid views.
|
||||||
///
|
///
|
||||||
/// Parent rows always render before their descendants. [TabListDirection]
|
/// Parent rows always render before their descendants. [TabDirection]
|
||||||
/// applies both to root group ordering and to sibling ordering below each
|
/// applies both to root group ordering and to sibling ordering below each
|
||||||
/// parent, so parent-child pairs stay together while child order still follows
|
/// parent, so parent-child pairs stay together while child order still follows
|
||||||
/// the configured direction.
|
/// the configured direction.
|
||||||
|
|||||||
+5
-5
@@ -41,7 +41,7 @@ TabViewReorderResult? buildTabViewReorderResult({
|
|||||||
required Set<String> pinnedTabIds,
|
required Set<String> pinnedTabIds,
|
||||||
required int oldIndex,
|
required int oldIndex,
|
||||||
required int newIndex,
|
required int newIndex,
|
||||||
required TabListDirection tabListDirection,
|
required TabDirection tabListDirection,
|
||||||
required bool hierarchical,
|
required bool hierarchical,
|
||||||
required bool sortPinnedFirst,
|
required bool sortPinnedFirst,
|
||||||
}) {
|
}) {
|
||||||
@@ -178,7 +178,7 @@ TabViewReorderResult? buildTabViewReorderResult({
|
|||||||
// order before picking anchors so genBetween receives prev.orderKey <
|
// order before picking anchors so genBetween receives prev.orderKey <
|
||||||
// next.orderKey. Block contents (e.g. the moving subtree from _subtreeIds)
|
// next.orderKey. Block contents (e.g. the moving subtree from _subtreeIds)
|
||||||
// are already storage-ordered internally, so we reverse blocks as units.
|
// are already storage-ordered internally, so we reverse blocks as units.
|
||||||
if (tabListDirection == TabListDirection.newestFirst) {
|
if (tabListDirection == TabDirection.newestFirst) {
|
||||||
for (final blocks in blocksByRoot.values) {
|
for (final blocks in blocksByRoot.values) {
|
||||||
if (blocks.length > 1) {
|
if (blocks.length > 1) {
|
||||||
final reversedTail = blocks.sublist(1).reversed.toList();
|
final reversedTail = blocks.sublist(1).reversed.toList();
|
||||||
@@ -189,7 +189,7 @@ TabViewReorderResult? buildTabViewReorderResult({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final orderedRootIds = tabListDirection == TabListDirection.newestFirst
|
final orderedRootIds = tabListDirection == TabDirection.newestFirst
|
||||||
? rootOrder.reversed
|
? rootOrder.reversed
|
||||||
: rootOrder;
|
: rootOrder;
|
||||||
|
|
||||||
@@ -213,13 +213,13 @@ TabViewReorderResult? buildTabViewReorderResult({
|
|||||||
|
|
||||||
List<String> _orderedIdsForStorageAnchors(
|
List<String> _orderedIdsForStorageAnchors(
|
||||||
List<String> orderedTabIds, {
|
List<String> orderedTabIds, {
|
||||||
required TabListDirection tabListDirection,
|
required TabDirection tabListDirection,
|
||||||
required Set<String> pinnedTabIds,
|
required Set<String> pinnedTabIds,
|
||||||
required Map<String, String?> parentById,
|
required Map<String, String?> parentById,
|
||||||
required String movingPartitionRootId,
|
required String movingPartitionRootId,
|
||||||
required bool sortPinnedFirst,
|
required bool sortPinnedFirst,
|
||||||
}) {
|
}) {
|
||||||
var storageOrderedIds = tabListDirection == TabListDirection.newestFirst
|
var storageOrderedIds = tabListDirection == TabDirection.newestFirst
|
||||||
// Rendering flips root group order for newest-first; convert the
|
// Rendering flips root group order for newest-first; convert the
|
||||||
// display order back to storage order before choosing anchors.
|
// display order back to storage order before choosing anchors.
|
||||||
? orderedTabIds.reversed.toList()
|
? orderedTabIds.reversed.toList()
|
||||||
|
|||||||
+10
-1
@@ -200,7 +200,7 @@ class _TabGridView extends HookConsumerWidget {
|
|||||||
).select((value) => (value.value?.query ?? '').isNotEmpty),
|
).select((value) => (value.value?.query ?? '').isNotEmpty),
|
||||||
);
|
);
|
||||||
|
|
||||||
final List<TabViewItem> primaryRows;
|
List<TabViewItem> primaryRows;
|
||||||
if (hasActiveSearch) {
|
if (hasActiveSearch) {
|
||||||
final flat = ref.watch(
|
final flat = ref.watch(
|
||||||
seamlessFilteredTabEntitiesProvider(
|
seamlessFilteredTabEntitiesProvider(
|
||||||
@@ -242,6 +242,15 @@ class _TabGridView extends HookConsumerWidget {
|
|||||||
: TabViewItem.standalone(tabId: c.tabId),
|
: TabViewItem.standalone(tabId: c.tabId),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
if (!showHierarchicalTabs && filterOptions.sortPinnedFirst) {
|
||||||
|
final pinned = primaryRows
|
||||||
|
.where((r) => pinnedTabIds.contains(r.tabId))
|
||||||
|
.toList();
|
||||||
|
final unpinned = primaryRows
|
||||||
|
.where((r) => !pinnedTabIds.contains(r.tabId))
|
||||||
|
.toList();
|
||||||
|
primaryRows = [...pinned, ...unpinned];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final tabSuggestionsEnabled = ref.watch(
|
final tabSuggestionsEnabled = ref.watch(
|
||||||
|
|||||||
+10
-1
@@ -261,7 +261,7 @@ class _TabListView extends HookConsumerWidget {
|
|||||||
).select((value) => (value.value?.query ?? '').isNotEmpty),
|
).select((value) => (value.value?.query ?? '').isNotEmpty),
|
||||||
);
|
);
|
||||||
|
|
||||||
final List<TabViewItem> primaryRows;
|
List<TabViewItem> primaryRows;
|
||||||
if (hasActiveSearch) {
|
if (hasActiveSearch) {
|
||||||
final flat = ref.watch(
|
final flat = ref.watch(
|
||||||
seamlessFilteredTabEntitiesProvider(
|
seamlessFilteredTabEntitiesProvider(
|
||||||
@@ -303,6 +303,15 @@ class _TabListView extends HookConsumerWidget {
|
|||||||
: TabViewItem.standalone(tabId: c.tabId),
|
: TabViewItem.standalone(tabId: c.tabId),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
if (!showHierarchicalTabs && filterOptions.sortPinnedFirst) {
|
||||||
|
final pinned = primaryRows
|
||||||
|
.where((r) => pinnedTabIds.contains(r.tabId))
|
||||||
|
.toList();
|
||||||
|
final unpinned = primaryRows
|
||||||
|
.where((r) => !pinnedTabIds.contains(r.tabId))
|
||||||
|
.toList();
|
||||||
|
primaryRows = [...pinned, ...unpinned];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final tabSuggestionsEnabled = ref.watch(
|
final tabSuggestionsEnabled = ref.watch(
|
||||||
|
|||||||
+1
-1
@@ -33,7 +33,7 @@ final class IntentGatekeeperProvider
|
|||||||
IntentGatekeeper create() => IntentGatekeeper();
|
IntentGatekeeper create() => IntentGatekeeper();
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$intentGatekeeperHash() => r'0ab4a96dde7a21df5dd32d034f841bf4d40dbb10';
|
String _$intentGatekeeperHash() => r'466bef55521edc3574edaaea6e8cb2805bd87296';
|
||||||
|
|
||||||
abstract class _$IntentGatekeeper
|
abstract class _$IntentGatekeeper
|
||||||
extends $StreamNotifier<PendingIntentDecision> {
|
extends $StreamNotifier<PendingIntentDecision> {
|
||||||
|
|||||||
+9
-9
@@ -13,8 +13,8 @@ part of 'native_gatekeeper_replicator.dart';
|
|||||||
/// Only blocked packages are replicated — allow/unknown still fall through to
|
/// Only blocked packages are replicated — allow/unknown still fall through to
|
||||||
/// the Flutter gatekeeper dialog.
|
/// the Flutter gatekeeper dialog.
|
||||||
///
|
///
|
||||||
/// On startup, also consumes any "always allow" decisions made via notification
|
/// Also consumes any "always allow" decisions made via notification actions
|
||||||
/// actions while Flutter was not running, and merges them into Flutter's policy.
|
/// and merges them into Flutter's policy before the next gatekeeper check.
|
||||||
|
|
||||||
@ProviderFor(NativeIntentGatekeeperReplicator)
|
@ProviderFor(NativeIntentGatekeeperReplicator)
|
||||||
final nativeIntentGatekeeperReplicatorProvider =
|
final nativeIntentGatekeeperReplicatorProvider =
|
||||||
@@ -25,8 +25,8 @@ final nativeIntentGatekeeperReplicatorProvider =
|
|||||||
/// Only blocked packages are replicated — allow/unknown still fall through to
|
/// Only blocked packages are replicated — allow/unknown still fall through to
|
||||||
/// the Flutter gatekeeper dialog.
|
/// the Flutter gatekeeper dialog.
|
||||||
///
|
///
|
||||||
/// On startup, also consumes any "always allow" decisions made via notification
|
/// Also consumes any "always allow" decisions made via notification actions
|
||||||
/// actions while Flutter was not running, and merges them into Flutter's policy.
|
/// and merges them into Flutter's policy before the next gatekeeper check.
|
||||||
final class NativeIntentGatekeeperReplicatorProvider
|
final class NativeIntentGatekeeperReplicatorProvider
|
||||||
extends $NotifierProvider<NativeIntentGatekeeperReplicator, void> {
|
extends $NotifierProvider<NativeIntentGatekeeperReplicator, void> {
|
||||||
/// Mirrors the Flutter-side block list to the native side so the
|
/// Mirrors the Flutter-side block list to the native side so the
|
||||||
@@ -34,8 +34,8 @@ final class NativeIntentGatekeeperReplicatorProvider
|
|||||||
/// Only blocked packages are replicated — allow/unknown still fall through to
|
/// Only blocked packages are replicated — allow/unknown still fall through to
|
||||||
/// the Flutter gatekeeper dialog.
|
/// the Flutter gatekeeper dialog.
|
||||||
///
|
///
|
||||||
/// On startup, also consumes any "always allow" decisions made via notification
|
/// Also consumes any "always allow" decisions made via notification actions
|
||||||
/// actions while Flutter was not running, and merges them into Flutter's policy.
|
/// and merges them into Flutter's policy before the next gatekeeper check.
|
||||||
NativeIntentGatekeeperReplicatorProvider._()
|
NativeIntentGatekeeperReplicatorProvider._()
|
||||||
: super(
|
: super(
|
||||||
from: null,
|
from: null,
|
||||||
@@ -65,15 +65,15 @@ final class NativeIntentGatekeeperReplicatorProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$nativeIntentGatekeeperReplicatorHash() =>
|
String _$nativeIntentGatekeeperReplicatorHash() =>
|
||||||
r'ed1ba2a318467317d6fb412495171acb8819e483';
|
r'bb2e2a252143879e558d513f9c48f3e66513baa9';
|
||||||
|
|
||||||
/// Mirrors the Flutter-side block list to the native side so the
|
/// Mirrors the Flutter-side block list to the native side so the
|
||||||
/// `IntentReceiverActivity` can reject intents without launching Flutter.
|
/// `IntentReceiverActivity` can reject intents without launching Flutter.
|
||||||
/// Only blocked packages are replicated — allow/unknown still fall through to
|
/// Only blocked packages are replicated — allow/unknown still fall through to
|
||||||
/// the Flutter gatekeeper dialog.
|
/// the Flutter gatekeeper dialog.
|
||||||
///
|
///
|
||||||
/// On startup, also consumes any "always allow" decisions made via notification
|
/// Also consumes any "always allow" decisions made via notification actions
|
||||||
/// actions while Flutter was not running, and merges them into Flutter's policy.
|
/// and merges them into Flutter's policy before the next gatekeeper check.
|
||||||
|
|
||||||
abstract class _$NativeIntentGatekeeperReplicator extends $Notifier<void> {
|
abstract class _$NativeIntentGatekeeperReplicator extends $Notifier<void> {
|
||||||
void build();
|
void build();
|
||||||
|
|||||||
@@ -383,12 +383,12 @@ class _TabListDirectionSection extends HookConsumerWidget {
|
|||||||
showSelectedIcon: false,
|
showSelectedIcon: false,
|
||||||
segments: const [
|
segments: const [
|
||||||
ButtonSegment(
|
ButtonSegment(
|
||||||
value: TabListDirection.newestFirst,
|
value: TabDirection.newestFirst,
|
||||||
label: Text('Newest first'),
|
label: Text('Newest first'),
|
||||||
icon: Icon(MdiIcons.arrowCollapseUp),
|
icon: Icon(MdiIcons.arrowCollapseUp),
|
||||||
),
|
),
|
||||||
ButtonSegment(
|
ButtonSegment(
|
||||||
value: TabListDirection.oldestFirst,
|
value: TabDirection.oldestFirst,
|
||||||
label: Text('Oldest first'),
|
label: Text('Oldest first'),
|
||||||
icon: Icon(MdiIcons.arrowCollapseDown),
|
icon: Icon(MdiIcons.arrowCollapseDown),
|
||||||
),
|
),
|
||||||
@@ -438,12 +438,12 @@ class _TabBarDirectionSection extends HookConsumerWidget {
|
|||||||
showSelectedIcon: false,
|
showSelectedIcon: false,
|
||||||
segments: const [
|
segments: const [
|
||||||
ButtonSegment(
|
ButtonSegment(
|
||||||
value: TabBarDirection.newestFirst,
|
value: TabDirection.newestFirst,
|
||||||
label: Text('Newest first'),
|
label: Text('Newest first'),
|
||||||
icon: Icon(MdiIcons.arrowCollapseLeft),
|
icon: Icon(MdiIcons.arrowCollapseLeft),
|
||||||
),
|
),
|
||||||
ButtonSegment(
|
ButtonSegment(
|
||||||
value: TabBarDirection.oldestFirst,
|
value: TabDirection.oldestFirst,
|
||||||
label: Text('Oldest first'),
|
label: Text('Oldest first'),
|
||||||
icon: Icon(MdiIcons.arrowCollapseRight),
|
icon: Icon(MdiIcons.arrowCollapseRight),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -46,9 +46,7 @@ enum QuickTabSwitcherMode { lastUsedTabs, containerTabs }
|
|||||||
|
|
||||||
enum TabIntentOpenSetting { regular, private, isolated, ask }
|
enum TabIntentOpenSetting { regular, private, isolated, ask }
|
||||||
|
|
||||||
enum TabListDirection { newestFirst, oldestFirst }
|
enum TabDirection { newestFirst, oldestFirst }
|
||||||
|
|
||||||
enum TabBarDirection { newestFirst, oldestFirst }
|
|
||||||
|
|
||||||
enum TabBarPosition { top, bottom }
|
enum TabBarPosition { top, bottom }
|
||||||
|
|
||||||
@@ -88,8 +86,8 @@ class GeneralSettings with FastEquatable {
|
|||||||
final bool showIsolatedTabUi;
|
final bool showIsolatedTabUi;
|
||||||
@JsonKey(name: 'defaultCreateTabType')
|
@JsonKey(name: 'defaultCreateTabType')
|
||||||
final TabType storedDefaultCreateTabType;
|
final TabType storedDefaultCreateTabType;
|
||||||
final TabListDirection tabListDirection;
|
final TabDirection tabListDirection;
|
||||||
final TabBarDirection tabBarDirection;
|
final TabDirection tabBarDirection;
|
||||||
final TabIntentOpenSetting tabIntentOpenSetting;
|
final TabIntentOpenSetting tabIntentOpenSetting;
|
||||||
final bool autoHideTabBar;
|
final bool autoHideTabBar;
|
||||||
final TabBarSwipeAction tabBarSwipeAction;
|
final TabBarSwipeAction tabBarSwipeAction;
|
||||||
@@ -199,8 +197,8 @@ class GeneralSettings with FastEquatable {
|
|||||||
bool? showContainerUi,
|
bool? showContainerUi,
|
||||||
bool? showIsolatedTabUi,
|
bool? showIsolatedTabUi,
|
||||||
TabType? storedDefaultCreateTabType,
|
TabType? storedDefaultCreateTabType,
|
||||||
TabListDirection? tabListDirection,
|
TabDirection? tabListDirection,
|
||||||
TabBarDirection? tabBarDirection,
|
TabDirection? tabBarDirection,
|
||||||
TabIntentOpenSetting? tabIntentOpenSetting,
|
TabIntentOpenSetting? tabIntentOpenSetting,
|
||||||
bool? autoHideTabBar,
|
bool? autoHideTabBar,
|
||||||
TabBarSwipeAction? tabBarSwipeAction,
|
TabBarSwipeAction? tabBarSwipeAction,
|
||||||
@@ -253,8 +251,8 @@ class GeneralSettings with FastEquatable {
|
|||||||
showIsolatedTabUi = showIsolatedTabUi ?? true,
|
showIsolatedTabUi = showIsolatedTabUi ?? true,
|
||||||
storedDefaultCreateTabType =
|
storedDefaultCreateTabType =
|
||||||
storedDefaultCreateTabType ?? TabType.regular,
|
storedDefaultCreateTabType ?? TabType.regular,
|
||||||
tabListDirection = tabListDirection ?? TabListDirection.newestFirst,
|
tabListDirection = tabListDirection ?? TabDirection.newestFirst,
|
||||||
tabBarDirection = tabBarDirection ?? TabBarDirection.newestFirst,
|
tabBarDirection = tabBarDirection ?? TabDirection.newestFirst,
|
||||||
tabIntentOpenSetting = tabIntentOpenSetting ?? TabIntentOpenSetting.ask,
|
tabIntentOpenSetting = tabIntentOpenSetting ?? TabIntentOpenSetting.ask,
|
||||||
autoHideTabBar = autoHideTabBar ?? true,
|
autoHideTabBar = autoHideTabBar ?? true,
|
||||||
tabBarSwipeAction =
|
tabBarSwipeAction =
|
||||||
|
|||||||
@@ -43,9 +43,9 @@ abstract class _$GeneralSettingsCWProxy {
|
|||||||
TabType storedDefaultCreateTabType,
|
TabType storedDefaultCreateTabType,
|
||||||
);
|
);
|
||||||
|
|
||||||
GeneralSettings tabListDirection(TabListDirection tabListDirection);
|
GeneralSettings tabListDirection(TabDirection tabListDirection);
|
||||||
|
|
||||||
GeneralSettings tabBarDirection(TabBarDirection tabBarDirection);
|
GeneralSettings tabBarDirection(TabDirection tabBarDirection);
|
||||||
|
|
||||||
GeneralSettings tabIntentOpenSetting(
|
GeneralSettings tabIntentOpenSetting(
|
||||||
TabIntentOpenSetting tabIntentOpenSetting,
|
TabIntentOpenSetting tabIntentOpenSetting,
|
||||||
@@ -156,8 +156,8 @@ abstract class _$GeneralSettingsCWProxy {
|
|||||||
bool showContainerUi,
|
bool showContainerUi,
|
||||||
bool showIsolatedTabUi,
|
bool showIsolatedTabUi,
|
||||||
TabType storedDefaultCreateTabType,
|
TabType storedDefaultCreateTabType,
|
||||||
TabListDirection tabListDirection,
|
TabDirection tabListDirection,
|
||||||
TabBarDirection tabBarDirection,
|
TabDirection tabBarDirection,
|
||||||
TabIntentOpenSetting tabIntentOpenSetting,
|
TabIntentOpenSetting tabIntentOpenSetting,
|
||||||
bool autoHideTabBar,
|
bool autoHideTabBar,
|
||||||
TabBarSwipeAction tabBarSwipeAction,
|
TabBarSwipeAction tabBarSwipeAction,
|
||||||
@@ -268,11 +268,11 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
|
|||||||
) => call(storedDefaultCreateTabType: storedDefaultCreateTabType);
|
) => call(storedDefaultCreateTabType: storedDefaultCreateTabType);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
GeneralSettings tabListDirection(TabListDirection tabListDirection) =>
|
GeneralSettings tabListDirection(TabDirection tabListDirection) =>
|
||||||
call(tabListDirection: tabListDirection);
|
call(tabListDirection: tabListDirection);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
GeneralSettings tabBarDirection(TabBarDirection tabBarDirection) =>
|
GeneralSettings tabBarDirection(TabDirection tabBarDirection) =>
|
||||||
call(tabBarDirection: tabBarDirection);
|
call(tabBarDirection: tabBarDirection);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -585,13 +585,13 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
|
|||||||
tabListDirection == null
|
tabListDirection == null
|
||||||
? _value.tabListDirection
|
? _value.tabListDirection
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
: tabListDirection as TabListDirection,
|
: tabListDirection as TabDirection,
|
||||||
tabBarDirection:
|
tabBarDirection:
|
||||||
tabBarDirection == const $CopyWithPlaceholder() ||
|
tabBarDirection == const $CopyWithPlaceholder() ||
|
||||||
tabBarDirection == null
|
tabBarDirection == null
|
||||||
? _value.tabBarDirection
|
? _value.tabBarDirection
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
: tabBarDirection as TabBarDirection,
|
: tabBarDirection as TabDirection,
|
||||||
tabIntentOpenSetting:
|
tabIntentOpenSetting:
|
||||||
tabIntentOpenSetting == const $CopyWithPlaceholder() ||
|
tabIntentOpenSetting == const $CopyWithPlaceholder() ||
|
||||||
tabIntentOpenSetting == null
|
tabIntentOpenSetting == null
|
||||||
@@ -851,11 +851,11 @@ GeneralSettings _$GeneralSettingsFromJson(
|
|||||||
json['defaultCreateTabType'],
|
json['defaultCreateTabType'],
|
||||||
),
|
),
|
||||||
tabListDirection: $enumDecodeNullable(
|
tabListDirection: $enumDecodeNullable(
|
||||||
_$TabListDirectionEnumMap,
|
_$TabDirectionEnumMap,
|
||||||
json['tabListDirection'],
|
json['tabListDirection'],
|
||||||
),
|
),
|
||||||
tabBarDirection: $enumDecodeNullable(
|
tabBarDirection: $enumDecodeNullable(
|
||||||
_$TabBarDirectionEnumMap,
|
_$TabDirectionEnumMap,
|
||||||
json['tabBarDirection'],
|
json['tabBarDirection'],
|
||||||
),
|
),
|
||||||
tabIntentOpenSetting: $enumDecodeNullable(
|
tabIntentOpenSetting: $enumDecodeNullable(
|
||||||
@@ -955,8 +955,8 @@ Map<String, dynamic> _$GeneralSettingsToJson(
|
|||||||
'showIsolatedTabUi': instance.showIsolatedTabUi,
|
'showIsolatedTabUi': instance.showIsolatedTabUi,
|
||||||
'defaultCreateTabType':
|
'defaultCreateTabType':
|
||||||
_$TabTypeEnumMap[instance.storedDefaultCreateTabType]!,
|
_$TabTypeEnumMap[instance.storedDefaultCreateTabType]!,
|
||||||
'tabListDirection': _$TabListDirectionEnumMap[instance.tabListDirection]!,
|
'tabListDirection': _$TabDirectionEnumMap[instance.tabListDirection]!,
|
||||||
'tabBarDirection': _$TabBarDirectionEnumMap[instance.tabBarDirection]!,
|
'tabBarDirection': _$TabDirectionEnumMap[instance.tabBarDirection]!,
|
||||||
'tabIntentOpenSetting':
|
'tabIntentOpenSetting':
|
||||||
_$TabIntentOpenSettingEnumMap[instance.tabIntentOpenSetting]!,
|
_$TabIntentOpenSettingEnumMap[instance.tabIntentOpenSetting]!,
|
||||||
'autoHideTabBar': instance.autoHideTabBar,
|
'autoHideTabBar': instance.autoHideTabBar,
|
||||||
@@ -1031,14 +1031,9 @@ const _$TabTypeEnumMap = {
|
|||||||
TabType.isolated: 'isolated',
|
TabType.isolated: 'isolated',
|
||||||
};
|
};
|
||||||
|
|
||||||
const _$TabListDirectionEnumMap = {
|
const _$TabDirectionEnumMap = {
|
||||||
TabListDirection.newestFirst: 'newestFirst',
|
TabDirection.newestFirst: 'newestFirst',
|
||||||
TabListDirection.oldestFirst: 'oldestFirst',
|
TabDirection.oldestFirst: 'oldestFirst',
|
||||||
};
|
|
||||||
|
|
||||||
const _$TabBarDirectionEnumMap = {
|
|
||||||
TabBarDirection.newestFirst: 'newestFirst',
|
|
||||||
TabBarDirection.oldestFirst: 'oldestFirst',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const _$TabIntentOpenSettingEnumMap = {
|
const _$TabIntentOpenSettingEnumMap = {
|
||||||
|
|||||||
Reference in New Issue
Block a user