Add Supa account and search changes

This commit is contained in:
Fabian Freund
2026-05-22 18:10:22 +02:00
parent 3a19865b2e
commit 51289f1266
374 changed files with 54061 additions and 5013 deletions
@@ -31,15 +31,18 @@ class BrowserIcon with FastEquatable {
final Color? dominantColor;
final IconSource source;
static Future<BrowserIcon> fromBytes(
static Future<BrowserIcon?> fromBytes(
Uint8List bytes, {
required Color? dominantColor,
required IconSource source,
}) async {
final image = await tryDecodeImage(bytes);
if (image == null) {
return null;
}
return BrowserIcon(
image: image!,
image: image,
dominantColor: dominantColor,
source: source,
);
@@ -48,23 +48,6 @@ part 'tab_state.g.dart';
@Riverpod(keepAlive: true)
class TabStates extends _$TabStates {
/// Disposes images from a TabState to free GPU memory.
void _disposeTabImages(TabState tab) {
tab.icon?.dispose();
tab.thumbnail?.dispose();
}
/// Updates state while disposing images from removed tabs.
void _updateState(Map<String, TabState> newState) {
// Find and dispose images from tabs that are being removed
for (final tabId in state.keys) {
if (!newState.containsKey(tabId)) {
_disposeTabImages(state[tabId]!);
}
}
state = newState;
}
Future<void> _onTabContentStateChange(TabContentState contentState) async {
final current = await patchedState(contentState.id);
@@ -103,7 +86,7 @@ class TabStates extends _$TabStates {
showToolbarAsExpanded: contentState.showToolbarAsExpanded,
);
_updateState({...state}..[contentState.id] = newState);
state = {...state}..[contentState.id] = newState;
if (newState.isFinishedLoading) {
ref
@@ -139,9 +122,6 @@ class TabStates extends _$TabStates {
final image = await bytes.mapNotNull((bytes) => tryDecodeImage(bytes));
final current = state[tabId] ?? TabState.$default(tabId);
// Dispose old icon only after successfully creating new one
current.icon?.dispose();
state = {...state}..[tabId] = current.copyWith.icon(image);
}
@@ -151,9 +131,6 @@ class TabStates extends _$TabStates {
final image = await bytes.mapNotNull((bytes) => tryDecodeImage(bytes));
final current = state[tabId] ?? TabState.$default(tabId);
// Dispose old thumbnail only after successfully creating new one
current.thumbnail?.dispose();
state = {...state}..[tabId] = current.copyWith.thumbnail(image);
}
@@ -368,12 +345,6 @@ class TabStates extends _$TabStates {
);
ref.onDispose(() async {
// Dispose all remaining tab images
for (final tab in state.values) {
_disposeTabImages(tab);
}
// Cancel all stream subscriptions
for (final sub in subscriptions) {
await sub.cancel();
}
@@ -41,7 +41,7 @@ final class TabStatesProvider
}
}
String _$tabStatesHash() => r'6fc9a91b1343f9ae0b442100bed20e42448afa37';
String _$tabStatesHash() => r'd7efef406dd132a3ef1a67da06c794d18ccb484a';
abstract class _$TabStates extends $Notifier<Map<String, TabState>> {
Map<String, TabState> build();
@@ -35,8 +35,7 @@ part 'web_extensions_state.g.dart';
class WebExtensionsState extends _$WebExtensionsState {
late final LRUCache<String, EquatableImage> _imageCache;
WebExtensionsState()
: _imageCache = LRUCache(50, onEvict: (image) => image.dispose());
WebExtensionsState() : _imageCache = LRUCache(50);
void _onExtensionUpdate(ExtensionDataEvent event) {
if (!ref.mounted) {
@@ -46,11 +45,6 @@ class WebExtensionsState extends _$WebExtensionsState {
final ExtensionDataEvent(:extensionId, :data) = event;
if (data != null) {
final cachedIcon = _imageCache.get(extensionId);
if (cachedIcon != null && cachedIcon.value == null) {
_imageCache.remove(extensionId);
}
final current =
state[extensionId] ??
WebExtensionState(
@@ -74,7 +68,6 @@ class WebExtensionsState extends _$WebExtensionsState {
} else {
if (state.containsKey(extensionId)) {
state = {...state}..remove(extensionId);
// remove() triggers onEvict which handles disposal
_imageCache.remove(extensionId);
}
}
@@ -91,7 +84,6 @@ class WebExtensionsState extends _$WebExtensionsState {
}
if (image != null) {
// set() will evict the old entry via onEvict callback, which handles disposal
_imageCache.set(extensionId, image);
if (state.containsKey(extensionId)) {
@@ -198,7 +190,6 @@ class WebExtensionsState extends _$WebExtensionsState {
// Dispose all cached images
_imageCache.clear();
// Cancel all stream subscriptions
for (final sub in subscriptions) {
unawaited(sub.cancel());
}
@@ -62,7 +62,7 @@ final class WebExtensionsStateProvider
}
String _$webExtensionsStateHash() =>
r'e067323e9a0a466e46e0c4d529667950ee62d4ca';
r'59379bbaa9ae867f9dc50dae5c0f06b159faaedf';
final class WebExtensionsStateFamily extends $Family
with
@@ -26,6 +26,7 @@ import 'package:nullability/nullability.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:synchronized/synchronized.dart';
import 'package:weblibre/core/logger.dart';
import 'package:weblibre/core/routing/routes.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';
@@ -49,6 +50,20 @@ import 'package:weblibre/utils/debouncer.dart';
part 'tab.g.dart';
sealed class TabBackPromptBehavior {
const TabBackPromptBehavior();
}
final class BackgroundAppTabBackPromptBehavior extends TabBackPromptBehavior {
const BackgroundAppTabBackPromptBehavior();
}
final class ReturnToSearchTabBackPromptBehavior extends TabBackPromptBehavior {
final TabType tabType;
const ReturnToSearchTabBackPromptBehavior({required this.tabType});
}
@Riverpod(keepAlive: true)
class TabRepository extends _$TabRepository {
final _tabsService = GeckoTabService();
@@ -57,20 +72,20 @@ class TabRepository extends _$TabRepository {
bool _reclosing = false;
bool _suppressNextReclose = false;
final _tabFromIntent = <String>{};
final _tabBackPromptBehavior = <String, TabBackPromptBehavior>{};
final _closeLock = Lock();
final _pendingIsolationCleanup = <String>{};
bool hasLaunchedFromIntent(String? tabId) {
TabBackPromptBehavior? backPromptBehaviorFor(String? tabId) {
if (tabId == null) {
return false;
return null;
}
return _tabFromIntent.contains(tabId);
return _tabBackPromptBehavior[tabId];
}
void clearLaunchedFromIntent(String tabId) {
_tabFromIntent.remove(tabId);
void clearBackPromptBehavior(String tabId) {
_tabBackPromptBehavior.remove(tabId);
}
Future<String?> _resolveParentIdForContext({
@@ -105,6 +120,7 @@ class TabRepository extends _$TabRepository {
TabContainerSelection containerSelection =
const TabContainerSelection.useSelected(),
bool launchedFromIntent = false,
TabBackPromptBehavior? promptOnBackBehavior,
}) async {
final tabDao = ref.read(tabDatabaseProvider).tabDao;
@@ -152,12 +168,43 @@ class TabRepository extends _$TabRepository {
);
if (launchedFromIntent) {
_tabFromIntent.add(newTabId);
_tabBackPromptBehavior[newTabId] =
promptOnBackBehavior ?? const BackgroundAppTabBackPromptBehavior();
} else if (promptOnBackBehavior != null) {
_tabBackPromptBehavior[newTabId] = promptOnBackBehavior;
}
return newTabId;
}
Future<bool> _closeRestoredPrivateCaptureTabs(List<String> tabIds) async {
if (tabIds.isEmpty) {
return false;
}
final captureRows = await ref
.read(tabDatabaseProvider)
.captureTabDao
.findAll();
final tabStates = ref.read(tabStatesProvider);
final restoredPrivateCaptureTabIds = captureRows
.where((row) => row.createdAt.isBefore(_sessionStartedAt))
.map((row) => row.tabId)
.where((tabId) => tabIds.contains(tabId))
.where((tabId) => tabStates[tabId]?.tabMode is PrivateTabMode)
.toList(growable: false);
if (restoredPrivateCaptureTabIds.isEmpty) {
return false;
}
await _closeTabsInternal(
restoredPrivateCaptureTabIds,
recordTombstones: false,
);
return true;
}
Future<List<String>> addMultipleTabs({
required List<AddTabParams> tabs,
String? selectTabId,
@@ -571,6 +618,7 @@ class TabRepository extends _$TabRepository {
}
for (final tabId in tabIds) {
_tabBackPromptBehavior.remove(tabId);
final isolationContextId = ref
.read(tabStatesProvider)[tabId]
?.isolationContextId;
@@ -935,6 +983,8 @@ class TabRepository extends _$TabRepository {
await _clearTombstonesForCurrentTabs(next.value);
} else if (await _recloseRestoredClosedTabs(next.value)) {
return;
} else if (await _closeRestoredPrivateCaptureTabs(next.value)) {
return;
}
//Only sync tabs if there has been a previous value or is not empty
@@ -41,7 +41,7 @@ final class TabRepositoryProvider
}
}
String _$tabRepositoryHash() => r'f458baaf9061143a668470dccd57f32664728951';
String _$tabRepositoryHash() => r'ea5caa3c9c4b19898db9de1f77d5476794b8b66f';
abstract class _$TabRepository extends $Notifier<void> {
void build();