diff --git a/app/lib/features/geckoview/domain/repositories/tab.dart b/app/lib/features/geckoview/domain/repositories/tab.dart index 9a01e5b6..3b593ac4 100644 --- a/app/lib/features/geckoview/domain/repositories/tab.dart +++ b/app/lib/features/geckoview/domain/repositories/tab.dart @@ -143,11 +143,19 @@ class TabRepository extends _$TabRepository { return false; } - Future selectPreviousTab(String tabId) async { + Future selectPreviousTab( + String tabId, { + String? containerId, + bool skipContainerCheck = true, + }) async { final previousTabId = await ref .read(tabDatabaseProvider) .definitionsDrift - .previousTabByOrderKey(tabId: tabId) + .previousTabByOrderKey( + tabId: tabId, + containerId: containerId, + skipContainerCheck: skipContainerCheck, + ) .getSingleOrNull(); if (previousTabId != null) { @@ -157,11 +165,19 @@ class TabRepository extends _$TabRepository { return false; } - Future selectNextTab(String tabId) async { + Future selectNextTab( + String tabId, { + String? containerId, + bool skipContainerCheck = true, + }) async { final previousTabId = await ref .read(tabDatabaseProvider) .definitionsDrift - .nextTabByOrderKey(tabId: tabId) + .nextTabByOrderKey( + tabId: tabId, + containerId: containerId, + skipContainerCheck: skipContainerCheck, + ) .getSingleOrNull(); if (previousTabId != null) { @@ -211,9 +227,8 @@ class TabRepository extends _$TabRepository { } Future _selectNextTab(String tabId) async { - if (ref.read(tabListProvider).value.length == 1) { - return; - } + // ignore: only_use_keep_alive_inside_keep_alive + final tabState = ref.read(tabStateProvider(tabId)); final currentContainerId = await ref .read(tabDataRepositoryProvider.notifier) @@ -221,19 +236,59 @@ class TabRepository extends _$TabRepository { final sameContainerTabs = await ref .read(containerRepositoryProvider.notifier) - .getContainerTabIds(currentContainerId); + .getContainerTabIds(currentContainerId) + .then((tabs) => tabs.where((tab) => tab != tabId).toList()); - final nextAvailabeInContainer = sameContainerTabs.firstWhereOrNull( - (tab) => tab != tabId, - ); + final previousTabId = await ref + .read(tabDatabaseProvider) + .definitionsDrift + .previousTabByTimestamp(tabId: tabId) + .getSingleOrNull(); - if (nextAvailabeInContainer != null) { - return _tabsService.selectTab(tabId: sameContainerTabs.first); + if (previousTabId != null) { + if (sameContainerTabs.any((tab) => tab == previousTabId)) { + return _tabsService.selectTab(tabId: previousTabId); + } + } + + if (tabState?.parentId != null) { + if (sameContainerTabs.any((tab) => tab == tabState?.parentId)) { + return _tabsService.selectTab(tabId: tabState!.parentId!); + } + } + + final previousOrderedTabId = await ref + .read(tabDatabaseProvider) + .definitionsDrift + .previousTabByOrderKey( + tabId: tabId, + containerId: currentContainerId, + skipContainerCheck: false, + ) + .getSingleOrNull(); + + if (previousOrderedTabId != null) { + return _tabsService.selectTab(tabId: previousOrderedTabId); + } + + final nextOrderedTabId = await ref + .read(tabDatabaseProvider) + .definitionsDrift + .nextTabByOrderKey( + tabId: tabId, + containerId: currentContainerId, + skipContainerCheck: false, + ) + .getSingleOrNull(); + + if (nextOrderedTabId != null) { + return _tabsService.selectTab(tabId: nextOrderedTabId); } final unassignedTabs = await ref .read(containerRepositoryProvider.notifier) - .getContainerTabIds(null); + .getContainerTabIds(null) + .then((tabs) => tabs.where((tab) => tab != tabId).toList()); if (unassignedTabs.isNotEmpty) { return _tabsService.selectTab(tabId: unassignedTabs.first); @@ -254,7 +309,8 @@ class TabRepository extends _$TabRepository { .mapNotNull( (container) => ref .read(containerRepositoryProvider.notifier) - .getContainerTabIds(container.id), + .getContainerTabIds(container.id) + .then((tabs) => tabs.where((tab) => tab != tabId).toList()), ); if (nextContainerTabs.isNotEmpty) { diff --git a/app/lib/features/geckoview/domain/repositories/tab.g.dart b/app/lib/features/geckoview/domain/repositories/tab.g.dart index 858853f9..584db2f2 100644 --- a/app/lib/features/geckoview/domain/repositories/tab.g.dart +++ b/app/lib/features/geckoview/domain/repositories/tab.g.dart @@ -41,7 +41,7 @@ final class TabRepositoryProvider } } -String _$tabRepositoryHash() => r'7cb56001fbb038321197d237148c9ebf35740521'; +String _$tabRepositoryHash() => r'adf53bb00e5c80e9fcad6b9111d5723c996e966f'; abstract class _$TabRepository extends $Notifier { void build(); diff --git a/app/lib/features/geckoview/features/tabs/data/database/definitions.drift b/app/lib/features/geckoview/features/tabs/data/database/definitions.drift index 30f71ae3..369f60f3 100644 --- a/app/lib/features/geckoview/features/tabs/data/database/definitions.drift +++ b/app/lib/features/geckoview/features/tabs/data/database/definitions.drift @@ -82,7 +82,7 @@ containersWithCount WITH ContainerDataWithCount: ) AS tab_agg ON container.id = tab_agg.container_id ORDER BY tab_agg.last_updated DESC NULLS LAST; -leadingOrderKey(:container_id AS TEXT OR NULL, :bucket AS INTEGER): +leadingOrderKey(REQUIRED :container_id AS TEXT OR NULL, :bucket AS INTEGER): SELECT lexo_rank_previous( :bucket, ( @@ -94,7 +94,7 @@ leadingOrderKey(:container_id AS TEXT OR NULL, :bucket AS INTEGER): ) ); -trailingOrderKey(:container_id AS TEXT OR NULL, :bucket AS INTEGER): +trailingOrderKey(REQUIRED :container_id AS TEXT OR NULL, :bucket AS INTEGER): SELECT lexo_rank_next( :bucket, ( @@ -106,7 +106,7 @@ trailingOrderKey(:container_id AS TEXT OR NULL, :bucket AS INTEGER): ) ); -orderKeyAfterTab(:tab_id AS TEXT, :container_id AS TEXT OR NULL): +orderKeyAfterTab(:tab_id AS TEXT, REQUIRED :container_id AS TEXT OR NULL): WITH ordered_table AS ( SELECT id, order_key, @@ -118,7 +118,7 @@ orderKeyAfterTab(:tab_id AS TEXT, :container_id AS TEXT OR NULL): FROM ordered_table WHERE id = :tab_id; -orderKeyBeforeTab(:tab_id AS TEXT, :container_id AS TEXT OR NULL): +orderKeyBeforeTab(:tab_id AS TEXT, REQUIRED :container_id AS TEXT OR NULL): WITH ordered_table AS ( SELECT id, order_key, @@ -252,21 +252,23 @@ previousTabByTimestamp: FROM ranked_tabs WHERE id = :tab_id; -previousTabByOrderKey: +previousTabByOrderKey(:tab_id AS TEXT, :container_id AS TEXT OR NULL, :skip_container_check AS BOOL): WITH ranked_tabs AS ( SELECT id, order_key, LAG(id) OVER (ORDER BY order_key) as prev_tab_id FROM tab + WHERE :skip_container_check OR container_id IS :container_id ) SELECT prev_tab_id FROM ranked_tabs WHERE id = :tab_id; -nextTabByOrderKey: +nextTabByOrderKey(:tab_id AS TEXT, :container_id AS TEXT OR NULL, :skip_container_check AS BOOL): WITH ranked_tabs AS ( SELECT id, order_key, LEAD(id) OVER (ORDER BY order_key) as next_tab_id FROM tab + WHERE :skip_container_check OR container_id IS :container_id ) SELECT next_tab_id FROM ranked_tabs diff --git a/app/lib/features/geckoview/features/tabs/data/database/definitions.drift.dart b/app/lib/features/geckoview/features/tabs/data/database/definitions.drift.dart index a9e36e16..969569a7 100644 --- a/app/lib/features/geckoview/features/tabs/data/database/definitions.drift.dart +++ b/app/lib/features/geckoview/features/tabs/data/database/definitions.drift.dart @@ -2115,7 +2115,7 @@ class DefinitionsDrift extends i7.ModularAccessor { i0.Selectable leadingOrderKey({ required int bucket, - String? containerId, + required String? containerId, }) { return customSelect( 'SELECT lexo_rank_previous(?1, (SELECT order_key FROM tab WHERE container_id IS ?2 ORDER BY order_key LIMIT 1)) AS _c0', @@ -2126,7 +2126,7 @@ class DefinitionsDrift extends i7.ModularAccessor { i0.Selectable trailingOrderKey({ required int bucket, - String? containerId, + required String? containerId, }) { return customSelect( 'SELECT lexo_rank_next(?1, (SELECT order_key FROM tab WHERE container_id IS ?2 ORDER BY order_key DESC LIMIT 1)) AS _c0', @@ -2136,7 +2136,7 @@ class DefinitionsDrift extends i7.ModularAccessor { } i0.Selectable orderKeyAfterTab({ - String? containerId, + required String? containerId, required String tabId, }) { return customSelect( @@ -2147,7 +2147,7 @@ class DefinitionsDrift extends i7.ModularAccessor { } i0.Selectable orderKeyBeforeTab({ - String? containerId, + required String? containerId, required String tabId, }) { return customSelect( @@ -2245,18 +2245,34 @@ class DefinitionsDrift extends i7.ModularAccessor { ).map((i0.QueryRow row) => row.readNullable('prev_tab_id')); } - i0.Selectable previousTabByOrderKey({required String tabId}) { + i0.Selectable previousTabByOrderKey({ + required bool skipContainerCheck, + String? containerId, + required String tabId, + }) { return customSelect( - 'WITH ranked_tabs AS (SELECT id, order_key, LAG(id)OVER (ORDER BY order_key RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE NO OTHERS) AS prev_tab_id FROM tab) SELECT prev_tab_id FROM ranked_tabs WHERE id = ?1', - variables: [i0.Variable(tabId)], + 'WITH ranked_tabs AS (SELECT id, order_key, LAG(id)OVER (ORDER BY order_key RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE NO OTHERS) AS prev_tab_id FROM tab WHERE ?1 OR container_id IS ?2) SELECT prev_tab_id FROM ranked_tabs WHERE id = ?3', + variables: [ + i0.Variable(skipContainerCheck), + i0.Variable(containerId), + i0.Variable(tabId), + ], readsFrom: {tab}, ).map((i0.QueryRow row) => row.readNullable('prev_tab_id')); } - i0.Selectable nextTabByOrderKey({required String tabId}) { + i0.Selectable nextTabByOrderKey({ + required bool skipContainerCheck, + String? containerId, + required String tabId, + }) { return customSelect( - 'WITH ranked_tabs AS (SELECT id, order_key, LEAD(id)OVER (ORDER BY order_key RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE NO OTHERS) AS next_tab_id FROM tab) SELECT next_tab_id FROM ranked_tabs WHERE id = ?1', - variables: [i0.Variable(tabId)], + 'WITH ranked_tabs AS (SELECT id, order_key, LEAD(id)OVER (ORDER BY order_key RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE NO OTHERS) AS next_tab_id FROM tab WHERE ?1 OR container_id IS ?2) SELECT next_tab_id FROM ranked_tabs WHERE id = ?3', + variables: [ + i0.Variable(skipContainerCheck), + i0.Variable(containerId), + i0.Variable(tabId), + ], readsFrom: {tab}, ).map((i0.QueryRow row) => row.readNullable('next_tab_id')); }