improved quick tab switcher & general tab design/logic;

This commit is contained in:
Fabian Freund
2026-06-12 03:47:20 +02:00
parent 83c78b53d3
commit 3c5f472bdf
35 changed files with 2715 additions and 768 deletions
@@ -32,6 +32,8 @@ import 'package:weblibre/extensions/uri.dart';
import 'package:weblibre/features/geckoview/domain/entities/states/tab.dart';
import 'package:weblibre/features/geckoview/domain/entities/tab_container_selection.dart';
import 'package:weblibre/features/geckoview/domain/providers.dart';
import 'package:weblibre/features/geckoview/domain/providers/pending_tab_selection.dart';
import 'package:weblibre/features/geckoview/domain/providers/restore_complete.dart';
import 'package:weblibre/features/geckoview/domain/providers/selected_tab.dart';
import 'package:weblibre/features/geckoview/domain/providers/tab_list.dart';
import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart';
@@ -456,6 +458,14 @@ class TabRepository extends _$TabRepository {
}
Future<bool> selectTab(String tabId) async {
// The tab is still a pre-restore placeholder (known to the DB but not to
// the engine yet): queue the selection until the native state arrives.
if (!ref.read(browserRestoreCompleteProvider) &&
!ref.read(tabStatesProvider).containsKey(tabId)) {
ref.read(pendingTabSelectionProvider.notifier).queue(tabId);
return true;
}
final containerData = await ref
.read(tabDataRepositoryProvider.notifier)
.getTabContainerData(tabId);
@@ -768,6 +778,18 @@ class TabRepository extends _$TabRepository {
}
}
Future<void> _drainPendingIsolationCleanup() async {
if (_pendingIsolationCleanup.isEmpty) return;
final pending = Set<String>.of(_pendingIsolationCleanup);
_pendingIsolationCleanup.clear();
for (final contextId in pending) {
if (!ref.mounted) break;
await _cleanupIsolationContextIfEmpty(contextId);
}
}
Future<void> undoClose() {
// Suppress the next reclose pass: undo can resurrect a tab whose
// tombstone is still on disk (from a previous session); without this
@@ -1037,9 +1059,14 @@ class TabRepository extends _$TabRepository {
return;
}
//Only sync tabs if there has been a previous value or is not empty
//Only sync tabs if there has been a previous value or is not empty.
//Additionally require the native session restore to have completed:
//a partial pre-restore list (e.g. a share-intent tab arriving first)
//must not delete the cached rows of tabs that are still being
//restored.
final shouldSyncTabs =
next.value.isNotEmpty || (previous?.value.isNotEmpty ?? false);
ref.read(browserRestoreCompleteProvider) &&
(next.value.isNotEmpty || (previous?.value.isNotEmpty ?? false));
if (shouldSyncTabs) {
final syncTabsResult = await db.tabDao.syncTabs(
@@ -1054,14 +1081,7 @@ class TabRepository extends _$TabRepository {
// Process pending isolation context cleanups after syncTabs
// has deleted the rows, so the count check is accurate.
if (_pendingIsolationCleanup.isNotEmpty) {
final pending = Set<String>.of(_pendingIsolationCleanup);
_pendingIsolationCleanup.clear();
for (final contextId in pending) {
if (!ref.mounted) break;
await _cleanupIsolationContextIfEmpty(contextId);
}
}
await _drainPendingIsolationCleanup();
// One-shot orphan cleanup after tab list stabilizes (5s debounce).
// Also runs for DB-only contexts whose rows were already deleted
@@ -1084,6 +1104,26 @@ class TabRepository extends _$TabRepository {
},
);
// Catch up on the tab list emissions skipped while the restore-complete
// gate above was closed: reconcile the DB once against the current list.
ref.listen(browserRestoreCompleteProvider, (
previous,
restoreComplete,
) async {
if (restoreComplete && !(previous ?? false)) {
final currentTabs = ref.read(tabListProvider).value;
if (currentTabs.isNotEmpty) {
final syncTabsResult = await db.tabDao.syncTabs(
retainTabIds: currentTabs,
);
_pendingIsolationCleanup.addAll(
syncTabsResult.deletedIsolationContextIds,
);
await _drainPendingIsolationCleanup();
}
}
});
final tabStateDebouncer = Debouncer(const Duration(seconds: 1));
Map<String, TabState>? debounceStartValue;
@@ -41,7 +41,7 @@ final class TabRepositoryProvider
}
}
String _$tabRepositoryHash() => r'd7b67460388c264f477325266a3299fe6df2df07';
String _$tabRepositoryHash() => r'982b80b8ea7c694958bc10cc7e8e15310af6a238';
abstract class _$TabRepository extends $Notifier<void> {
void build();