one step closer

This commit is contained in:
Fabian Freund
2024-10-22 07:27:37 +02:00
parent c4d93e8fe7
commit 8d351e55c0
92 changed files with 3829 additions and 2694 deletions
@@ -1,8 +1,9 @@
import 'dart:async';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:lensai/features/geckoview/domain/providers.dart';
import 'package:lensai/features/geckoview/features/topics/data/database/database.dart';
import 'package:lensai/features/geckoview/features/topics/data/providers.dart';
import 'package:lensai/features/geckoview/features/tabs/data/database/database.dart';
import 'package:lensai/features/geckoview/features/tabs/data/providers.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'selected_tab.g.dart';
@@ -13,21 +14,29 @@ class SelectedTab extends _$SelectedTab {
@override
String? build() {
_db = ref.watch(tabDatabaseProvider);
final eventSerivce = ref.watch(eventServiceProvider);
final selectedTabSub = eventSerivce.selectedTabEvents.listen(
(tabId) {
state = tabId;
_db = ref.watch(tabDatabaseProvider);
ref.listen(
fireImmediately: true,
engineReadyStateProvider,
(previous, next) async {
if (next) {
await GeckoTabService().syncEvents(onSelectedTabChange: true);
}
},
);
ref.listenSelf((previous, next) async {
if (next != null) {
await _db.tabLinkDao.touchTabLink(next, timestamp: DateTime.now());
}
});
final selectedTabSub = eventSerivce.selectedTabEvents.listen(
(tabId) async {
state = tabId;
if (tabId != null) {
await _db.tabDao.touchTab(tabId, timestamp: DateTime.now());
}
},
);
ref.onDispose(() {
unawaited(selectedTabSub.cancel());
@@ -6,7 +6,7 @@ part of 'selected_tab.dart';
// RiverpodGenerator
// **************************************************************************
String _$selectedTabHash() => r'd57003aa20d8a6f711918821ae355e5537a55685';
String _$selectedTabHash() => r'e49fb1c3938a97f384673e158742e56479aa8a8f';
/// See also [SelectedTab].
@ProviderFor(SelectedTab)
@@ -0,0 +1,53 @@
import 'dart:async';
import 'package:collection/collection.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:lensai/features/geckoview/domain/providers.dart';
import 'package:lensai/features/geckoview/features/tabs/data/database/database.dart';
import 'package:lensai/features/geckoview/features/tabs/data/providers.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'tab_list.g.dart';
@Riverpod(keepAlive: true)
class TabList extends _$TabList {
late TabDatabase _db;
List<String> build() {
final eventService = ref.watch(eventServiceProvider);
_db = ref.watch(tabDatabaseProvider);
ref.listen(
fireImmediately: true,
engineReadyStateProvider,
(previous, next) async {
if (next) {
await GeckoTabService().syncEvents(onTabListChange: true);
}
},
);
final tabListSub = eventService.tabListEvents.listen(
(tabs) async {
if (!const DeepCollectionEquality().equals(state, tabs)) {
//Make sure we only syncing empty lists when there has been tabs
//before as security measurement
final syncTabs = tabs.isNotEmpty || state.isNotEmpty;
state = tabs;
if (syncTabs) {
await _db.tabDao.syncTabs(retainTabIds: tabs);
}
}
},
);
ref.onDispose(() {
unawaited(tabListSub.cancel());
});
return [];
}
}
@@ -0,0 +1,24 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'tab_list.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$tabListHash() => r'fd97d4692b1c74e91c5ebd4a75e6ff45ceb82ef9';
/// See also [TabList].
@ProviderFor(TabList)
final tabListProvider = NotifierProvider<TabList, List<String>>.internal(
TabList.new,
name: r'tabListProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$tabListHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$TabList = Notifier<List<String>>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
@@ -33,6 +33,10 @@ class TabSession extends _$TabSession {
return _sessionService.goForward();
}
Future<void> exitFullscreen() {
return _sessionService.exitFullscreen();
}
Future<Uint8List?> requestScreenshot() {
return _sessionService.requestScreenshot();
}
@@ -6,7 +6,7 @@ part of 'tab_session.dart';
// RiverpodGenerator
// **************************************************************************
String _$tabSessionHash() => r'25afbead5c1110ca865e48a5009da70eb20d3a28';
String _$tabSessionHash() => r'29b20f40af01e599ef80e198aec5f093d74efc6a';
/// Copied from Dart SDK
class _SystemHash {
@@ -1,6 +1,5 @@
import 'dart:async';
import 'package:collection/collection.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:lensai/extensions/image.dart';
import 'package:lensai/features/geckoview/domain/entities/find_result_state.dart';
@@ -10,33 +9,25 @@ import 'package:lensai/features/geckoview/domain/entities/security_state.dart';
import 'package:lensai/features/geckoview/domain/entities/tab_state.dart';
import 'package:lensai/features/geckoview/domain/providers.dart';
import 'package:lensai/features/geckoview/domain/providers/selected_tab.dart';
import 'package:lensai/features/geckoview/features/topics/data/database/database.dart';
import 'package:lensai/features/geckoview/features/topics/data/providers.dart';
import 'package:lensai/features/geckoview/utils/image_helper.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:synchronized/synchronized.dart';
part 'tab_state.g.dart';
@Riverpod(keepAlive: true)
class TabStates extends _$TabStates {
final _tabsService = GeckoTabService();
late TabDatabase _db;
void _onTabListChange(List<String> tabs) {
state = {
for (final tabId in tabs) tabId: state[tabId] ?? TabState.$default(tabId),
};
}
final _lock = Lock();
void _onTabContentStateChange(TabContentState contentState) {
final current =
state[contentState.id] ?? TabState.$default(contentState.id);
state = {...state}..[contentState.id] = current.copyWith(
contextId: contentState.contextId,
url: Uri.parse(contentState.url),
title: contentState.title,
title: (contentState.title.isNotEmpty)
? contentState.title
: current.title,
progress: contentState.progress,
isPrivate: contentState.isPrivate,
isFullScreen: contentState.isFullScreen,
@@ -47,26 +38,24 @@ class TabStates extends _$TabStates {
Future<void> _onIconChange(IconEvent event) async {
final IconEvent(:tabId, :bytes) = event;
final current = state[tabId] ?? TabState.$default(tabId);
final image = (bytes != null)
? (await tryDecodeImage(bytes)
.then((image) async => image?.toEquatable()))
: null;
final current = state[tabId] ?? TabState.$default(tabId);
state = {...state}..[tabId] = current.copyWith.icon(image);
}
Future<void> _onThumbnailChange(ThumbnailEvent event) async {
final ThumbnailEvent(:tabId, :bytes) = event;
final current = state[tabId] ?? TabState.$default(tabId);
final image = (bytes != null)
? (await tryDecodeImage(bytes)
.then((image) async => image?.toEquatable()))
: null;
final current = state[tabId] ?? TabState.$default(tabId);
state = {...state}..[tabId] = current.copyWith.thumbnail(image);
}
@@ -74,7 +63,6 @@ class TabStates extends _$TabStates {
final SecurityInfoEvent(:tabId, :securityInfo) = event;
final current = state[tabId] ?? TabState.$default(tabId);
state = {...state}..[tabId] = current.copyWith.securityInfoState(
SecurityState(
secure: securityInfo.secure,
@@ -88,7 +76,6 @@ class TabStates extends _$TabStates {
final HistoryEvent(:tabId, :history) = event;
final current = state[tabId] ?? TabState.$default(tabId);
state = {...state}..[tabId] = current.copyWith.historyState(
HistoryState(
items: history.items.nonNulls
@@ -108,7 +95,6 @@ class TabStates extends _$TabStates {
final ReaderableEvent(:tabId, :readerable) = event;
final current = state[tabId] ?? TabState.$default(tabId);
state = {...state}..[tabId] = current.copyWith.readerableState(
ReaderableState(
readerable: readerable.readerable,
@@ -117,13 +103,13 @@ class TabStates extends _$TabStates {
);
}
void _onFindResults(FindResultsEvent event) {
void _onFindResultsChange(FindResultsEvent event) {
final FindResultsEvent(:tabId, :results) = event;
if (results.isNotEmpty) {
final current = state[tabId] ?? TabState.$default(tabId);
final result = results.last;
final current = state[tabId] ?? TabState.$default(tabId);
state = {...state}..[tabId] = current.copyWith.findResultState(
FindResultState(
activeMatchOrdinal: result.activeMatchOrdinal,
@@ -134,64 +120,61 @@ class TabStates extends _$TabStates {
}
}
Future<String> addTab({
Uri? url,
bool selectTab = true,
bool startLoading = true,
String? parentId,
LoadUrlFlags flags = LoadUrlFlags.NONE,
String? contextId,
Source source = Internal.newTab,
bool private = false,
HistoryMetadataKey? historyMetadata,
Map<String, String>? additionalHeaders,
}) {
return _tabsService.addTab(
url: url,
selectTab: selectTab,
startLoading: startLoading,
parentId: parentId,
flags: flags,
contextId: contextId,
source: source,
private: private,
historyMetadata: historyMetadata,
additionalHeaders: additionalHeaders,
);
}
Future<void> closeTabs(List<String> tabIds) {
return _tabsService.removeTabs(ids: tabIds);
}
Future<void> closeTab(String tabIds) {
return _tabsService.removeTab(tabId: tabIds);
}
@override
Map<String, TabState> build() {
final eventService = ref.watch(eventServiceProvider);
_db = ref.watch(tabDatabaseProvider);
final subscriptions = [
eventService.tabListEvents.listen(_onTabListChange),
eventService.tabContentEvents.listen(_onTabContentStateChange),
eventService.iconEvents.listen(_onIconChange),
eventService.thumbnailEvents.listen(_onThumbnailChange),
eventService.securityInfoEvents.listen(_onSecurityInfoStateChange),
eventService.historyEvents.listen(_onHistoryStateChange),
eventService.readerableEvents.listen(_onReaderableStateChange),
eventService.findResultsEvent.listen(_onFindResults),
eventService.tabContentEvents.listen(
(event) async {
await _lock.synchronized(() => _onTabContentStateChange(event));
},
),
eventService.iconEvents.listen(
(event) async {
await _lock.synchronized(() => _onIconChange(event));
},
),
eventService.thumbnailEvents.listen(
(event) async {
await _lock.synchronized(() => _onThumbnailChange(event));
},
),
eventService.securityInfoEvents.listen(
(event) async {
await _lock.synchronized(() => _onSecurityInfoStateChange(event));
},
),
eventService.historyEvents.listen(
(event) async {
await _lock.synchronized(() => _onHistoryStateChange(event));
},
),
eventService.readerableEvents.listen(
(event) async {
await _lock.synchronized(() => _onReaderableStateChange(event));
},
),
eventService.findResultsEvent.listen(
(event) async {
await _lock.synchronized(() => _onFindResultsChange(event));
},
),
];
ref.listenSelf(
ref.listen(
fireImmediately: true,
engineReadyStateProvider,
(previous, next) async {
if (!const DeepCollectionEquality()
.equals(previous?.keys.toSet(), next.keys.toSet())) {
final ids = next.keys.toList();
if (ids.isNotEmpty) {
await _db.tabLinkDao.syncTabLinks(retainTabIds: ids);
}
if (next) {
await GeckoTabService().syncEvents(
onTabContentStateChange: true,
onIconChange: true,
onThumbnailChange: true,
onSecurityInfoStateChange: true,
onHistoryStateChange: true,
onFindResults: true,
);
}
},
);
@@ -169,7 +169,7 @@ final selectedTabStateProvider = AutoDisposeProvider<TabState?>.internal(
);
typedef SelectedTabStateRef = AutoDisposeProviderRef<TabState?>;
String _$tabStatesHash() => r'f3ae09e49150955dcb29ec89f0f3fe63155a1d6e';
String _$tabStatesHash() => r'dcd293279a901eeaab7728979e39e5ca3bee8c66';
/// See also [TabStates].
@ProviderFor(TabStates)