one step closer
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||
import 'package:lensai/core/logger.dart';
|
||||
import 'package:lensai/features/geckoview/domain/providers/tab_session.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
@@ -10,8 +11,6 @@ part 'providers.g.dart';
|
||||
GeckoEventService eventService(EventServiceRef ref) {
|
||||
final service = GeckoEventService.setUp();
|
||||
|
||||
unawaited(GeckoTabService().syncEvents());
|
||||
|
||||
ref.onDispose(() {
|
||||
service.dispose();
|
||||
});
|
||||
@@ -19,6 +18,41 @@ GeckoEventService eventService(EventServiceRef ref) {
|
||||
return service;
|
||||
}
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class EngineReadyState extends _$EngineReadyState {
|
||||
@override
|
||||
bool build() {
|
||||
final eventService = ref.watch(eventServiceProvider);
|
||||
|
||||
final currentState =
|
||||
eventService.engineReadyStateEvents.valueOrNull ?? false;
|
||||
|
||||
if (!currentState) {
|
||||
unawaited(
|
||||
eventService.engineReadyStateEvents
|
||||
.firstWhere((value) => value == true)
|
||||
.timeout(
|
||||
const Duration(seconds: 5),
|
||||
onTimeout: () {
|
||||
logger.w('Waiting for engine ready state timed out');
|
||||
return true;
|
||||
},
|
||||
).whenComplete(() => state = true),
|
||||
);
|
||||
}
|
||||
|
||||
final sub = eventService.engineReadyStateEvents.listen((value) {
|
||||
state = value;
|
||||
});
|
||||
|
||||
ref.onDispose(() async {
|
||||
await sub.cancel();
|
||||
});
|
||||
|
||||
return currentState;
|
||||
}
|
||||
}
|
||||
|
||||
@Riverpod()
|
||||
TabSession selectedTabSessionNotifier(SelectedTabSessionNotifierRef ref) {
|
||||
return ref.watch(tabSessionProvider(null).notifier);
|
||||
|
||||
@@ -6,7 +6,7 @@ part of 'providers.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$eventServiceHash() => r'c36c6d895629002945c7b4eafba8389771e2eb2d';
|
||||
String _$eventServiceHash() => r'5aa357fdf0d217677a9a66ecb50417ac18929cad';
|
||||
|
||||
/// See also [eventService].
|
||||
@ProviderFor(eventService)
|
||||
@@ -37,5 +37,21 @@ final selectedTabSessionNotifierProvider =
|
||||
);
|
||||
|
||||
typedef SelectedTabSessionNotifierRef = AutoDisposeProviderRef<TabSession>;
|
||||
String _$engineReadyStateHash() => r'c682333e2e07cf0635aa7ae793a2088ca648c950';
|
||||
|
||||
/// See also [EngineReadyState].
|
||||
@ProviderFor(EngineReadyState)
|
||||
final engineReadyStateProvider =
|
||||
NotifierProvider<EngineReadyState, bool>.internal(
|
||||
EngineReadyState.new,
|
||||
name: r'engineReadyStateProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$engineReadyStateHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$EngineReadyState = Notifier<bool>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:drift/drift.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:lensai/features/geckoview/features/tabs/domain/providers/selected_container.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'tab.g.dart';
|
||||
@@ -9,6 +14,8 @@ part 'tab.g.dart';
|
||||
class TabRepository extends _$TabRepository {
|
||||
final _tabsService = GeckoTabService();
|
||||
|
||||
late TabDatabase _db;
|
||||
|
||||
Future<String> addTab({
|
||||
Uri? url,
|
||||
bool selectTab = true,
|
||||
@@ -48,5 +55,20 @@ class TabRepository extends _$TabRepository {
|
||||
}
|
||||
|
||||
@override
|
||||
void build() {}
|
||||
void build() {
|
||||
final eventSerivce = ref.watch(eventServiceProvider);
|
||||
|
||||
_db = ref.watch(tabDatabaseProvider);
|
||||
|
||||
final tabAddedSub = eventSerivce.tabAddedStream.listen(
|
||||
(tabId) async {
|
||||
final containerId = ref.read(selectedContainerProvider);
|
||||
await _db.tabDao.upsertTab(tabId, containerId: Value(containerId));
|
||||
},
|
||||
);
|
||||
|
||||
ref.onDispose(() {
|
||||
unawaited(tabAddedSub.cancel());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ part of 'tab.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$tabRepositoryHash() => r'0abf38ef42f29015d81ccd11eb5990b741ebc616';
|
||||
String _$tabRepositoryHash() => r'05040223b83cd5ff80afaf89407043a10e4777cb';
|
||||
|
||||
/// See also [TabRepository].
|
||||
@ProviderFor(TabRepository)
|
||||
|
||||
Reference in New Issue
Block a user