push
This commit is contained in:
@@ -3,6 +3,7 @@ import 'package:fast_equatable/fast_equatable.dart';
|
||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||
import 'package:lensai/data/models/web_page_info.dart';
|
||||
import 'package:lensai/domain/entities/equatable_image.dart';
|
||||
import 'package:lensai/extensions/nullable.dart';
|
||||
import 'package:lensai/features/geckoview/domain/entities/browser_icon.dart';
|
||||
import 'package:lensai/features/geckoview/domain/entities/find_result_state.dart';
|
||||
import 'package:lensai/features/geckoview/domain/entities/history_state.dart';
|
||||
@@ -24,13 +25,13 @@ class TabState extends WebPageInfo with FastEquatable {
|
||||
final EquatableImage? icon;
|
||||
|
||||
@override
|
||||
BrowserIcon? get favicon => (icon != null)
|
||||
? BrowserIcon(
|
||||
image: icon!,
|
||||
BrowserIcon? get favicon => icon.mapNotNull(
|
||||
(icon) => BrowserIcon(
|
||||
image: icon,
|
||||
dominantColor: null,
|
||||
source: IconSource.memory,
|
||||
)
|
||||
: null;
|
||||
),
|
||||
);
|
||||
|
||||
final EquatableImage? thumbnail;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
||||
|
||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||
import 'package:lensai/extensions/image.dart';
|
||||
import 'package:lensai/extensions/nullable.dart';
|
||||
import 'package:lensai/features/geckoview/domain/entities/find_result_state.dart';
|
||||
import 'package:lensai/features/geckoview/domain/entities/history_state.dart';
|
||||
import 'package:lensai/features/geckoview/domain/entities/readerable_state.dart';
|
||||
@@ -39,10 +40,10 @@ class TabStates extends _$TabStates {
|
||||
Future<void> _onIconChange(IconChangeEvent event) async {
|
||||
final IconChangeEvent(:tabId, :bytes) = event;
|
||||
|
||||
final image = (bytes != null)
|
||||
? (await tryDecodeImage(bytes)
|
||||
.then((image) async => image?.toEquatable()))
|
||||
: null;
|
||||
final image = await bytes.mapNotNull(
|
||||
(bytes) =>
|
||||
tryDecodeImage(bytes).then((image) async => image?.toEquatable()),
|
||||
);
|
||||
|
||||
final current = state[tabId] ?? TabState.$default(tabId);
|
||||
state = {...state}..[tabId] = current.copyWith.icon(image);
|
||||
@@ -51,10 +52,10 @@ class TabStates extends _$TabStates {
|
||||
Future<void> _onThumbnailChange(ThumbnailEvent event) async {
|
||||
final ThumbnailEvent(:tabId, :bytes) = event;
|
||||
|
||||
final image = (bytes != null)
|
||||
? (await tryDecodeImage(bytes)
|
||||
.then((image) async => image?.toEquatable()))
|
||||
: null;
|
||||
final image = await bytes.mapNotNull(
|
||||
(bytes) =>
|
||||
tryDecodeImage(bytes).then((image) async => image?.toEquatable()),
|
||||
);
|
||||
|
||||
final current = state[tabId] ?? TabState.$default(tabId);
|
||||
state = {...state}..[tabId] = current.copyWith.thumbnail(image);
|
||||
|
||||
@@ -173,7 +173,7 @@ final selectedTabStateProvider = AutoDisposeProvider<TabState?>.internal(
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef SelectedTabStateRef = AutoDisposeProviderRef<TabState?>;
|
||||
String _$tabStatesHash() => r'77f07f330dfac8dc8d28d18737203c7c122f3b74';
|
||||
String _$tabStatesHash() => r'296d85ec016b22236b5830fce6b01cda81fbc02d';
|
||||
|
||||
/// See also [TabStates].
|
||||
@ProviderFor(TabStates)
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'dart:ui';
|
||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||
import 'package:lensai/domain/entities/equatable_image.dart';
|
||||
import 'package:lensai/extensions/image.dart';
|
||||
import 'package:lensai/extensions/nullable.dart';
|
||||
import 'package:lensai/features/geckoview/domain/entities/web_extension_state.dart';
|
||||
import 'package:lensai/features/geckoview/domain/providers.dart';
|
||||
import 'package:lensai/features/geckoview/utils/image_helper.dart';
|
||||
@@ -33,12 +34,10 @@ class WebExtensionsState extends _$WebExtensionsState {
|
||||
title: data.title,
|
||||
enabled: data.enabled ?? current.enabled,
|
||||
badgeText: data.badgeText,
|
||||
badgeTextColor: (data.badgeTextColor != null)
|
||||
? Color(data.badgeTextColor!)
|
||||
: null,
|
||||
badgeBackgroundColor: (data.badgeBackgroundColor != null)
|
||||
? Color(data.badgeBackgroundColor!)
|
||||
: null,
|
||||
badgeTextColor:
|
||||
data.badgeTextColor.mapNotNull((color) => Color(color)),
|
||||
badgeBackgroundColor:
|
||||
data.badgeBackgroundColor.mapNotNull((color) => Color(color)),
|
||||
);
|
||||
} else {
|
||||
if (state.containsKey(extensionId)) {
|
||||
|
||||
@@ -7,7 +7,7 @@ part of 'web_extensions_state.dart';
|
||||
// **************************************************************************
|
||||
|
||||
String _$webExtensionsStateHash() =>
|
||||
r'bbaba8d27980046275858d2707bc66c6e349a537';
|
||||
r'4e6dc46e49ff353e1c3cc4616008685c0e21501d';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
||||
@@ -2,14 +2,15 @@ import 'dart:async';
|
||||
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||
import 'package:lensai/extensions/nullable.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/domain/providers/tab_list.dart';
|
||||
import 'package:lensai/features/geckoview/domain/providers/tab_state.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:lensai/features/geckoview/features/tabs/domain/repositories/container.dart';
|
||||
import 'package:lensai/utils/debouncer.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
@@ -19,31 +20,59 @@ part 'tab.g.dart';
|
||||
class TabRepository extends _$TabRepository {
|
||||
final _tabsService = GeckoTabService();
|
||||
|
||||
late TabDatabase _db;
|
||||
|
||||
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,
|
||||
}) async {
|
||||
final selectedContainer =
|
||||
await ref.read(selectedContainerProvider.notifier).fetchData();
|
||||
|
||||
return ref.read(tabDatabaseProvider).tabDao.upsertContainerTabTransactional(
|
||||
() {
|
||||
return _tabsService.addTab(
|
||||
url: url,
|
||||
selectTab: selectTab,
|
||||
startLoading: startLoading,
|
||||
parentId: parentId,
|
||||
flags: flags,
|
||||
contextId: selectedContainer?.metadata.contextualIdentity,
|
||||
source: source,
|
||||
private: private,
|
||||
historyMetadata: historyMetadata,
|
||||
additionalHeaders: additionalHeaders,
|
||||
);
|
||||
},
|
||||
containerId: Value(selectedContainer?.id),
|
||||
);
|
||||
}
|
||||
|
||||
Future<String> duplicateTab({
|
||||
required String? selectTabId,
|
||||
String? containerId,
|
||||
bool selectTab = true,
|
||||
}) async {
|
||||
final containerData = await containerId.mapNotNull(
|
||||
(containerId) => ref
|
||||
.read(containerRepositoryProvider.notifier)
|
||||
.getContainerData(containerId),
|
||||
);
|
||||
|
||||
return ref.read(tabDatabaseProvider).tabDao.upsertContainerTabTransactional(
|
||||
() {
|
||||
return _tabsService.duplicateTab(
|
||||
selectTabId: selectTabId,
|
||||
newContextId: containerData?.metadata.contextualIdentity,
|
||||
selectNewTab: selectTab,
|
||||
);
|
||||
},
|
||||
containerId: Value(containerData?.id),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -67,18 +96,19 @@ class TabRepository extends _$TabRepository {
|
||||
final tabStateDebouncer = Debouncer(const Duration(seconds: 3));
|
||||
Map<String, TabState>? debounceStartValue;
|
||||
|
||||
_db = ref.watch(tabDatabaseProvider);
|
||||
final db = ref.watch(tabDatabaseProvider);
|
||||
|
||||
final tabAddedSub = eventSerivce.tabAddedStream.listen(
|
||||
(tabId) async {
|
||||
final containerId = ref.read(selectedContainerProvider);
|
||||
await _db.tabDao.upsertTab(tabId, containerId: Value(containerId));
|
||||
await db.tabDao
|
||||
.upsertUnassignedTab(tabId, containerId: Value(containerId));
|
||||
},
|
||||
);
|
||||
|
||||
final tabContentSub =
|
||||
tabContentService.tabContentStream.listen((content) async {
|
||||
await _db.tabDao.updateTabContent(
|
||||
await db.tabDao.updateTabContent(
|
||||
content.tabId,
|
||||
isProbablyReaderable: content.isProbablyReaderable,
|
||||
extractedContentMarkdown: content.extractedContentMarkdown,
|
||||
@@ -92,7 +122,7 @@ class TabRepository extends _$TabRepository {
|
||||
selectedTabProvider,
|
||||
(previous, tabId) async {
|
||||
if (tabId != null) {
|
||||
await _db.tabDao.touchTab(tabId, timestamp: DateTime.now());
|
||||
await db.tabDao.touchTab(tabId, timestamp: DateTime.now());
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -104,7 +134,7 @@ class TabRepository extends _$TabRepository {
|
||||
final syncTabs = next.isNotEmpty || (previous?.isNotEmpty ?? false);
|
||||
|
||||
if (syncTabs) {
|
||||
await _db.tabDao.syncTabs(retainTabIds: next);
|
||||
await db.tabDao.syncTabs(retainTabIds: next);
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -120,7 +150,7 @@ class TabRepository extends _$TabRepository {
|
||||
}
|
||||
|
||||
tabStateDebouncer.eventOccured(() async {
|
||||
await _db.tabDao.updateTabs(debounceStartValue, next);
|
||||
await db.tabDao.updateTabs(debounceStartValue, next);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@ part of 'tab.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$tabRepositoryHash() => r'b9caf89fe372b79f37c4a3ebff800ccc2d9289d2';
|
||||
String _$tabRepositoryHash() => r'85af29fa18ea5a4a67fbffbb9478c801dafddac2';
|
||||
|
||||
/// See also [TabRepository].
|
||||
@ProviderFor(TabRepository)
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||
import 'package:lensai/features/user/data/models/general_settings.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'delete_browser_data.g.dart';
|
||||
|
||||
@Riverpod()
|
||||
class DeleteBrowserDataService extends _$DeleteBrowserDataService {
|
||||
final _service = GeckoDeleteBrowserDataService();
|
||||
|
||||
Future<void> deleteData(Set<DeleteBrowsingDataType>? types) async {
|
||||
if (types != null) {
|
||||
for (final type in types) {
|
||||
switch (type) {
|
||||
case DeleteBrowsingDataType.tabs:
|
||||
await _service.deleteTabs();
|
||||
case DeleteBrowsingDataType.history:
|
||||
await _service.deleteBrowsingHistory();
|
||||
case DeleteBrowsingDataType.cookies:
|
||||
await _service.deleteCookiesAndSiteData();
|
||||
case DeleteBrowsingDataType.cache:
|
||||
await _service.deleteCachedFiles();
|
||||
case DeleteBrowsingDataType.permissions:
|
||||
await _service.deleteSitePermissions();
|
||||
case DeleteBrowsingDataType.downloads:
|
||||
await _service.deleteDownloads();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void build() {}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'delete_browser_data.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$deleteBrowserDataServiceHash() =>
|
||||
r'f5fdbac17c14693fceac913591750ca9285b496c';
|
||||
|
||||
/// See also [DeleteBrowserDataService].
|
||||
@ProviderFor(DeleteBrowserDataService)
|
||||
final deleteBrowserDataServiceProvider =
|
||||
AutoDisposeNotifierProvider<DeleteBrowserDataService, void>.internal(
|
||||
DeleteBrowserDataService.new,
|
||||
name: r'deleteBrowserDataServiceProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$deleteBrowserDataServiceHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$DeleteBrowserDataService = AutoDisposeNotifier<void>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
@@ -1,10 +1,90 @@
|
||||
import 'package:flutter/material.dart' show ThemeMode;
|
||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||
import 'package:riverpod/riverpod.dart';
|
||||
import 'package:lensai/features/user/domain/repositories/engine_settings.dart';
|
||||
import 'package:lensai/features/user/domain/repositories/general_settings.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'engine_settings.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
GeckoEngineSettingsService engineSettingsService(Ref ref) {
|
||||
return GeckoEngineSettingsService();
|
||||
class EngineSettingsReplicationService
|
||||
extends _$EngineSettingsReplicationService {
|
||||
final _service = GeckoEngineSettingsService();
|
||||
|
||||
@override
|
||||
void build() {
|
||||
var initialSettingsSent = false;
|
||||
|
||||
ref.listen(
|
||||
generalSettingsRepositoryProvider
|
||||
.select((settings) => settings.themeMode),
|
||||
(previous, next) async {
|
||||
final theme = switch (next) {
|
||||
ThemeMode.system => ColorScheme.system,
|
||||
ThemeMode.light => ColorScheme.light,
|
||||
ThemeMode.dark => ColorScheme.dark,
|
||||
};
|
||||
|
||||
await _service.preferredColorScheme(theme);
|
||||
},
|
||||
);
|
||||
|
||||
ref.listen(
|
||||
engineSettingsRepositoryProvider,
|
||||
(previous, next) async {
|
||||
if (initialSettingsSent && previous != null) {
|
||||
if (previous.javascriptEnabled != next.javascriptEnabled) {
|
||||
await _service.javascriptEnabled(next.javascriptEnabled);
|
||||
}
|
||||
if (previous.trackingProtectionPolicy !=
|
||||
next.trackingProtectionPolicy) {
|
||||
await _service
|
||||
.trackingProtectionPolicy(next.trackingProtectionPolicy);
|
||||
}
|
||||
if (previous.httpsOnlyMode != next.httpsOnlyMode) {
|
||||
await _service.httpsOnlyMode(next.httpsOnlyMode);
|
||||
}
|
||||
if (previous.globalPrivacyControlEnabled !=
|
||||
next.globalPrivacyControlEnabled) {
|
||||
await _service
|
||||
.globalPrivacyControlEnabled(next.globalPrivacyControlEnabled);
|
||||
}
|
||||
if (previous.preferredColorScheme != next.preferredColorScheme) {
|
||||
await _service.preferredColorScheme(next.preferredColorScheme);
|
||||
}
|
||||
if (previous.cookieBannerHandlingMode !=
|
||||
next.cookieBannerHandlingMode) {
|
||||
await _service
|
||||
.cookieBannerHandlingMode(next.cookieBannerHandlingMode);
|
||||
}
|
||||
if (previous.cookieBannerHandlingModePrivateBrowsing !=
|
||||
next.cookieBannerHandlingModePrivateBrowsing) {
|
||||
await _service.cookieBannerHandlingModePrivateBrowsing(
|
||||
next.cookieBannerHandlingModePrivateBrowsing,
|
||||
);
|
||||
}
|
||||
if (previous.cookieBannerHandlingGlobalRules !=
|
||||
next.cookieBannerHandlingGlobalRules) {
|
||||
await _service.cookieBannerHandlingGlobalRules(
|
||||
next.cookieBannerHandlingGlobalRules,
|
||||
);
|
||||
}
|
||||
if (previous.cookieBannerHandlingGlobalRulesSubFrames !=
|
||||
next.cookieBannerHandlingGlobalRulesSubFrames) {
|
||||
await _service.cookieBannerHandlingGlobalRulesSubFrames(
|
||||
next.cookieBannerHandlingGlobalRulesSubFrames,
|
||||
);
|
||||
}
|
||||
if (previous.webContentIsolationStrategy !=
|
||||
next.webContentIsolationStrategy) {
|
||||
await _service
|
||||
.webContentIsolationStrategy(next.webContentIsolationStrategy);
|
||||
}
|
||||
} else {
|
||||
await _service.setDefaultSettings(next);
|
||||
initialSettingsSent = true;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+10
-12
@@ -6,24 +6,22 @@ part of 'engine_settings.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$engineSettingsServiceHash() =>
|
||||
r'051d7b6f65eb5dcb07405b4af1bdadd8dd74fdc0';
|
||||
String _$engineSettingsReplicationServiceHash() =>
|
||||
r'0729260eeb34b58470622ac9a3a6abb257730a61';
|
||||
|
||||
/// See also [engineSettingsService].
|
||||
@ProviderFor(engineSettingsService)
|
||||
final engineSettingsServiceProvider =
|
||||
Provider<GeckoEngineSettingsService>.internal(
|
||||
engineSettingsService,
|
||||
name: r'engineSettingsServiceProvider',
|
||||
/// See also [EngineSettingsReplicationService].
|
||||
@ProviderFor(EngineSettingsReplicationService)
|
||||
final engineSettingsReplicationServiceProvider =
|
||||
NotifierProvider<EngineSettingsReplicationService, void>.internal(
|
||||
EngineSettingsReplicationService.new,
|
||||
name: r'engineSettingsReplicationServiceProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$engineSettingsServiceHash,
|
||||
: _$engineSettingsReplicationServiceHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef EngineSettingsServiceRef = ProviderRef<GeckoEngineSettingsService>;
|
||||
typedef _$EngineSettingsReplicationService = Notifier<void>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
|
||||
@@ -1,103 +1,104 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:lensai/data/models/equatable_iterable.dart';
|
||||
import 'package:lensai/features/geckoview/domain/entities/tab_state.dart';
|
||||
import 'package:lensai/features/geckoview/domain/providers/tab_state.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/domain/providers.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/domain/repositories/tab.dart';
|
||||
// import 'package:collection/collection.dart';
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
// import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
// import 'package:lensai/data/models/equatable_iterable.dart';
|
||||
// import 'package:lensai/extensions/nullable.dart';
|
||||
// import 'package:lensai/features/geckoview/domain/entities/tab_state.dart';
|
||||
// import 'package:lensai/features/geckoview/domain/providers/tab_state.dart';
|
||||
// import 'package:lensai/features/geckoview/features/tabs/domain/providers.dart';
|
||||
// import 'package:lensai/features/geckoview/features/tabs/domain/repositories/tab.dart';
|
||||
|
||||
class TabActionDialog extends HookConsumerWidget {
|
||||
final TabState initialTab;
|
||||
// class TabActionDialog extends HookConsumerWidget {
|
||||
// final TabState initialTab;
|
||||
|
||||
final void Function()? onDismiss;
|
||||
// final void Function()? onDismiss;
|
||||
|
||||
const TabActionDialog({
|
||||
required this.initialTab,
|
||||
this.onDismiss,
|
||||
super.key,
|
||||
});
|
||||
// const TabActionDialog({
|
||||
// required this.initialTab,
|
||||
// this.onDismiss,
|
||||
// super.key,
|
||||
// });
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final tabState = ref.watch(tabStateProvider(initialTab.id)) ?? initialTab;
|
||||
final tabContainerId = ref.watch(
|
||||
tabContainerIdProvider(initialTab.id)
|
||||
.select((value) => value.valueOrNull),
|
||||
);
|
||||
// @override
|
||||
// Widget build(BuildContext context, WidgetRef ref) {
|
||||
// final tabState = ref.watch(tabStateProvider(initialTab.id)) ?? initialTab;
|
||||
// final tabContainerId = ref.watch(
|
||||
// tabContainerIdProvider(initialTab.id)
|
||||
// .select((value) => value.valueOrNull),
|
||||
// );
|
||||
|
||||
final containers = ref
|
||||
.watch(
|
||||
containersWithCountProvider.select(
|
||||
(value) => EquatableCollection(value.valueOrNull, immutable: true),
|
||||
),
|
||||
)
|
||||
.collection;
|
||||
// final containers = ref
|
||||
// .watch(
|
||||
// containersWithCountProvider.select(
|
||||
// (value) => EquatableCollection(value.valueOrNull, immutable: true),
|
||||
// ),
|
||||
// )
|
||||
// .collection;
|
||||
|
||||
final selectedContainer = containers
|
||||
?.firstWhereOrNull((container) => container.id == tabContainerId);
|
||||
// final selectedContainer = containers
|
||||
// ?.firstWhereOrNull((container) => container.id == tabContainerId);
|
||||
|
||||
final expansionController = useExpansionTileController();
|
||||
// final expansionController = useExpansionTileController();
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
ModalBarrier(
|
||||
color: Theme.of(context).dialogTheme.barrierColor ?? Colors.black54,
|
||||
onDismiss: onDismiss,
|
||||
),
|
||||
SimpleDialog(
|
||||
titlePadding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 0.0),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
insetPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 20.0,
|
||||
vertical: 24.0,
|
||||
),
|
||||
title: ListTile(
|
||||
leading: RawImage(
|
||||
image: tabState.icon?.value,
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(tabState.title),
|
||||
subtitle: Text(tabState.url.authority),
|
||||
),
|
||||
children: [
|
||||
SizedBox(
|
||||
//We need this to stretch the dialog, then padding from dialog is applied
|
||||
width: double.maxFinite,
|
||||
child: ExpansionTile(
|
||||
controller: expansionController,
|
||||
leading: (selectedContainer != null)
|
||||
? CircleAvatar(backgroundColor: selectedContainer.color)
|
||||
: null,
|
||||
title: (selectedContainer != null)
|
||||
? Text(selectedContainer.name ?? 'New Container')
|
||||
: const Text('Assign a Container'),
|
||||
children: containers
|
||||
?.where((container) => container.id != tabContainerId)
|
||||
.map(
|
||||
(container) => ListTile(
|
||||
leading:
|
||||
CircleAvatar(backgroundColor: container.color),
|
||||
title: Text(container.name ?? 'New Container'),
|
||||
onTap: () async {
|
||||
await ref
|
||||
.read(tabDataRepositoryProvider.notifier)
|
||||
.assignContainer(initialTab.id, container.id);
|
||||
// return Stack(
|
||||
// children: [
|
||||
// ModalBarrier(
|
||||
// color: Theme.of(context).dialogTheme.barrierColor ?? Colors.black54,
|
||||
// onDismiss: onDismiss,
|
||||
// ),
|
||||
// SimpleDialog(
|
||||
// titlePadding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 0.0),
|
||||
// contentPadding: EdgeInsets.zero,
|
||||
// insetPadding: const EdgeInsets.symmetric(
|
||||
// horizontal: 20.0,
|
||||
// vertical: 24.0,
|
||||
// ),
|
||||
// title: ListTile(
|
||||
// leading: RawImage(
|
||||
// image: tabState.icon?.value,
|
||||
// width: 24,
|
||||
// height: 24,
|
||||
// ),
|
||||
// contentPadding: EdgeInsets.zero,
|
||||
// title: Text(tabState.title),
|
||||
// subtitle: Text(tabState.url.authority),
|
||||
// ),
|
||||
// children: [
|
||||
// SizedBox(
|
||||
// //We need this to stretch the dialog, then padding from dialog is applied
|
||||
// width: double.maxFinite,
|
||||
// child: ExpansionTile(
|
||||
// controller: expansionController,
|
||||
// leading: selectedContainer?.color.mapNotNull(
|
||||
// (color) => CircleAvatar(backgroundColor: color),
|
||||
// ),
|
||||
// title: (selectedContainer != null)
|
||||
// ? Text(selectedContainer.name ?? 'New Container')
|
||||
// : const Text('Assign a Container'),
|
||||
// children: containers
|
||||
// ?.where((container) => container.id != tabContainerId)
|
||||
// .map(
|
||||
// (container) => ListTile(
|
||||
// leading:
|
||||
// CircleAvatar(backgroundColor: container.color),
|
||||
// title: Text(container.name ?? 'New Container'),
|
||||
// onTap: () async {
|
||||
// await ref
|
||||
// .read(tabDataRepositoryProvider.notifier)
|
||||
// .assignContainer(initialTab.id, container.id);
|
||||
|
||||
expansionController.collapse();
|
||||
},
|
||||
),
|
||||
)
|
||||
.toList() ??
|
||||
[],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
// expansionController.collapse();
|
||||
// },
|
||||
// ),
|
||||
// )
|
||||
// .toList() ??
|
||||
// [],
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
+2
-4
@@ -22,7 +22,7 @@ import 'package:lensai/features/geckoview/features/browser/domain/entities/sheet
|
||||
import 'package:lensai/features/kagi/data/entities/modes.dart';
|
||||
import 'package:lensai/features/kagi/utils/url_builder.dart' as uri_builder;
|
||||
import 'package:lensai/features/share_intent/domain/entities/shared_content.dart';
|
||||
import 'package:lensai/features/user/domain/repositories/settings.dart';
|
||||
import 'package:lensai/features/user/domain/providers.dart';
|
||||
import 'package:lensai/presentation/widgets/failure_widget.dart';
|
||||
import 'package:lensai/presentation/widgets/website_title_tile.dart';
|
||||
import 'package:lensai/utils/ui_helper.dart' as ui_helper;
|
||||
@@ -40,9 +40,7 @@ class WebPageDialog extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final incognitoEnabled = ref.watch(
|
||||
settingsRepositoryProvider.select((value) => value.incognitoMode),
|
||||
);
|
||||
final incognitoEnabled = ref.watch(incognitoModeEnabledProvider);
|
||||
|
||||
final availableBangsAsync = ref.watch(
|
||||
bangDataListProvider(
|
||||
|
||||
@@ -17,7 +17,6 @@ import 'package:lensai/features/geckoview/domain/providers/web_extensions_state.
|
||||
import 'package:lensai/features/geckoview/domain/repositories/tab.dart';
|
||||
import 'package:lensai/features/geckoview/features/browser/domain/entities/sheet.dart';
|
||||
import 'package:lensai/features/geckoview/features/browser/domain/services/create_tab.dart';
|
||||
import 'package:lensai/features/geckoview/features/browser/domain/services/engine_settings.dart';
|
||||
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/app_bar_title.dart';
|
||||
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/browser_view.dart';
|
||||
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/draggable_scrollable_header.dart';
|
||||
@@ -31,7 +30,6 @@ import 'package:lensai/features/geckoview/features/readerview/presentation/widge
|
||||
import 'package:lensai/features/geckoview/features/readerview/presentation/widgets/reader_button.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/features/chat/presentation/widgets/tab_qa_chat.dart';
|
||||
import 'package:lensai/features/kagi/data/entities/modes.dart';
|
||||
import 'package:lensai/features/user/domain/repositories/settings.dart';
|
||||
import 'package:lensai/presentation/hooks/draggable_scrollable_controller.dart';
|
||||
import 'package:lensai/presentation/hooks/menu_controller.dart';
|
||||
import 'package:lensai/presentation/hooks/overlay_portal_controller.dart';
|
||||
@@ -77,15 +75,8 @@ class BrowserScreen extends HookConsumerWidget {
|
||||
},
|
||||
);
|
||||
|
||||
ref.listen(
|
||||
settingsRepositoryProvider.select((value) => value.enableJavascript),
|
||||
(previous, next) async {
|
||||
await ref.read(engineSettingsServiceProvider).javaScriptEnabled(next);
|
||||
},
|
||||
);
|
||||
|
||||
// ref.listen(
|
||||
// settingsRepositoryProvider
|
||||
// generalSettingsRepositoryProvider
|
||||
// .select((value) => value.valueOrNull?.kagiSession),
|
||||
// (previous, next) async {
|
||||
// if (next != null && next.isNotEmpty) {
|
||||
@@ -598,7 +589,7 @@ class BrowserScreen extends HookConsumerWidget {
|
||||
MenuItemButton(
|
||||
onPressed: () async {
|
||||
await context
|
||||
.push(BrowserHardeningRoute().location);
|
||||
.push(WebEngineHardeningRoute().location);
|
||||
},
|
||||
leadingIcon: const Icon(Icons.info),
|
||||
child: const Text('Pref'),
|
||||
@@ -648,7 +639,8 @@ class BrowserScreen extends HookConsumerWidget {
|
||||
: null;
|
||||
|
||||
final tabCount = ref.read(
|
||||
tabListProvider.select((tabs) => tabs.length));
|
||||
tabListProvider.select((tabs) => tabs.length),
|
||||
);
|
||||
|
||||
//Don't do anything if a child route is active
|
||||
if (GoRouterState.of(context).topRoute?.path !=
|
||||
|
||||
@@ -8,13 +8,21 @@ import 'package:lensai/features/geckoview/domain/providers.dart';
|
||||
import 'package:lensai/features/geckoview/domain/providers/tab_state.dart';
|
||||
import 'package:lensai/features/geckoview/domain/providers/web_extensions_state.dart';
|
||||
import 'package:lensai/features/geckoview/domain/repositories/tab.dart';
|
||||
import 'package:lensai/features/geckoview/features/browser/domain/services/delete_browser_data.dart';
|
||||
import 'package:lensai/features/geckoview/features/browser/domain/services/engine_settings.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/features/vector_store/domain/repositories/document.dart';
|
||||
import 'package:lensai/features/user/domain/repositories/cache.dart';
|
||||
import 'package:lensai/features/user/domain/repositories/general_settings.dart';
|
||||
import 'package:lensai/features/user/domain/services/local_authentication.dart';
|
||||
|
||||
class BrowserView extends StatefulHookConsumerWidget {
|
||||
final Duration screenshotPeriod;
|
||||
final FutureOr<void> Function()? postInitializationStep;
|
||||
|
||||
const BrowserView({this.screenshotPeriod = const Duration(seconds: 10)});
|
||||
const BrowserView({
|
||||
this.screenshotPeriod = const Duration(seconds: 10),
|
||||
this.postInitializationStep,
|
||||
});
|
||||
|
||||
@override
|
||||
ConsumerState<ConsumerStatefulWidget> createState() => _BrowserViewState();
|
||||
@@ -63,6 +71,15 @@ class _BrowserViewState extends ConsumerState<BrowserView>
|
||||
|
||||
return GeckoView(
|
||||
preInitializationStep: () async {
|
||||
await ref
|
||||
.read(generalSettingsRepositoryProvider.notifier)
|
||||
.fetch()
|
||||
.then((settings) {
|
||||
ref
|
||||
.read(deleteBrowserDataServiceProvider.notifier)
|
||||
.deleteData(settings.deleteBrowsingDataOnQuit);
|
||||
});
|
||||
|
||||
await ref
|
||||
.read(eventServiceProvider)
|
||||
.viewReadyStateEvents
|
||||
@@ -77,6 +94,9 @@ class _BrowserViewState extends ConsumerState<BrowserView>
|
||||
},
|
||||
);
|
||||
},
|
||||
postInitializationStep: () async {
|
||||
await widget.postInitializationStep?.call();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -109,6 +129,11 @@ class _BrowserViewState extends ConsumerState<BrowserView>
|
||||
cacheRepositoryProvider,
|
||||
(previous, next) {},
|
||||
);
|
||||
|
||||
ref.listenManual(
|
||||
engineSettingsReplicationServiceProvider,
|
||||
(previous, next) {},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -124,6 +149,10 @@ class _BrowserViewState extends ConsumerState<BrowserView>
|
||||
_periodicScreenshotUpdate?.cancel();
|
||||
_timerPaused = true;
|
||||
}
|
||||
|
||||
ref
|
||||
.read(localAuthenticationServiceProvider.notifier)
|
||||
.evictCacheOnBackground();
|
||||
case AppLifecycleState.resumed:
|
||||
if (_timerPaused) {
|
||||
_periodicScreenshotUpdate =
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ class LandingAction extends HookConsumerWidget {
|
||||
final sessionTokenAvailable = false;
|
||||
|
||||
// ref.watch(
|
||||
// settingsRepositoryProvider.select(
|
||||
// generalSettingsRepositoryProvider.select(
|
||||
// (value) => value.valueOrNull?.kagiSession?.isNotEmpty ?? false,
|
||||
// ),
|
||||
// );
|
||||
|
||||
+3
-3
@@ -2,6 +2,7 @@ import 'package:expandable_page_view/expandable_page_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:lensai/extensions/nullable.dart';
|
||||
import 'package:lensai/features/geckoview/features/browser/domain/entities/sheet.dart';
|
||||
import 'package:lensai/features/kagi/data/entities/modes.dart';
|
||||
import 'package:lensai/features/kagi/presentation/tabs/summarize_tab.dart';
|
||||
@@ -24,9 +25,8 @@ class CreateTabSheetWidget extends HookConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final sharedContent = useMemoized(
|
||||
() => (parameter.content != null)
|
||||
? SharedContent.parse(parameter.content!)
|
||||
: null,
|
||||
() => parameter.content
|
||||
.mapNotNull((content) => SharedContent.parse(content)),
|
||||
[parameter],
|
||||
);
|
||||
|
||||
|
||||
+11
-11
@@ -56,15 +56,15 @@ class _TabDraggable extends HookConsumerWidget {
|
||||
onClose();
|
||||
}
|
||||
},
|
||||
onDoubleTap: () {
|
||||
ref.read(overlayDialogControllerProvider.notifier).show(
|
||||
TabActionDialog(
|
||||
initialTab: tab,
|
||||
onDismiss:
|
||||
ref.read(overlayDialogControllerProvider.notifier).dismiss,
|
||||
),
|
||||
);
|
||||
},
|
||||
// onDoubleTap: () {
|
||||
// ref.read(overlayDialogControllerProvider.notifier).show(
|
||||
// TabActionDialog(
|
||||
// initialTab: tab,
|
||||
// onDismiss:
|
||||
// ref.read(overlayDialogControllerProvider.notifier).dismiss,
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
onDelete: () async {
|
||||
await ref.read(tabRepositoryProvider.notifier).closeTab(tab.id);
|
||||
},
|
||||
@@ -178,8 +178,8 @@ class _Tab extends HookConsumerWidget {
|
||||
|
||||
return ContainerChips(
|
||||
selectedContainer: selectedContainer,
|
||||
onSelected: (container) {
|
||||
ref
|
||||
onSelected: (container) async {
|
||||
await ref
|
||||
.read(selectedContainerProvider.notifier)
|
||||
.setContainerId(container.id);
|
||||
},
|
||||
|
||||
@@ -40,7 +40,7 @@ class PreferenceSetting with FastEquatable {
|
||||
|
||||
bool get isActive => value == actualValue;
|
||||
|
||||
final String title;
|
||||
final String? title;
|
||||
final String? description;
|
||||
|
||||
final bool requireUserOptIn;
|
||||
|
||||
+5
-5
@@ -73,7 +73,7 @@ extension $PreferenceSettingGroupCopyWith on PreferenceSettingGroup {
|
||||
abstract class _$PreferenceSettingCWProxy {
|
||||
PreferenceSetting value(Object value);
|
||||
|
||||
PreferenceSetting title(String title);
|
||||
PreferenceSetting title(String? title);
|
||||
|
||||
PreferenceSetting description(String? description);
|
||||
|
||||
@@ -91,7 +91,7 @@ abstract class _$PreferenceSettingCWProxy {
|
||||
/// ````
|
||||
PreferenceSetting call({
|
||||
Object value,
|
||||
String title,
|
||||
String? title,
|
||||
String? description,
|
||||
Object? actualValue,
|
||||
bool requireUserOptIn,
|
||||
@@ -109,7 +109,7 @@ class _$PreferenceSettingCWProxyImpl implements _$PreferenceSettingCWProxy {
|
||||
PreferenceSetting value(Object value) => this(value: value);
|
||||
|
||||
@override
|
||||
PreferenceSetting title(String title) => this(title: title);
|
||||
PreferenceSetting title(String? title) => this(title: title);
|
||||
|
||||
@override
|
||||
PreferenceSetting description(String? description) =>
|
||||
@@ -151,7 +151,7 @@ class _$PreferenceSettingCWProxyImpl implements _$PreferenceSettingCWProxy {
|
||||
title: title == const $CopyWithPlaceholder()
|
||||
? _value.title
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: title as String,
|
||||
: title as String?,
|
||||
description: description == const $CopyWithPlaceholder()
|
||||
? _value.description
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
@@ -186,7 +186,7 @@ extension $PreferenceSettingCopyWith on PreferenceSetting {
|
||||
PreferenceSetting _$PreferenceSettingFromJson(Map<String, dynamic> json) =>
|
||||
PreferenceSetting(
|
||||
value: json['value'] as Object,
|
||||
title: json['title'] as String,
|
||||
title: json['title'] as String?,
|
||||
description: json['description'] as String?,
|
||||
requireUserOptIn: json['requireUserOptIn'] as bool? ?? false,
|
||||
shouldBeDefault: json['shouldBeDefault'] as bool? ?? false,
|
||||
|
||||
+49
-41
@@ -5,6 +5,7 @@ import 'package:collection/collection.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||
import 'package:lensai/features/geckoview/features/preferences/data/models/preference_setting.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/utils/setting_groups_serializer.dart';
|
||||
import 'package:riverpod/riverpod.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
@@ -12,40 +13,29 @@ import 'package:rxdart/rxdart.dart';
|
||||
part 'preference_settings.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
Future<Map<String, PreferenceSettingGroup>> _preferenceSettingGroups(
|
||||
Ref ref,
|
||||
) async {
|
||||
final content = await rootBundle
|
||||
Future<Map<String, dynamic>> _preferenceSettingContent(Ref ref) async {
|
||||
return await rootBundle
|
||||
.loadString('assets/preferences/settings.json')
|
||||
.then(jsonDecode) as Map<String, dynamic>;
|
||||
}
|
||||
|
||||
final userContent = content['user'] as Map<String, dynamic>;
|
||||
|
||||
return userContent.map(
|
||||
(key, value) => MapEntry(
|
||||
key,
|
||||
PreferenceSettingGroup(
|
||||
// ignore: avoid_dynamic_calls
|
||||
description: value['description'] as String?,
|
||||
// ignore: avoid_dynamic_calls
|
||||
settings: (value['preferences'] as Map<String, dynamic>).map(
|
||||
(key, value) => MapEntry(
|
||||
key,
|
||||
PreferenceSetting.fromJson(value as Map<String, dynamic>),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@Riverpod(keepAlive: true)
|
||||
Future<Map<String, PreferenceSettingGroup>> _preferenceSettingGroups(
|
||||
Ref ref,
|
||||
PreferencePartition partition,
|
||||
) async {
|
||||
final content = await ref.read(_preferenceSettingContentProvider.future);
|
||||
return deserializePreferenceSettingGroups(partition, content);
|
||||
}
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
Future<PreferenceSettingGroup> _preferenceSettingGroup(
|
||||
Ref ref,
|
||||
PreferencePartition partition,
|
||||
String groupName,
|
||||
) {
|
||||
return ref.watch(
|
||||
_preferenceSettingGroupsProvider.selectAsync(
|
||||
_preferenceSettingGroupsProvider(partition).selectAsync(
|
||||
(groups) =>
|
||||
groups[groupName] ?? (throw Exception('Unknown setting group')),
|
||||
),
|
||||
@@ -90,13 +80,16 @@ class _PreferenceRepository extends _$PreferenceRepository {
|
||||
}
|
||||
|
||||
@Riverpod()
|
||||
class PreferenceSettingsGeneralRepository
|
||||
extends _$PreferenceSettingsGeneralRepository {
|
||||
late Map<String, PreferenceSettingGroup> _statelessGroups;
|
||||
class UnifiedPreferenceSettingsRepository
|
||||
extends _$UnifiedPreferenceSettingsRepository {
|
||||
Map<String, PreferenceSettingGroup>? _statelessGroups;
|
||||
|
||||
Future<void> apply() async {
|
||||
_statelessGroups =
|
||||
await ref.read(_preferenceSettingGroupsProvider(partition).future);
|
||||
|
||||
final prefs = {
|
||||
for (final group in _statelessGroups.values)
|
||||
for (final group in _statelessGroups!.values)
|
||||
...Map.fromEntries(
|
||||
group.settings.entries
|
||||
.where((e) => !e.value.requireUserOptIn)
|
||||
@@ -108,7 +101,10 @@ class PreferenceSettingsGeneralRepository
|
||||
}
|
||||
|
||||
Future<void> reset() async {
|
||||
final prefs = _statelessGroups.values
|
||||
_statelessGroups =
|
||||
await ref.read(_preferenceSettingGroupsProvider(partition).future);
|
||||
|
||||
final prefs = _statelessGroups!.values
|
||||
.map((group) => group.settings.keys)
|
||||
.flattened
|
||||
.toList();
|
||||
@@ -117,13 +113,16 @@ class PreferenceSettingsGeneralRepository
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<Map<String, PreferenceSettingGroup>> build() async* {
|
||||
_statelessGroups = await ref.watch(_preferenceSettingGroupsProvider.future);
|
||||
Stream<Map<String, PreferenceSettingGroup>> build(
|
||||
PreferencePartition partition,
|
||||
) async* {
|
||||
_statelessGroups =
|
||||
await ref.watch(_preferenceSettingGroupsProvider(partition).future);
|
||||
|
||||
final prefStream = ref.watch(_preferenceRepositoryProvider);
|
||||
|
||||
yield* prefStream.map(
|
||||
(prefs) => _statelessGroups.map(
|
||||
(prefs) => _statelessGroups!.map(
|
||||
(groupName, group) => MapEntry(
|
||||
groupName,
|
||||
group.copyWith.settings(
|
||||
@@ -143,18 +142,21 @@ class PreferenceSettingsGeneralRepository
|
||||
@Riverpod()
|
||||
class PreferenceSettingsGroupRepository
|
||||
extends _$PreferenceSettingsGroupRepository {
|
||||
late PreferenceSettingGroup _statelessSettingGroup;
|
||||
PreferenceSettingGroup? _statelessSettingGroup;
|
||||
|
||||
Future<void> apply({List<String>? filter}) async {
|
||||
_statelessSettingGroup ??= await ref
|
||||
.read(_preferenceSettingGroupProvider(partition, groupName).future);
|
||||
|
||||
final prefs = Map.fromEntries(
|
||||
filter?.map(
|
||||
(e) => MapEntry(
|
||||
e,
|
||||
_statelessSettingGroup.settings[e]?.value ??
|
||||
_statelessSettingGroup!.settings[e]?.value ??
|
||||
(throw Exception('Preference not part of group')),
|
||||
),
|
||||
) ??
|
||||
_statelessSettingGroup.settings.entries
|
||||
_statelessSettingGroup!.settings.entries
|
||||
.where((e) => !e.value.requireUserOptIn)
|
||||
.map((e) => MapEntry(e.key, e.value.value)),
|
||||
);
|
||||
@@ -163,28 +165,34 @@ class PreferenceSettingsGroupRepository
|
||||
}
|
||||
|
||||
Future<void> reset({List<String>? filter}) async {
|
||||
_statelessSettingGroup ??= await ref
|
||||
.read(_preferenceSettingGroupProvider(partition, groupName).future);
|
||||
|
||||
if (filter != null &&
|
||||
filter.any(
|
||||
(value) => !_statelessSettingGroup.settings.containsKey(value),
|
||||
(value) => !_statelessSettingGroup!.settings.containsKey(value),
|
||||
)) {
|
||||
throw Exception('Preference not part of group');
|
||||
}
|
||||
|
||||
await ref
|
||||
.read(_preferenceRepositoryProvider.notifier)
|
||||
.resetPrefs(filter ?? _statelessSettingGroup.settings.keys.toList());
|
||||
.resetPrefs(filter ?? _statelessSettingGroup!.settings.keys.toList());
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<PreferenceSettingGroup> build(String groupName) async* {
|
||||
_statelessSettingGroup =
|
||||
await ref.watch(_preferenceSettingGroupProvider(groupName).future);
|
||||
Stream<PreferenceSettingGroup> build(
|
||||
PreferencePartition partition,
|
||||
String groupName,
|
||||
) async* {
|
||||
_statelessSettingGroup = await ref
|
||||
.watch(_preferenceSettingGroupProvider(partition, groupName).future);
|
||||
|
||||
final prefStream = ref.watch(_preferenceRepositoryProvider);
|
||||
|
||||
yield* prefStream.map(
|
||||
(prefs) => _statelessSettingGroup.copyWith.settings(
|
||||
_statelessSettingGroup.settings.map(
|
||||
(prefs) => _statelessSettingGroup!.copyWith.settings(
|
||||
_statelessSettingGroup!.settings.map(
|
||||
(key, value) => MapEntry(key, value.copyWith.actualValue(prefs[key])),
|
||||
),
|
||||
),
|
||||
|
||||
+350
-33
@@ -6,28 +6,27 @@ part of 'preference_settings.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$preferenceSettingGroupsHash() =>
|
||||
r'fa9286248cc71c88c888a518441721dc99b7e0c2';
|
||||
String _$preferenceSettingContentHash() =>
|
||||
r'a8a61fdf8800cb7adb365c100409ddce09c574b1';
|
||||
|
||||
/// See also [_preferenceSettingGroups].
|
||||
@ProviderFor(_preferenceSettingGroups)
|
||||
final _preferenceSettingGroupsProvider =
|
||||
FutureProvider<Map<String, PreferenceSettingGroup>>.internal(
|
||||
_preferenceSettingGroups,
|
||||
name: r'_preferenceSettingGroupsProvider',
|
||||
/// See also [_preferenceSettingContent].
|
||||
@ProviderFor(_preferenceSettingContent)
|
||||
final _preferenceSettingContentProvider =
|
||||
FutureProvider<Map<String, dynamic>>.internal(
|
||||
_preferenceSettingContent,
|
||||
name: r'_preferenceSettingContentProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$preferenceSettingGroupsHash,
|
||||
: _$preferenceSettingContentHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef _PreferenceSettingGroupsRef
|
||||
= FutureProviderRef<Map<String, PreferenceSettingGroup>>;
|
||||
String _$preferenceSettingGroupHash() =>
|
||||
r'960169ab9f6985a5b73a63663e671a8eac7cd776';
|
||||
typedef _PreferenceSettingContentRef = FutureProviderRef<Map<String, dynamic>>;
|
||||
String _$preferenceSettingGroupsHash() =>
|
||||
r'd267dacf82a11568c9cf0cc864f434db35f93394';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
@@ -50,6 +49,145 @@ class _SystemHash {
|
||||
}
|
||||
}
|
||||
|
||||
/// See also [_preferenceSettingGroups].
|
||||
@ProviderFor(_preferenceSettingGroups)
|
||||
const _preferenceSettingGroupsProvider = _PreferenceSettingGroupsFamily();
|
||||
|
||||
/// See also [_preferenceSettingGroups].
|
||||
class _PreferenceSettingGroupsFamily
|
||||
extends Family<AsyncValue<Map<String, PreferenceSettingGroup>>> {
|
||||
/// See also [_preferenceSettingGroups].
|
||||
const _PreferenceSettingGroupsFamily();
|
||||
|
||||
/// See also [_preferenceSettingGroups].
|
||||
_PreferenceSettingGroupsProvider call(
|
||||
PreferencePartition partition,
|
||||
) {
|
||||
return _PreferenceSettingGroupsProvider(
|
||||
partition,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
_PreferenceSettingGroupsProvider getProviderOverride(
|
||||
covariant _PreferenceSettingGroupsProvider provider,
|
||||
) {
|
||||
return call(
|
||||
provider.partition,
|
||||
);
|
||||
}
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||
_allTransitiveDependencies;
|
||||
|
||||
@override
|
||||
String? get name => r'_preferenceSettingGroupsProvider';
|
||||
}
|
||||
|
||||
/// See also [_preferenceSettingGroups].
|
||||
class _PreferenceSettingGroupsProvider
|
||||
extends FutureProvider<Map<String, PreferenceSettingGroup>> {
|
||||
/// See also [_preferenceSettingGroups].
|
||||
_PreferenceSettingGroupsProvider(
|
||||
PreferencePartition partition,
|
||||
) : this._internal(
|
||||
(ref) => _preferenceSettingGroups(
|
||||
ref as _PreferenceSettingGroupsRef,
|
||||
partition,
|
||||
),
|
||||
from: _preferenceSettingGroupsProvider,
|
||||
name: r'_preferenceSettingGroupsProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$preferenceSettingGroupsHash,
|
||||
dependencies: _PreferenceSettingGroupsFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
_PreferenceSettingGroupsFamily._allTransitiveDependencies,
|
||||
partition: partition,
|
||||
);
|
||||
|
||||
_PreferenceSettingGroupsProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.partition,
|
||||
}) : super.internal();
|
||||
|
||||
final PreferencePartition partition;
|
||||
|
||||
@override
|
||||
Override overrideWith(
|
||||
FutureOr<Map<String, PreferenceSettingGroup>> Function(
|
||||
_PreferenceSettingGroupsRef provider)
|
||||
create,
|
||||
) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: _PreferenceSettingGroupsProvider._internal(
|
||||
(ref) => create(ref as _PreferenceSettingGroupsRef),
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
partition: partition,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
FutureProviderElement<Map<String, PreferenceSettingGroup>> createElement() {
|
||||
return _PreferenceSettingGroupsProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is _PreferenceSettingGroupsProvider &&
|
||||
other.partition == partition;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, partition.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin _PreferenceSettingGroupsRef
|
||||
on FutureProviderRef<Map<String, PreferenceSettingGroup>> {
|
||||
/// The parameter `partition` of this provider.
|
||||
PreferencePartition get partition;
|
||||
}
|
||||
|
||||
class _PreferenceSettingGroupsProviderElement
|
||||
extends FutureProviderElement<Map<String, PreferenceSettingGroup>>
|
||||
with _PreferenceSettingGroupsRef {
|
||||
_PreferenceSettingGroupsProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
PreferencePartition get partition =>
|
||||
(origin as _PreferenceSettingGroupsProvider).partition;
|
||||
}
|
||||
|
||||
String _$preferenceSettingGroupHash() =>
|
||||
r'64d80a37928d7e5aea7d3d567dde8ade7292cd5f';
|
||||
|
||||
/// See also [_preferenceSettingGroup].
|
||||
@ProviderFor(_preferenceSettingGroup)
|
||||
const _preferenceSettingGroupProvider = _PreferenceSettingGroupFamily();
|
||||
@@ -62,9 +200,11 @@ class _PreferenceSettingGroupFamily
|
||||
|
||||
/// See also [_preferenceSettingGroup].
|
||||
_PreferenceSettingGroupProvider call(
|
||||
PreferencePartition partition,
|
||||
String groupName,
|
||||
) {
|
||||
return _PreferenceSettingGroupProvider(
|
||||
partition,
|
||||
groupName,
|
||||
);
|
||||
}
|
||||
@@ -74,6 +214,7 @@ class _PreferenceSettingGroupFamily
|
||||
covariant _PreferenceSettingGroupProvider provider,
|
||||
) {
|
||||
return call(
|
||||
provider.partition,
|
||||
provider.groupName,
|
||||
);
|
||||
}
|
||||
@@ -98,10 +239,12 @@ class _PreferenceSettingGroupProvider
|
||||
extends FutureProvider<PreferenceSettingGroup> {
|
||||
/// See also [_preferenceSettingGroup].
|
||||
_PreferenceSettingGroupProvider(
|
||||
PreferencePartition partition,
|
||||
String groupName,
|
||||
) : this._internal(
|
||||
(ref) => _preferenceSettingGroup(
|
||||
ref as _PreferenceSettingGroupRef,
|
||||
partition,
|
||||
groupName,
|
||||
),
|
||||
from: _preferenceSettingGroupProvider,
|
||||
@@ -113,6 +256,7 @@ class _PreferenceSettingGroupProvider
|
||||
dependencies: _PreferenceSettingGroupFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
_PreferenceSettingGroupFamily._allTransitiveDependencies,
|
||||
partition: partition,
|
||||
groupName: groupName,
|
||||
);
|
||||
|
||||
@@ -123,9 +267,11 @@ class _PreferenceSettingGroupProvider
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.partition,
|
||||
required this.groupName,
|
||||
}) : super.internal();
|
||||
|
||||
final PreferencePartition partition;
|
||||
final String groupName;
|
||||
|
||||
@override
|
||||
@@ -143,6 +289,7 @@ class _PreferenceSettingGroupProvider
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
partition: partition,
|
||||
groupName: groupName,
|
||||
),
|
||||
);
|
||||
@@ -156,12 +303,14 @@ class _PreferenceSettingGroupProvider
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is _PreferenceSettingGroupProvider &&
|
||||
other.partition == partition &&
|
||||
other.groupName == groupName;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, partition.hashCode);
|
||||
hash = _SystemHash.combine(hash, groupName.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
@@ -171,6 +320,9 @@ class _PreferenceSettingGroupProvider
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin _PreferenceSettingGroupRef on FutureProviderRef<PreferenceSettingGroup> {
|
||||
/// The parameter `partition` of this provider.
|
||||
PreferencePartition get partition;
|
||||
|
||||
/// The parameter `groupName` of this provider.
|
||||
String get groupName;
|
||||
}
|
||||
@@ -180,6 +332,9 @@ class _PreferenceSettingGroupProviderElement
|
||||
with _PreferenceSettingGroupRef {
|
||||
_PreferenceSettingGroupProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
PreferencePartition get partition =>
|
||||
(origin as _PreferenceSettingGroupProvider).partition;
|
||||
@override
|
||||
String get groupName => (origin as _PreferenceSettingGroupProvider).groupName;
|
||||
}
|
||||
@@ -202,33 +357,174 @@ final _preferenceRepositoryProvider = AutoDisposeNotifierProvider<
|
||||
|
||||
typedef _$PreferenceRepository
|
||||
= AutoDisposeNotifier<Raw<Stream<Map<String, Object>>>>;
|
||||
String _$preferenceSettingsGeneralRepositoryHash() =>
|
||||
r'24a70de8284107bb43778e1014abe41bd805987a';
|
||||
String _$unifiedPreferenceSettingsRepositoryHash() =>
|
||||
r'35aa16ffa7aa1453c8331ed7470ef85a68d27178';
|
||||
|
||||
/// See also [PreferenceSettingsGeneralRepository].
|
||||
@ProviderFor(PreferenceSettingsGeneralRepository)
|
||||
final preferenceSettingsGeneralRepositoryProvider =
|
||||
AutoDisposeStreamNotifierProvider<PreferenceSettingsGeneralRepository,
|
||||
Map<String, PreferenceSettingGroup>>.internal(
|
||||
PreferenceSettingsGeneralRepository.new,
|
||||
name: r'preferenceSettingsGeneralRepositoryProvider',
|
||||
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$preferenceSettingsGeneralRepositoryHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
abstract class _$UnifiedPreferenceSettingsRepository
|
||||
extends BuildlessAutoDisposeStreamNotifier<
|
||||
Map<String, PreferenceSettingGroup>> {
|
||||
late final PreferencePartition partition;
|
||||
|
||||
Stream<Map<String, PreferenceSettingGroup>> build(
|
||||
PreferencePartition partition,
|
||||
);
|
||||
}
|
||||
|
||||
/// See also [UnifiedPreferenceSettingsRepository].
|
||||
@ProviderFor(UnifiedPreferenceSettingsRepository)
|
||||
const unifiedPreferenceSettingsRepositoryProvider =
|
||||
UnifiedPreferenceSettingsRepositoryFamily();
|
||||
|
||||
/// See also [UnifiedPreferenceSettingsRepository].
|
||||
class UnifiedPreferenceSettingsRepositoryFamily
|
||||
extends Family<AsyncValue<Map<String, PreferenceSettingGroup>>> {
|
||||
/// See also [UnifiedPreferenceSettingsRepository].
|
||||
const UnifiedPreferenceSettingsRepositoryFamily();
|
||||
|
||||
/// See also [UnifiedPreferenceSettingsRepository].
|
||||
UnifiedPreferenceSettingsRepositoryProvider call(
|
||||
PreferencePartition partition,
|
||||
) {
|
||||
return UnifiedPreferenceSettingsRepositoryProvider(
|
||||
partition,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
UnifiedPreferenceSettingsRepositoryProvider getProviderOverride(
|
||||
covariant UnifiedPreferenceSettingsRepositoryProvider provider,
|
||||
) {
|
||||
return call(
|
||||
provider.partition,
|
||||
);
|
||||
}
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||
_allTransitiveDependencies;
|
||||
|
||||
@override
|
||||
String? get name => r'unifiedPreferenceSettingsRepositoryProvider';
|
||||
}
|
||||
|
||||
/// See also [UnifiedPreferenceSettingsRepository].
|
||||
class UnifiedPreferenceSettingsRepositoryProvider
|
||||
extends AutoDisposeStreamNotifierProviderImpl<
|
||||
UnifiedPreferenceSettingsRepository,
|
||||
Map<String, PreferenceSettingGroup>> {
|
||||
/// See also [UnifiedPreferenceSettingsRepository].
|
||||
UnifiedPreferenceSettingsRepositoryProvider(
|
||||
PreferencePartition partition,
|
||||
) : this._internal(
|
||||
() => UnifiedPreferenceSettingsRepository()..partition = partition,
|
||||
from: unifiedPreferenceSettingsRepositoryProvider,
|
||||
name: r'unifiedPreferenceSettingsRepositoryProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$unifiedPreferenceSettingsRepositoryHash,
|
||||
dependencies: UnifiedPreferenceSettingsRepositoryFamily._dependencies,
|
||||
allTransitiveDependencies: UnifiedPreferenceSettingsRepositoryFamily
|
||||
._allTransitiveDependencies,
|
||||
partition: partition,
|
||||
);
|
||||
|
||||
UnifiedPreferenceSettingsRepositoryProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.partition,
|
||||
}) : super.internal();
|
||||
|
||||
final PreferencePartition partition;
|
||||
|
||||
@override
|
||||
Stream<Map<String, PreferenceSettingGroup>> runNotifierBuild(
|
||||
covariant UnifiedPreferenceSettingsRepository notifier,
|
||||
) {
|
||||
return notifier.build(
|
||||
partition,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Override overrideWith(UnifiedPreferenceSettingsRepository Function() create) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: UnifiedPreferenceSettingsRepositoryProvider._internal(
|
||||
() => create()..partition = partition,
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
partition: partition,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeStreamNotifierProviderElement<UnifiedPreferenceSettingsRepository,
|
||||
Map<String, PreferenceSettingGroup>> createElement() {
|
||||
return _UnifiedPreferenceSettingsRepositoryProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is UnifiedPreferenceSettingsRepositoryProvider &&
|
||||
other.partition == partition;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, partition.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin UnifiedPreferenceSettingsRepositoryRef
|
||||
on AutoDisposeStreamNotifierProviderRef<
|
||||
Map<String, PreferenceSettingGroup>> {
|
||||
/// The parameter `partition` of this provider.
|
||||
PreferencePartition get partition;
|
||||
}
|
||||
|
||||
class _UnifiedPreferenceSettingsRepositoryProviderElement
|
||||
extends AutoDisposeStreamNotifierProviderElement<
|
||||
UnifiedPreferenceSettingsRepository,
|
||||
Map<String, PreferenceSettingGroup>>
|
||||
with UnifiedPreferenceSettingsRepositoryRef {
|
||||
_UnifiedPreferenceSettingsRepositoryProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
PreferencePartition get partition =>
|
||||
(origin as UnifiedPreferenceSettingsRepositoryProvider).partition;
|
||||
}
|
||||
|
||||
typedef _$PreferenceSettingsGeneralRepository
|
||||
= AutoDisposeStreamNotifier<Map<String, PreferenceSettingGroup>>;
|
||||
String _$preferenceSettingsGroupRepositoryHash() =>
|
||||
r'023794294c5639ac6c6941f87a4ed91e1551fbc9';
|
||||
r'60f416ffe1cde4d535fb5b643a80aead20836bae';
|
||||
|
||||
abstract class _$PreferenceSettingsGroupRepository
|
||||
extends BuildlessAutoDisposeStreamNotifier<PreferenceSettingGroup> {
|
||||
late final PreferencePartition partition;
|
||||
late final String groupName;
|
||||
|
||||
Stream<PreferenceSettingGroup> build(
|
||||
PreferencePartition partition,
|
||||
String groupName,
|
||||
);
|
||||
}
|
||||
@@ -246,9 +542,11 @@ class PreferenceSettingsGroupRepositoryFamily
|
||||
|
||||
/// See also [PreferenceSettingsGroupRepository].
|
||||
PreferenceSettingsGroupRepositoryProvider call(
|
||||
PreferencePartition partition,
|
||||
String groupName,
|
||||
) {
|
||||
return PreferenceSettingsGroupRepositoryProvider(
|
||||
partition,
|
||||
groupName,
|
||||
);
|
||||
}
|
||||
@@ -258,6 +556,7 @@ class PreferenceSettingsGroupRepositoryFamily
|
||||
covariant PreferenceSettingsGroupRepositoryProvider provider,
|
||||
) {
|
||||
return call(
|
||||
provider.partition,
|
||||
provider.groupName,
|
||||
);
|
||||
}
|
||||
@@ -283,9 +582,12 @@ class PreferenceSettingsGroupRepositoryProvider
|
||||
PreferenceSettingsGroupRepository, PreferenceSettingGroup> {
|
||||
/// See also [PreferenceSettingsGroupRepository].
|
||||
PreferenceSettingsGroupRepositoryProvider(
|
||||
PreferencePartition partition,
|
||||
String groupName,
|
||||
) : this._internal(
|
||||
() => PreferenceSettingsGroupRepository()..groupName = groupName,
|
||||
() => PreferenceSettingsGroupRepository()
|
||||
..partition = partition
|
||||
..groupName = groupName,
|
||||
from: preferenceSettingsGroupRepositoryProvider,
|
||||
name: r'preferenceSettingsGroupRepositoryProvider',
|
||||
debugGetCreateSourceHash:
|
||||
@@ -295,6 +597,7 @@ class PreferenceSettingsGroupRepositoryProvider
|
||||
dependencies: PreferenceSettingsGroupRepositoryFamily._dependencies,
|
||||
allTransitiveDependencies: PreferenceSettingsGroupRepositoryFamily
|
||||
._allTransitiveDependencies,
|
||||
partition: partition,
|
||||
groupName: groupName,
|
||||
);
|
||||
|
||||
@@ -305,9 +608,11 @@ class PreferenceSettingsGroupRepositoryProvider
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.partition,
|
||||
required this.groupName,
|
||||
}) : super.internal();
|
||||
|
||||
final PreferencePartition partition;
|
||||
final String groupName;
|
||||
|
||||
@override
|
||||
@@ -315,6 +620,7 @@ class PreferenceSettingsGroupRepositoryProvider
|
||||
covariant PreferenceSettingsGroupRepository notifier,
|
||||
) {
|
||||
return notifier.build(
|
||||
partition,
|
||||
groupName,
|
||||
);
|
||||
}
|
||||
@@ -324,12 +630,15 @@ class PreferenceSettingsGroupRepositoryProvider
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: PreferenceSettingsGroupRepositoryProvider._internal(
|
||||
() => create()..groupName = groupName,
|
||||
() => create()
|
||||
..partition = partition
|
||||
..groupName = groupName,
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
partition: partition,
|
||||
groupName: groupName,
|
||||
),
|
||||
);
|
||||
@@ -344,12 +653,14 @@ class PreferenceSettingsGroupRepositoryProvider
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is PreferenceSettingsGroupRepositoryProvider &&
|
||||
other.partition == partition &&
|
||||
other.groupName == groupName;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, partition.hashCode);
|
||||
hash = _SystemHash.combine(hash, groupName.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
@@ -360,6 +671,9 @@ class PreferenceSettingsGroupRepositoryProvider
|
||||
// ignore: unused_element
|
||||
mixin PreferenceSettingsGroupRepositoryRef
|
||||
on AutoDisposeStreamNotifierProviderRef<PreferenceSettingGroup> {
|
||||
/// The parameter `partition` of this provider.
|
||||
PreferencePartition get partition;
|
||||
|
||||
/// The parameter `groupName` of this provider.
|
||||
String get groupName;
|
||||
}
|
||||
@@ -370,6 +684,9 @@ class _PreferenceSettingsGroupRepositoryProviderElement
|
||||
PreferenceSettingGroup> with PreferenceSettingsGroupRepositoryRef {
|
||||
_PreferenceSettingsGroupRepositoryProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
PreferencePartition get partition =>
|
||||
(origin as PreferenceSettingsGroupRepositoryProvider).partition;
|
||||
@override
|
||||
String get groupName =>
|
||||
(origin as PreferenceSettingsGroupRepositoryProvider).groupName;
|
||||
|
||||
+3
-2
@@ -5,7 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:lensai/features/geckoview/domain/entities/readerable_state.dart';
|
||||
import 'package:lensai/features/geckoview/domain/providers/tab_state.dart';
|
||||
import 'package:lensai/features/geckoview/features/readerview/presentation/controllers/readerable.dart';
|
||||
import 'package:lensai/features/user/domain/repositories/settings.dart';
|
||||
import 'package:lensai/features/user/domain/repositories/general_settings.dart';
|
||||
import 'package:lensai/presentation/widgets/animate_gradient_shader.dart';
|
||||
|
||||
class ReaderButton extends HookConsumerWidget {
|
||||
@@ -16,7 +16,8 @@ class ReaderButton extends HookConsumerWidget {
|
||||
final readerChanging = ref.watch(readerableScreenControllerProvider);
|
||||
|
||||
final enableReadability = ref.watch(
|
||||
settingsRepositoryProvider.select((value) => value.enableReadability),
|
||||
generalSettingsRepositoryProvider
|
||||
.select((value) => value.enableReadability),
|
||||
);
|
||||
|
||||
final readerabilityState = ref.watch(
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/models/container_data.dart';
|
||||
|
||||
class ContainerMetadataConverter
|
||||
implements TypeConverter<ContainerMetadata, String> {
|
||||
const ContainerMetadataConverter();
|
||||
|
||||
@override
|
||||
ContainerMetadata fromSql(String fromDb) {
|
||||
final json = jsonDecode(fromDb) as Map<String, dynamic>;
|
||||
return ContainerMetadata.fromJson(json);
|
||||
}
|
||||
|
||||
@override
|
||||
String toSql(ContainerMetadata value) {
|
||||
return jsonEncode(value.toJson());
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:lensai/core/uuid.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/database/database.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/models/container_data.dart';
|
||||
|
||||
@@ -12,26 +11,24 @@ class ContainerDao extends DatabaseAccessor<TabDatabase>
|
||||
with _$ContainerDaoMixin {
|
||||
ContainerDao(super.db);
|
||||
|
||||
Future<void> addContainer({String? name, required Color color}) {
|
||||
Future<void> addContainer(ContainerData container) {
|
||||
return db.container.insertOne(
|
||||
ContainerCompanion.insert(
|
||||
id: uuid.v7(),
|
||||
name: Value(name),
|
||||
color: color,
|
||||
id: container.id,
|
||||
name: Value(container.name),
|
||||
color: container.color,
|
||||
metadata: Value(container.metadata),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> replaceContainer(
|
||||
String id, {
|
||||
required String? name,
|
||||
required Color color,
|
||||
}) {
|
||||
Future<void> replaceContainer(ContainerData container) {
|
||||
return db.container.replaceOne(
|
||||
ContainerCompanion(
|
||||
id: Value(id),
|
||||
name: Value(name),
|
||||
color: Value(color),
|
||||
id: Value(container.id),
|
||||
name: Value(container.name),
|
||||
color: Value(container.color),
|
||||
metadata: Value(container.metadata),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ class TabDao extends DatabaseAccessor<TabDatabase> with _$TabDaoMixin {
|
||||
return query.map((row) => row.read(db.tab.id)!);
|
||||
}
|
||||
|
||||
SingleSelectable<String?> tabContainerId(String tabId) {
|
||||
SingleOrNullSelectable<String?> tabContainerId(String tabId) {
|
||||
final query = selectOnly(db.tab)
|
||||
..addColumns([db.tab.containerId])
|
||||
..where(db.tab.id.equals(tabId));
|
||||
@@ -49,7 +49,41 @@ class TabDao extends DatabaseAccessor<TabDatabase> with _$TabDaoMixin {
|
||||
return query.map((row) => row.read(db.tab.containerId));
|
||||
}
|
||||
|
||||
Future<String> upsertTab(
|
||||
Future<String> upsertContainerTabTransactional(
|
||||
Future<String> Function() createTab, {
|
||||
Value<String?> containerId = const Value.absent(),
|
||||
Value<String?> orderKey = const Value.absent(),
|
||||
}) {
|
||||
return db.transaction(() async {
|
||||
final tabId = await createTab();
|
||||
|
||||
final currentOrderKey = orderKey.value ??
|
||||
await db.containerDao
|
||||
.generateLeadingOrderKey(containerId.value)
|
||||
.getSingle();
|
||||
|
||||
await db.tab.insertOne(
|
||||
TabCompanion.insert(
|
||||
id: tabId,
|
||||
timestamp: DateTime.now(),
|
||||
containerId: containerId,
|
||||
orderKey: currentOrderKey,
|
||||
),
|
||||
onConflict: DoUpdate(
|
||||
(old) => TabCompanion.custom(
|
||||
containerId:
|
||||
(containerId.present) ? Variable(containerId.value) : null,
|
||||
orderKey: (orderKey.present) ? Variable(orderKey.value) : null,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return tabId;
|
||||
});
|
||||
}
|
||||
|
||||
//Upsert an tab only if there is no container assigned yet
|
||||
Future<String> upsertUnassignedTab(
|
||||
String tabId, {
|
||||
Value<String?> containerId = const Value.absent(),
|
||||
Value<String?> orderKey = const Value.absent(),
|
||||
@@ -73,6 +107,7 @@ class TabDao extends DatabaseAccessor<TabDatabase> with _$TabDaoMixin {
|
||||
(containerId.present) ? Variable(containerId.value) : null,
|
||||
orderKey: (orderKey.present) ? Variable(orderKey.value) : null,
|
||||
),
|
||||
where: (old) => old.containerId.isNull(),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:flutter/widgets.dart' show Color, IconData;
|
||||
import 'package:flutter/widgets.dart' show Color;
|
||||
import 'package:lensai/data/database/converters/color.dart';
|
||||
import 'package:lensai/data/database/converters/icon_data.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/database/converters/container_metadata_converter.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/database/daos/container.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/database/daos/tab.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/models/container_data.dart';
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import 'package:lensai/data/database/converters/color.dart';
|
||||
import 'package:lensai/data/database/converters/icon_data.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/models/container_data.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/models/tab_query_result.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/database/converters/container_metadata_converter.dart';
|
||||
|
||||
CREATE TABLE container (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
contextual_identity TEXT,
|
||||
name TEXT,
|
||||
color INTEGER NOT NULL MAPPED BY `const ColorConverter()`,
|
||||
icon TEXT MAPPED BY `const IconDataTypeConverter()`
|
||||
metadata TEXT MAPPED BY `const ContainerMetadataConverter()`
|
||||
) WITH ContainerData;
|
||||
|
||||
CREATE TABLE tab (
|
||||
@@ -79,7 +78,7 @@ containersWithCount WITH ContainerDataWithCount:
|
||||
FROM tab
|
||||
GROUP BY container_id
|
||||
) AS tab_agg ON container.id = tab_agg.container_id
|
||||
ORDER BY tab_agg.last_updated DESC NULLS FIRST;
|
||||
ORDER BY tab_agg.last_updated DESC NULLS LAST;
|
||||
|
||||
leadingOrderKey(:container_id AS TEXT OR NULL, :bucket AS INTEGER):
|
||||
SELECT lexo_rank_previous(
|
||||
|
||||
@@ -13,11 +13,6 @@ class Container extends Table with TableInfo<Container, ContainerData> {
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
$customConstraints: 'PRIMARY KEY NOT NULL');
|
||||
late final GeneratedColumn<String> contextualIdentity =
|
||||
GeneratedColumn<String>('contextual_identity', aliasedName, true,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: false,
|
||||
$customConstraints: '');
|
||||
late final GeneratedColumn<String> name = GeneratedColumn<String>(
|
||||
'name', aliasedName, true,
|
||||
type: DriftSqlType.string,
|
||||
@@ -29,15 +24,14 @@ class Container extends Table with TableInfo<Container, ContainerData> {
|
||||
requiredDuringInsert: true,
|
||||
$customConstraints: 'NOT NULL')
|
||||
.withConverter<Color>(Container.$convertercolor);
|
||||
late final GeneratedColumnWithTypeConverter<IconData?, String> icon =
|
||||
GeneratedColumn<String>('icon', aliasedName, true,
|
||||
late final GeneratedColumnWithTypeConverter<ContainerMetadata?, String>
|
||||
metadata = GeneratedColumn<String>('metadata', aliasedName, true,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: false,
|
||||
$customConstraints: '')
|
||||
.withConverter<IconData?>(Container.$convertericonn);
|
||||
.withConverter<ContainerMetadata?>(Container.$convertermetadatan);
|
||||
@override
|
||||
List<GeneratedColumn> get $columns =>
|
||||
[id, contextualIdentity, name, color, icon];
|
||||
List<GeneratedColumn> get $columns => [id, name, color, metadata];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@override
|
||||
@@ -51,14 +45,13 @@ class Container extends Table with TableInfo<Container, ContainerData> {
|
||||
return ContainerData(
|
||||
id: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.string, data['${effectivePrefix}id'])!,
|
||||
contextualIdentity: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string, data['${effectivePrefix}contextual_identity']),
|
||||
name: attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.string, data['${effectivePrefix}name']),
|
||||
color: Container.$convertercolor.fromSql(attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.int, data['${effectivePrefix}color'])!),
|
||||
icon: Container.$convertericonn.fromSql(attachedDatabase.typeMapping
|
||||
.read(DriftSqlType.string, data['${effectivePrefix}icon'])),
|
||||
metadata: Container.$convertermetadatan.fromSql(attachedDatabase
|
||||
.typeMapping
|
||||
.read(DriftSqlType.string, data['${effectivePrefix}metadata'])),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -68,69 +61,62 @@ class Container extends Table with TableInfo<Container, ContainerData> {
|
||||
}
|
||||
|
||||
static TypeConverter<Color, int> $convertercolor = const ColorConverter();
|
||||
static TypeConverter<IconData, String> $convertericon =
|
||||
const IconDataTypeConverter();
|
||||
static TypeConverter<IconData?, String?> $convertericonn =
|
||||
NullAwareTypeConverter.wrap($convertericon);
|
||||
static TypeConverter<ContainerMetadata, String> $convertermetadata =
|
||||
const ContainerMetadataConverter();
|
||||
static TypeConverter<ContainerMetadata?, String?> $convertermetadatan =
|
||||
NullAwareTypeConverter.wrap($convertermetadata);
|
||||
@override
|
||||
bool get dontWriteConstraints => true;
|
||||
}
|
||||
|
||||
class ContainerCompanion extends UpdateCompanion<ContainerData> {
|
||||
final Value<String> id;
|
||||
final Value<String?> contextualIdentity;
|
||||
final Value<String?> name;
|
||||
final Value<Color> color;
|
||||
final Value<IconData?> icon;
|
||||
final Value<ContainerMetadata?> metadata;
|
||||
final Value<int> rowid;
|
||||
const ContainerCompanion({
|
||||
this.id = const Value.absent(),
|
||||
this.contextualIdentity = const Value.absent(),
|
||||
this.name = const Value.absent(),
|
||||
this.color = const Value.absent(),
|
||||
this.icon = const Value.absent(),
|
||||
this.metadata = const Value.absent(),
|
||||
this.rowid = const Value.absent(),
|
||||
});
|
||||
ContainerCompanion.insert({
|
||||
required String id,
|
||||
this.contextualIdentity = const Value.absent(),
|
||||
this.name = const Value.absent(),
|
||||
required Color color,
|
||||
this.icon = const Value.absent(),
|
||||
this.metadata = const Value.absent(),
|
||||
this.rowid = const Value.absent(),
|
||||
}) : id = Value(id),
|
||||
color = Value(color);
|
||||
static Insertable<ContainerData> custom({
|
||||
Expression<String>? id,
|
||||
Expression<String>? contextualIdentity,
|
||||
Expression<String>? name,
|
||||
Expression<int>? color,
|
||||
Expression<String>? icon,
|
||||
Expression<String>? metadata,
|
||||
Expression<int>? rowid,
|
||||
}) {
|
||||
return RawValuesInsertable({
|
||||
if (id != null) 'id': id,
|
||||
if (contextualIdentity != null) 'contextual_identity': contextualIdentity,
|
||||
if (name != null) 'name': name,
|
||||
if (color != null) 'color': color,
|
||||
if (icon != null) 'icon': icon,
|
||||
if (metadata != null) 'metadata': metadata,
|
||||
if (rowid != null) 'rowid': rowid,
|
||||
});
|
||||
}
|
||||
|
||||
ContainerCompanion copyWith(
|
||||
{Value<String>? id,
|
||||
Value<String?>? contextualIdentity,
|
||||
Value<String?>? name,
|
||||
Value<Color>? color,
|
||||
Value<IconData?>? icon,
|
||||
Value<ContainerMetadata?>? metadata,
|
||||
Value<int>? rowid}) {
|
||||
return ContainerCompanion(
|
||||
id: id ?? this.id,
|
||||
contextualIdentity: contextualIdentity ?? this.contextualIdentity,
|
||||
name: name ?? this.name,
|
||||
color: color ?? this.color,
|
||||
icon: icon ?? this.icon,
|
||||
metadata: metadata ?? this.metadata,
|
||||
rowid: rowid ?? this.rowid,
|
||||
);
|
||||
}
|
||||
@@ -141,9 +127,6 @@ class ContainerCompanion extends UpdateCompanion<ContainerData> {
|
||||
if (id.present) {
|
||||
map['id'] = Variable<String>(id.value);
|
||||
}
|
||||
if (contextualIdentity.present) {
|
||||
map['contextual_identity'] = Variable<String>(contextualIdentity.value);
|
||||
}
|
||||
if (name.present) {
|
||||
map['name'] = Variable<String>(name.value);
|
||||
}
|
||||
@@ -151,9 +134,9 @@ class ContainerCompanion extends UpdateCompanion<ContainerData> {
|
||||
map['color'] =
|
||||
Variable<int>(Container.$convertercolor.toSql(color.value));
|
||||
}
|
||||
if (icon.present) {
|
||||
map['icon'] =
|
||||
Variable<String>(Container.$convertericonn.toSql(icon.value));
|
||||
if (metadata.present) {
|
||||
map['metadata'] =
|
||||
Variable<String>(Container.$convertermetadatan.toSql(metadata.value));
|
||||
}
|
||||
if (rowid.present) {
|
||||
map['rowid'] = Variable<int>(rowid.value);
|
||||
@@ -165,10 +148,9 @@ class ContainerCompanion extends UpdateCompanion<ContainerData> {
|
||||
String toString() {
|
||||
return (StringBuffer('ContainerCompanion(')
|
||||
..write('id: $id, ')
|
||||
..write('contextualIdentity: $contextualIdentity, ')
|
||||
..write('name: $name, ')
|
||||
..write('color: $color, ')
|
||||
..write('icon: $icon, ')
|
||||
..write('metadata: $metadata, ')
|
||||
..write('rowid: $rowid')
|
||||
..write(')'))
|
||||
.toString();
|
||||
@@ -1597,18 +1579,18 @@ abstract class _$TabDatabase extends GeneratedDatabase {
|
||||
|
||||
Selectable<ContainerDataWithCount> containersWithCount() {
|
||||
return customSelect(
|
||||
'SELECT container.*, tab_agg.tab_count FROM container LEFT JOIN (SELECT container_id, COUNT(*) AS tab_count, MAX(timestamp) AS last_updated FROM tab GROUP BY container_id) AS tab_agg ON container.id = tab_agg.container_id ORDER BY tab_agg.last_updated DESC NULLS FIRST',
|
||||
'SELECT container.*, tab_agg.tab_count FROM container LEFT JOIN (SELECT container_id, COUNT(*) AS tab_count, MAX(timestamp) AS last_updated FROM tab GROUP BY container_id) AS tab_agg ON container.id = tab_agg.container_id ORDER BY tab_agg.last_updated DESC NULLS LAST',
|
||||
variables: [],
|
||||
readsFrom: {
|
||||
container,
|
||||
tab,
|
||||
}).map((QueryRow row) => ContainerDataWithCount(
|
||||
id: row.read<String>('id'),
|
||||
contextualIdentity: row.readNullable<String>('contextual_identity'),
|
||||
name: row.readNullable<String>('name'),
|
||||
color: Container.$convertercolor.fromSql(row.read<int>('color')),
|
||||
icon: NullAwareTypeConverter.wrapFromSql(
|
||||
Container.$convertericon, row.readNullable<String>('icon')),
|
||||
metadata: NullAwareTypeConverter.wrapFromSql(
|
||||
Container.$convertermetadata,
|
||||
row.readNullable<String>('metadata')),
|
||||
tabCount: row.readNullable<int>('tab_count'),
|
||||
));
|
||||
}
|
||||
@@ -1819,18 +1801,16 @@ abstract class _$TabDatabase extends GeneratedDatabase {
|
||||
|
||||
typedef $ContainerCreateCompanionBuilder = ContainerCompanion Function({
|
||||
required String id,
|
||||
Value<String?> contextualIdentity,
|
||||
Value<String?> name,
|
||||
required Color color,
|
||||
Value<IconData?> icon,
|
||||
Value<ContainerMetadata?> metadata,
|
||||
Value<int> rowid,
|
||||
});
|
||||
typedef $ContainerUpdateCompanionBuilder = ContainerCompanion Function({
|
||||
Value<String> id,
|
||||
Value<String?> contextualIdentity,
|
||||
Value<String?> name,
|
||||
Value<Color> color,
|
||||
Value<IconData?> icon,
|
||||
Value<ContainerMetadata?> metadata,
|
||||
Value<int> rowid,
|
||||
});
|
||||
|
||||
@@ -1864,10 +1844,6 @@ class $ContainerFilterComposer extends Composer<_$TabDatabase, Container> {
|
||||
ColumnFilters<String> get id => $composableBuilder(
|
||||
column: $table.id, builder: (column) => ColumnFilters(column));
|
||||
|
||||
ColumnFilters<String> get contextualIdentity => $composableBuilder(
|
||||
column: $table.contextualIdentity,
|
||||
builder: (column) => ColumnFilters(column));
|
||||
|
||||
ColumnFilters<String> get name => $composableBuilder(
|
||||
column: $table.name, builder: (column) => ColumnFilters(column));
|
||||
|
||||
@@ -1876,9 +1852,9 @@ class $ContainerFilterComposer extends Composer<_$TabDatabase, Container> {
|
||||
column: $table.color,
|
||||
builder: (column) => ColumnWithTypeConverterFilters(column));
|
||||
|
||||
ColumnWithTypeConverterFilters<IconData?, IconData, String> get icon =>
|
||||
$composableBuilder(
|
||||
column: $table.icon,
|
||||
ColumnWithTypeConverterFilters<ContainerMetadata?, ContainerMetadata, String>
|
||||
get metadata => $composableBuilder(
|
||||
column: $table.metadata,
|
||||
builder: (column) => ColumnWithTypeConverterFilters(column));
|
||||
|
||||
Expression<bool> tabRefs(Expression<bool> Function($TabFilterComposer f) f) {
|
||||
@@ -1913,18 +1889,14 @@ class $ContainerOrderingComposer extends Composer<_$TabDatabase, Container> {
|
||||
ColumnOrderings<String> get id => $composableBuilder(
|
||||
column: $table.id, builder: (column) => ColumnOrderings(column));
|
||||
|
||||
ColumnOrderings<String> get contextualIdentity => $composableBuilder(
|
||||
column: $table.contextualIdentity,
|
||||
builder: (column) => ColumnOrderings(column));
|
||||
|
||||
ColumnOrderings<String> get name => $composableBuilder(
|
||||
column: $table.name, builder: (column) => ColumnOrderings(column));
|
||||
|
||||
ColumnOrderings<int> get color => $composableBuilder(
|
||||
column: $table.color, builder: (column) => ColumnOrderings(column));
|
||||
|
||||
ColumnOrderings<String> get icon => $composableBuilder(
|
||||
column: $table.icon, builder: (column) => ColumnOrderings(column));
|
||||
ColumnOrderings<String> get metadata => $composableBuilder(
|
||||
column: $table.metadata, builder: (column) => ColumnOrderings(column));
|
||||
}
|
||||
|
||||
class $ContainerAnnotationComposer extends Composer<_$TabDatabase, Container> {
|
||||
@@ -1938,17 +1910,14 @@ class $ContainerAnnotationComposer extends Composer<_$TabDatabase, Container> {
|
||||
GeneratedColumn<String> get id =>
|
||||
$composableBuilder(column: $table.id, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<String> get contextualIdentity => $composableBuilder(
|
||||
column: $table.contextualIdentity, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<String> get name =>
|
||||
$composableBuilder(column: $table.name, builder: (column) => column);
|
||||
|
||||
GeneratedColumnWithTypeConverter<Color, int> get color =>
|
||||
$composableBuilder(column: $table.color, builder: (column) => column);
|
||||
|
||||
GeneratedColumnWithTypeConverter<IconData?, String> get icon =>
|
||||
$composableBuilder(column: $table.icon, builder: (column) => column);
|
||||
GeneratedColumnWithTypeConverter<ContainerMetadata?, String> get metadata =>
|
||||
$composableBuilder(column: $table.metadata, builder: (column) => column);
|
||||
|
||||
Expression<T> tabRefs<T extends Object>(
|
||||
Expression<T> Function($TabAnnotationComposer a) f) {
|
||||
@@ -1996,34 +1965,30 @@ class $ContainerTableManager extends RootTableManager<
|
||||
$ContainerAnnotationComposer($db: db, $table: table),
|
||||
updateCompanionCallback: ({
|
||||
Value<String> id = const Value.absent(),
|
||||
Value<String?> contextualIdentity = const Value.absent(),
|
||||
Value<String?> name = const Value.absent(),
|
||||
Value<Color> color = const Value.absent(),
|
||||
Value<IconData?> icon = const Value.absent(),
|
||||
Value<ContainerMetadata?> metadata = const Value.absent(),
|
||||
Value<int> rowid = const Value.absent(),
|
||||
}) =>
|
||||
ContainerCompanion(
|
||||
id: id,
|
||||
contextualIdentity: contextualIdentity,
|
||||
name: name,
|
||||
color: color,
|
||||
icon: icon,
|
||||
metadata: metadata,
|
||||
rowid: rowid,
|
||||
),
|
||||
createCompanionCallback: ({
|
||||
required String id,
|
||||
Value<String?> contextualIdentity = const Value.absent(),
|
||||
Value<String?> name = const Value.absent(),
|
||||
required Color color,
|
||||
Value<IconData?> icon = const Value.absent(),
|
||||
Value<ContainerMetadata?> metadata = const Value.absent(),
|
||||
Value<int> rowid = const Value.absent(),
|
||||
}) =>
|
||||
ContainerCompanion.insert(
|
||||
id: id,
|
||||
contextualIdentity: contextualIdentity,
|
||||
name: name,
|
||||
color: color,
|
||||
icon: icon,
|
||||
metadata: metadata,
|
||||
rowid: rowid,
|
||||
),
|
||||
withReferenceMapper: (p0) => p0
|
||||
|
||||
@@ -1,20 +1,103 @@
|
||||
import 'package:copy_with_extension/copy_with_extension.dart';
|
||||
import 'package:fast_equatable/fast_equatable.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:lensai/data/database/converters/icon_data.dart';
|
||||
|
||||
part 'container_data.g.dart';
|
||||
|
||||
@CopyWith()
|
||||
@JsonSerializable()
|
||||
class ContainerAuthSettings with FastEquatable {
|
||||
final bool authenticationRequired;
|
||||
final bool lockOnAppBackground;
|
||||
final Duration? lockTimeout;
|
||||
|
||||
ContainerAuthSettings({
|
||||
required this.authenticationRequired,
|
||||
required this.lockOnAppBackground,
|
||||
required this.lockTimeout,
|
||||
});
|
||||
|
||||
ContainerAuthSettings.withDefaults({
|
||||
bool? authenticationRequired,
|
||||
bool? lockOnAppBackground,
|
||||
Duration? lockTimeout,
|
||||
}) : this(
|
||||
authenticationRequired: authenticationRequired ?? false,
|
||||
lockOnAppBackground: lockOnAppBackground ?? false,
|
||||
lockTimeout: lockTimeout,
|
||||
);
|
||||
|
||||
factory ContainerAuthSettings.fromJson(Map<String, dynamic> json) =>
|
||||
_$ContainerAuthSettingsFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$ContainerAuthSettingsToJson(this);
|
||||
|
||||
@override
|
||||
List<Object?> get hashParameters => [
|
||||
authenticationRequired,
|
||||
lockOnAppBackground,
|
||||
lockTimeout,
|
||||
];
|
||||
|
||||
@override
|
||||
bool get cacheHash => true;
|
||||
}
|
||||
|
||||
@CopyWith()
|
||||
@JsonSerializable(constructor: 'withDefaults')
|
||||
class ContainerMetadata with FastEquatable {
|
||||
@IconDataJsonConverter()
|
||||
final IconData? iconData;
|
||||
final String? contextualIdentity;
|
||||
final ContainerAuthSettings authSettings;
|
||||
|
||||
ContainerMetadata({
|
||||
required this.iconData,
|
||||
required this.contextualIdentity,
|
||||
required this.authSettings,
|
||||
});
|
||||
|
||||
ContainerMetadata.withDefaults({
|
||||
IconData? iconData,
|
||||
String? contextualIdentity,
|
||||
ContainerAuthSettings? authSettings,
|
||||
}) : this(
|
||||
iconData: iconData,
|
||||
contextualIdentity: contextualIdentity,
|
||||
authSettings: authSettings ?? ContainerAuthSettings.withDefaults(),
|
||||
);
|
||||
|
||||
factory ContainerMetadata.fromJson(Map<String, dynamic> json) =>
|
||||
_$ContainerMetadataFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$ContainerMetadataToJson(this);
|
||||
|
||||
@override
|
||||
List<Object?> get hashParameters => [
|
||||
iconData,
|
||||
contextualIdentity,
|
||||
authSettings,
|
||||
];
|
||||
|
||||
@override
|
||||
bool get cacheHash => true;
|
||||
}
|
||||
|
||||
@CopyWith()
|
||||
class ContainerData with FastEquatable {
|
||||
final String id;
|
||||
final String? contextualIdentity;
|
||||
final String? name;
|
||||
final Color color;
|
||||
final IconData? icon;
|
||||
final ContainerMetadata metadata;
|
||||
|
||||
ContainerData({
|
||||
required this.id,
|
||||
this.contextualIdentity,
|
||||
this.name,
|
||||
required this.color,
|
||||
this.icon,
|
||||
});
|
||||
ContainerMetadata? metadata,
|
||||
}) : metadata = metadata ?? ContainerMetadata.withDefaults();
|
||||
|
||||
@override
|
||||
bool get cacheHash => true;
|
||||
@@ -22,10 +105,9 @@ class ContainerData with FastEquatable {
|
||||
@override
|
||||
List<Object?> get hashParameters => [
|
||||
id,
|
||||
contextualIdentity,
|
||||
name,
|
||||
color,
|
||||
icon,
|
||||
metadata,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -34,10 +116,9 @@ class ContainerDataWithCount extends ContainerData {
|
||||
|
||||
ContainerDataWithCount({
|
||||
required super.id,
|
||||
super.contextualIdentity,
|
||||
super.name,
|
||||
required super.color,
|
||||
super.icon,
|
||||
super.metadata,
|
||||
required this.tabCount,
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,294 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'container_data.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// CopyWithGenerator
|
||||
// **************************************************************************
|
||||
|
||||
abstract class _$ContainerAuthSettingsCWProxy {
|
||||
ContainerAuthSettings authenticationRequired(bool authenticationRequired);
|
||||
|
||||
ContainerAuthSettings lockOnAppBackground(bool lockOnAppBackground);
|
||||
|
||||
ContainerAuthSettings lockTimeout(Duration? lockTimeout);
|
||||
|
||||
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `ContainerAuthSettings(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
||||
///
|
||||
/// Usage
|
||||
/// ```dart
|
||||
/// ContainerAuthSettings(...).copyWith(id: 12, name: "My name")
|
||||
/// ````
|
||||
ContainerAuthSettings call({
|
||||
bool authenticationRequired,
|
||||
bool lockOnAppBackground,
|
||||
Duration? lockTimeout,
|
||||
});
|
||||
}
|
||||
|
||||
/// Proxy class for `copyWith` functionality. This is a callable class and can be used as follows: `instanceOfContainerAuthSettings.copyWith(...)`. Additionally contains functions for specific fields e.g. `instanceOfContainerAuthSettings.copyWith.fieldName(...)`
|
||||
class _$ContainerAuthSettingsCWProxyImpl
|
||||
implements _$ContainerAuthSettingsCWProxy {
|
||||
const _$ContainerAuthSettingsCWProxyImpl(this._value);
|
||||
|
||||
final ContainerAuthSettings _value;
|
||||
|
||||
@override
|
||||
ContainerAuthSettings authenticationRequired(bool authenticationRequired) =>
|
||||
this(authenticationRequired: authenticationRequired);
|
||||
|
||||
@override
|
||||
ContainerAuthSettings lockOnAppBackground(bool lockOnAppBackground) =>
|
||||
this(lockOnAppBackground: lockOnAppBackground);
|
||||
|
||||
@override
|
||||
ContainerAuthSettings lockTimeout(Duration? lockTimeout) =>
|
||||
this(lockTimeout: lockTimeout);
|
||||
|
||||
@override
|
||||
|
||||
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `ContainerAuthSettings(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
||||
///
|
||||
/// Usage
|
||||
/// ```dart
|
||||
/// ContainerAuthSettings(...).copyWith(id: 12, name: "My name")
|
||||
/// ````
|
||||
ContainerAuthSettings call({
|
||||
Object? authenticationRequired = const $CopyWithPlaceholder(),
|
||||
Object? lockOnAppBackground = const $CopyWithPlaceholder(),
|
||||
Object? lockTimeout = const $CopyWithPlaceholder(),
|
||||
}) {
|
||||
return ContainerAuthSettings(
|
||||
authenticationRequired:
|
||||
authenticationRequired == const $CopyWithPlaceholder()
|
||||
? _value.authenticationRequired
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: authenticationRequired as bool,
|
||||
lockOnAppBackground: lockOnAppBackground == const $CopyWithPlaceholder()
|
||||
? _value.lockOnAppBackground
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: lockOnAppBackground as bool,
|
||||
lockTimeout: lockTimeout == const $CopyWithPlaceholder()
|
||||
? _value.lockTimeout
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: lockTimeout as Duration?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension $ContainerAuthSettingsCopyWith on ContainerAuthSettings {
|
||||
/// Returns a callable class that can be used as follows: `instanceOfContainerAuthSettings.copyWith(...)` or like so:`instanceOfContainerAuthSettings.copyWith.fieldName(...)`.
|
||||
// ignore: library_private_types_in_public_api
|
||||
_$ContainerAuthSettingsCWProxy get copyWith =>
|
||||
_$ContainerAuthSettingsCWProxyImpl(this);
|
||||
}
|
||||
|
||||
abstract class _$ContainerMetadataCWProxy {
|
||||
ContainerMetadata iconData(IconData? iconData);
|
||||
|
||||
ContainerMetadata contextualIdentity(String? contextualIdentity);
|
||||
|
||||
ContainerMetadata authSettings(ContainerAuthSettings authSettings);
|
||||
|
||||
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `ContainerMetadata(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
||||
///
|
||||
/// Usage
|
||||
/// ```dart
|
||||
/// ContainerMetadata(...).copyWith(id: 12, name: "My name")
|
||||
/// ````
|
||||
ContainerMetadata call({
|
||||
IconData? iconData,
|
||||
String? contextualIdentity,
|
||||
ContainerAuthSettings authSettings,
|
||||
});
|
||||
}
|
||||
|
||||
/// Proxy class for `copyWith` functionality. This is a callable class and can be used as follows: `instanceOfContainerMetadata.copyWith(...)`. Additionally contains functions for specific fields e.g. `instanceOfContainerMetadata.copyWith.fieldName(...)`
|
||||
class _$ContainerMetadataCWProxyImpl implements _$ContainerMetadataCWProxy {
|
||||
const _$ContainerMetadataCWProxyImpl(this._value);
|
||||
|
||||
final ContainerMetadata _value;
|
||||
|
||||
@override
|
||||
ContainerMetadata iconData(IconData? iconData) => this(iconData: iconData);
|
||||
|
||||
@override
|
||||
ContainerMetadata contextualIdentity(String? contextualIdentity) =>
|
||||
this(contextualIdentity: contextualIdentity);
|
||||
|
||||
@override
|
||||
ContainerMetadata authSettings(ContainerAuthSettings authSettings) =>
|
||||
this(authSettings: authSettings);
|
||||
|
||||
@override
|
||||
|
||||
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `ContainerMetadata(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
||||
///
|
||||
/// Usage
|
||||
/// ```dart
|
||||
/// ContainerMetadata(...).copyWith(id: 12, name: "My name")
|
||||
/// ````
|
||||
ContainerMetadata call({
|
||||
Object? iconData = const $CopyWithPlaceholder(),
|
||||
Object? contextualIdentity = const $CopyWithPlaceholder(),
|
||||
Object? authSettings = const $CopyWithPlaceholder(),
|
||||
}) {
|
||||
return ContainerMetadata(
|
||||
iconData: iconData == const $CopyWithPlaceholder()
|
||||
? _value.iconData
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: iconData as IconData?,
|
||||
contextualIdentity: contextualIdentity == const $CopyWithPlaceholder()
|
||||
? _value.contextualIdentity
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: contextualIdentity as String?,
|
||||
authSettings: authSettings == const $CopyWithPlaceholder()
|
||||
? _value.authSettings
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: authSettings as ContainerAuthSettings,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension $ContainerMetadataCopyWith on ContainerMetadata {
|
||||
/// Returns a callable class that can be used as follows: `instanceOfContainerMetadata.copyWith(...)` or like so:`instanceOfContainerMetadata.copyWith.fieldName(...)`.
|
||||
// ignore: library_private_types_in_public_api
|
||||
_$ContainerMetadataCWProxy get copyWith =>
|
||||
_$ContainerMetadataCWProxyImpl(this);
|
||||
}
|
||||
|
||||
abstract class _$ContainerDataCWProxy {
|
||||
ContainerData id(String id);
|
||||
|
||||
ContainerData name(String? name);
|
||||
|
||||
ContainerData color(Color color);
|
||||
|
||||
ContainerData metadata(ContainerMetadata? metadata);
|
||||
|
||||
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `ContainerData(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
||||
///
|
||||
/// Usage
|
||||
/// ```dart
|
||||
/// ContainerData(...).copyWith(id: 12, name: "My name")
|
||||
/// ````
|
||||
ContainerData call({
|
||||
String id,
|
||||
String? name,
|
||||
Color color,
|
||||
ContainerMetadata? metadata,
|
||||
});
|
||||
}
|
||||
|
||||
/// Proxy class for `copyWith` functionality. This is a callable class and can be used as follows: `instanceOfContainerData.copyWith(...)`. Additionally contains functions for specific fields e.g. `instanceOfContainerData.copyWith.fieldName(...)`
|
||||
class _$ContainerDataCWProxyImpl implements _$ContainerDataCWProxy {
|
||||
const _$ContainerDataCWProxyImpl(this._value);
|
||||
|
||||
final ContainerData _value;
|
||||
|
||||
@override
|
||||
ContainerData id(String id) => this(id: id);
|
||||
|
||||
@override
|
||||
ContainerData name(String? name) => this(name: name);
|
||||
|
||||
@override
|
||||
ContainerData color(Color color) => this(color: color);
|
||||
|
||||
@override
|
||||
ContainerData metadata(ContainerMetadata? metadata) =>
|
||||
this(metadata: metadata);
|
||||
|
||||
@override
|
||||
|
||||
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `ContainerData(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
||||
///
|
||||
/// Usage
|
||||
/// ```dart
|
||||
/// ContainerData(...).copyWith(id: 12, name: "My name")
|
||||
/// ````
|
||||
ContainerData call({
|
||||
Object? id = const $CopyWithPlaceholder(),
|
||||
Object? name = const $CopyWithPlaceholder(),
|
||||
Object? color = const $CopyWithPlaceholder(),
|
||||
Object? metadata = const $CopyWithPlaceholder(),
|
||||
}) {
|
||||
return ContainerData(
|
||||
id: id == const $CopyWithPlaceholder()
|
||||
? _value.id
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: id as String,
|
||||
name: name == const $CopyWithPlaceholder()
|
||||
? _value.name
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: name as String?,
|
||||
color: color == const $CopyWithPlaceholder()
|
||||
? _value.color
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: color as Color,
|
||||
metadata: metadata == const $CopyWithPlaceholder()
|
||||
? _value.metadata
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: metadata as ContainerMetadata?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension $ContainerDataCopyWith on ContainerData {
|
||||
/// Returns a callable class that can be used as follows: `instanceOfContainerData.copyWith(...)` or like so:`instanceOfContainerData.copyWith.fieldName(...)`.
|
||||
// ignore: library_private_types_in_public_api
|
||||
_$ContainerDataCWProxy get copyWith => _$ContainerDataCWProxyImpl(this);
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
ContainerAuthSettings _$ContainerAuthSettingsFromJson(
|
||||
Map<String, dynamic> json) =>
|
||||
ContainerAuthSettings(
|
||||
authenticationRequired: json['authenticationRequired'] as bool,
|
||||
lockOnAppBackground: json['lockOnAppBackground'] as bool,
|
||||
lockTimeout: json['lockTimeout'] == null
|
||||
? null
|
||||
: Duration(microseconds: (json['lockTimeout'] as num).toInt()),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ContainerAuthSettingsToJson(
|
||||
ContainerAuthSettings instance) =>
|
||||
<String, dynamic>{
|
||||
'authenticationRequired': instance.authenticationRequired,
|
||||
'lockOnAppBackground': instance.lockOnAppBackground,
|
||||
'lockTimeout': instance.lockTimeout?.inMicroseconds,
|
||||
};
|
||||
|
||||
ContainerMetadata _$ContainerMetadataFromJson(Map<String, dynamic> json) =>
|
||||
ContainerMetadata.withDefaults(
|
||||
iconData: _$JsonConverterFromJson<Map<String, dynamic>, IconData>(
|
||||
json['iconData'], const IconDataJsonConverter().fromJson),
|
||||
contextualIdentity: json['contextualIdentity'] as String?,
|
||||
authSettings: json['authSettings'] == null
|
||||
? null
|
||||
: ContainerAuthSettings.fromJson(
|
||||
json['authSettings'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ContainerMetadataToJson(ContainerMetadata instance) =>
|
||||
<String, dynamic>{
|
||||
'iconData': _$JsonConverterToJson<Map<String, dynamic>, IconData>(
|
||||
instance.iconData, const IconDataJsonConverter().toJson),
|
||||
'contextualIdentity': instance.contextualIdentity,
|
||||
'authSettings': instance.authSettings,
|
||||
};
|
||||
|
||||
Value? _$JsonConverterFromJson<Json, Value>(
|
||||
Object? json,
|
||||
Value? Function(Json json) fromJson,
|
||||
) =>
|
||||
json == null ? null : fromJson(json as Json);
|
||||
|
||||
Json? _$JsonConverterToJson<Json, Value>(
|
||||
Value? value,
|
||||
Json? Function(Value value) toJson,
|
||||
) =>
|
||||
value == null ? null : toJson(value);
|
||||
@@ -58,12 +58,6 @@ AsyncValue<List<ContainerDataWithCount>> filteredContainersWithCount(
|
||||
);
|
||||
}
|
||||
|
||||
@Riverpod()
|
||||
Stream<String?> tabContainerId(Ref ref, String tabId) {
|
||||
final db = ref.watch(tabDatabaseProvider);
|
||||
return db.tabDao.tabContainerId(tabId).watchSingle();
|
||||
}
|
||||
|
||||
@Riverpod()
|
||||
Stream<List<String>> containerTabIds(
|
||||
Ref ref,
|
||||
|
||||
@@ -206,136 +206,6 @@ class _FilteredContainersWithCountProviderElement
|
||||
(origin as FilteredContainersWithCountProvider).searchText;
|
||||
}
|
||||
|
||||
String _$tabContainerIdHash() => r'726ddd000d23935a2c8ae39c7957412fab16016d';
|
||||
|
||||
/// See also [tabContainerId].
|
||||
@ProviderFor(tabContainerId)
|
||||
const tabContainerIdProvider = TabContainerIdFamily();
|
||||
|
||||
/// See also [tabContainerId].
|
||||
class TabContainerIdFamily extends Family<AsyncValue<String?>> {
|
||||
/// See also [tabContainerId].
|
||||
const TabContainerIdFamily();
|
||||
|
||||
/// See also [tabContainerId].
|
||||
TabContainerIdProvider call(
|
||||
String tabId,
|
||||
) {
|
||||
return TabContainerIdProvider(
|
||||
tabId,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TabContainerIdProvider getProviderOverride(
|
||||
covariant TabContainerIdProvider provider,
|
||||
) {
|
||||
return call(
|
||||
provider.tabId,
|
||||
);
|
||||
}
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||
_allTransitiveDependencies;
|
||||
|
||||
@override
|
||||
String? get name => r'tabContainerIdProvider';
|
||||
}
|
||||
|
||||
/// See also [tabContainerId].
|
||||
class TabContainerIdProvider extends AutoDisposeStreamProvider<String?> {
|
||||
/// See also [tabContainerId].
|
||||
TabContainerIdProvider(
|
||||
String tabId,
|
||||
) : this._internal(
|
||||
(ref) => tabContainerId(
|
||||
ref as TabContainerIdRef,
|
||||
tabId,
|
||||
),
|
||||
from: tabContainerIdProvider,
|
||||
name: r'tabContainerIdProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$tabContainerIdHash,
|
||||
dependencies: TabContainerIdFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
TabContainerIdFamily._allTransitiveDependencies,
|
||||
tabId: tabId,
|
||||
);
|
||||
|
||||
TabContainerIdProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.tabId,
|
||||
}) : super.internal();
|
||||
|
||||
final String tabId;
|
||||
|
||||
@override
|
||||
Override overrideWith(
|
||||
Stream<String?> Function(TabContainerIdRef provider) create,
|
||||
) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: TabContainerIdProvider._internal(
|
||||
(ref) => create(ref as TabContainerIdRef),
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
tabId: tabId,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeStreamProviderElement<String?> createElement() {
|
||||
return _TabContainerIdProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is TabContainerIdProvider && other.tabId == tabId;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, tabId.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin TabContainerIdRef on AutoDisposeStreamProviderRef<String?> {
|
||||
/// The parameter `tabId` of this provider.
|
||||
String get tabId;
|
||||
}
|
||||
|
||||
class _TabContainerIdProviderElement
|
||||
extends AutoDisposeStreamProviderElement<String?> with TabContainerIdRef {
|
||||
_TabContainerIdProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
String get tabId => (origin as TabContainerIdProvider).tabId;
|
||||
}
|
||||
|
||||
String _$containerTabIdsHash() => r'7545a6c500b1832bf81c0838e257dfe5e051463d';
|
||||
|
||||
/// See also [containerTabIds].
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/models/container_data.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/providers.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/domain/providers.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/domain/repositories/container.dart';
|
||||
import 'package:lensai/features/user/domain/services/local_authentication.dart';
|
||||
import 'package:riverpod/riverpod.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
@@ -7,15 +10,47 @@ part 'selected_container.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class SelectedContainer extends _$SelectedContainer {
|
||||
void setContainerId(String id) {
|
||||
state = id;
|
||||
Future<ContainerData?> fetchData() async {
|
||||
if (state != null) {
|
||||
return ref
|
||||
.read(containerRepositoryProvider.notifier)
|
||||
.getContainerData(state!);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
void toggleContainer(String id) {
|
||||
Future<void> setContainerId(String id) async {
|
||||
final container = await ref
|
||||
.read(containerRepositoryProvider.notifier)
|
||||
.getContainerData(id);
|
||||
|
||||
if (container != null) {
|
||||
if (container.metadata.authSettings.authenticationRequired) {
|
||||
final authResult = await ref
|
||||
.read(localAuthenticationServiceProvider.notifier)
|
||||
.authenticate(
|
||||
authKey: 'container_access::${container.id}',
|
||||
localizedReason:
|
||||
'Require authentication for container ${container.name ?? 'New Container'}',
|
||||
settings: container.metadata.authSettings,
|
||||
useAuthCache: true,
|
||||
);
|
||||
|
||||
if (authResult) {
|
||||
state = id;
|
||||
}
|
||||
} else {
|
||||
state = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> toggleContainer(String id) async {
|
||||
if (state == id) {
|
||||
clearContainer();
|
||||
} else {
|
||||
setContainerId(id);
|
||||
await setContainerId(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +60,17 @@ class SelectedContainer extends _$SelectedContainer {
|
||||
|
||||
@override
|
||||
String? build() {
|
||||
ref.listen(
|
||||
containersWithCountProvider,
|
||||
(previous, next) {
|
||||
if (state != null && next.valueOrNull != null) {
|
||||
if (!next.value!.any((container) => container.id == state)) {
|
||||
clearContainer();
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ final selectedContainerDataProvider =
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef SelectedContainerDataRef = AutoDisposeStreamProviderRef<ContainerData?>;
|
||||
String _$selectedContainerHash() => r'e38e86db5bd0584af9561156c26ceaea3d23aebf';
|
||||
String _$selectedContainerHash() => r'753f30646c4992981ec538dcfd33fa23aa1f673d';
|
||||
|
||||
/// See also [SelectedContainer].
|
||||
@ProviderFor(SelectedContainer)
|
||||
|
||||
@@ -1,54 +1,72 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/database/database.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/models/container_data.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/providers.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/domain/repositories/tab.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
part 'container.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class ContainerRepository extends _$ContainerRepository {
|
||||
late TabDatabase _db;
|
||||
|
||||
@override
|
||||
void build() {
|
||||
_db = ref.watch(tabDatabaseProvider);
|
||||
Future<void> addContainer(ContainerData container) {
|
||||
return ref.read(tabDatabaseProvider).containerDao.addContainer(container);
|
||||
}
|
||||
|
||||
Future<void> addContainer({required String? name, required Color color}) {
|
||||
return _db.containerDao.addContainer(name: name, color: color);
|
||||
Future<void> replaceContainer(ContainerData container) {
|
||||
return ref
|
||||
.read(tabDatabaseProvider)
|
||||
.containerDao
|
||||
.replaceContainer(container);
|
||||
}
|
||||
|
||||
Future<void> replaceContainer({
|
||||
required String id,
|
||||
required String? name,
|
||||
required Color color,
|
||||
}) {
|
||||
return _db.containerDao.replaceContainer(id, name: name, color: color);
|
||||
Future<ContainerData?> getContainerData(String id) async {
|
||||
return ref
|
||||
.read(tabDatabaseProvider)
|
||||
.containerDao
|
||||
.getContainerData(id)
|
||||
.getSingleOrNull();
|
||||
}
|
||||
|
||||
Future<void> deleteContainer(String id) {
|
||||
return _db.containerDao.deleteContainer(id);
|
||||
Future<void> deleteContainer(String id) async {
|
||||
await ref.read(tabDataRepositoryProvider.notifier).closeAllTabs(id);
|
||||
|
||||
return ref.read(tabDatabaseProvider).containerDao.deleteContainer(id);
|
||||
}
|
||||
|
||||
Future<Set<Color>> getDistinctColors() {
|
||||
return _db.containerDao
|
||||
return ref
|
||||
.read(tabDatabaseProvider)
|
||||
.containerDao
|
||||
.getDistinctColors()
|
||||
.get()
|
||||
.then((colors) => colors.toSet());
|
||||
}
|
||||
|
||||
Future<String> getLeadingOrderKey(String? containerId) {
|
||||
return _db.containerDao.generateLeadingOrderKey(containerId).getSingle();
|
||||
return ref
|
||||
.read(tabDatabaseProvider)
|
||||
.containerDao
|
||||
.generateLeadingOrderKey(containerId)
|
||||
.getSingle();
|
||||
}
|
||||
|
||||
Future<String> getTrailingOrderKey(String? containerId) {
|
||||
return _db.containerDao.generateTrailingOrderKey(containerId).getSingle();
|
||||
return ref
|
||||
.read(tabDatabaseProvider)
|
||||
.containerDao
|
||||
.generateTrailingOrderKey(containerId)
|
||||
.getSingle();
|
||||
}
|
||||
|
||||
Future<String> getOrderKeyAfterTab(String tabId, String? containerId) {
|
||||
return _db.containerDao
|
||||
return ref
|
||||
.read(tabDatabaseProvider)
|
||||
.containerDao
|
||||
.generateOrderKeyAfterTabId(containerId, tabId)
|
||||
.getSingle();
|
||||
}
|
||||
|
||||
@override
|
||||
void build() {}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ part of 'container.dart';
|
||||
// **************************************************************************
|
||||
|
||||
String _$containerRepositoryHash() =>
|
||||
r'89ad15861fb213d6a3fa51232fd0ff9943f51e92';
|
||||
r'b965883f2279b6f0148592bbac5ff40a60798f51';
|
||||
|
||||
/// See also [ContainerRepository].
|
||||
@ProviderFor(ContainerRepository)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:lensai/features/geckoview/domain/repositories/tab.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';
|
||||
|
||||
@@ -7,35 +6,40 @@ part 'tab.g.dart';
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class TabDataRepository extends _$TabDataRepository {
|
||||
late TabDatabase _db;
|
||||
|
||||
@override
|
||||
void build() {
|
||||
_db = ref.watch(tabDatabaseProvider);
|
||||
}
|
||||
void build() {}
|
||||
|
||||
Future<void> assignContainer(String tabId, String? containerId) {
|
||||
return _db.tabDao.assignContainer(
|
||||
tabId,
|
||||
containerId: containerId,
|
||||
);
|
||||
return ref.read(tabDatabaseProvider).tabDao.assignContainer(
|
||||
tabId,
|
||||
containerId: containerId,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> assignOrderKey(String tabId, String orderKey) {
|
||||
return _db.tabDao.assignOrderKey(
|
||||
tabId,
|
||||
orderKey: orderKey,
|
||||
);
|
||||
return ref.read(tabDatabaseProvider).tabDao.assignOrderKey(
|
||||
tabId,
|
||||
orderKey: orderKey,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> closeAllTabs(String? containerId) async {
|
||||
final tabIds = await _db.tabDao.containerTabIds(containerId).get();
|
||||
final tabIds = await ref
|
||||
.read(tabDatabaseProvider)
|
||||
.tabDao
|
||||
.containerTabIds(containerId)
|
||||
.get();
|
||||
|
||||
if (tabIds.isNotEmpty) {
|
||||
await ref.read(tabRepositoryProvider.notifier).closeTabs(tabIds);
|
||||
}
|
||||
}
|
||||
|
||||
Future<String?> containerTabId(String tabId) {
|
||||
return _db.tabDao.tabContainerId(tabId).getSingle();
|
||||
return ref
|
||||
.read(tabDatabaseProvider)
|
||||
.tabDao
|
||||
.tabContainerId(tabId)
|
||||
.getSingleOrNull();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ part of 'tab.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$tabDataRepositoryHash() => r'c067bbbf319cd95ad869655740fdf49d13159b94';
|
||||
String _$tabDataRepositoryHash() => r'30ae87fbcb37facd565cb28de605814cd7db4ce8';
|
||||
|
||||
/// See also [TabDataRepository].
|
||||
@ProviderFor(TabDataRepository)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/database/database.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/models/tab_query_result.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/providers.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
@@ -9,8 +8,6 @@ part 'tab_search.g.dart';
|
||||
|
||||
@Riverpod()
|
||||
class TabSearchRepository extends _$TabSearchRepository {
|
||||
late TabDatabase _db;
|
||||
|
||||
Future<void> addQuery(
|
||||
String input, {
|
||||
int snippetLength = 120,
|
||||
@@ -20,7 +17,9 @@ class TabSearchRepository extends _$TabSearchRepository {
|
||||
}) async {
|
||||
if (input.isNotEmpty) {
|
||||
state = await AsyncValue.guard(
|
||||
() => _db.tabDao
|
||||
() => ref
|
||||
.read(tabDatabaseProvider)
|
||||
.tabDao
|
||||
.queryTabs(
|
||||
matchPrefix: matchPrefix,
|
||||
matchSuffix: matchSuffix,
|
||||
@@ -37,8 +36,6 @@ class TabSearchRepository extends _$TabSearchRepository {
|
||||
|
||||
@override
|
||||
Future<List<TabQueryResult>?> build() {
|
||||
_db = ref.watch(tabDatabaseProvider);
|
||||
|
||||
return Future.value();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ part of 'tab_search.dart';
|
||||
// **************************************************************************
|
||||
|
||||
String _$tabSearchRepositoryHash() =>
|
||||
r'9bd42f89bcc3a8d94add093076737dd57383bae2';
|
||||
r'57d130f50aa3feb7a31d27f9a5b54234e0e7064f';
|
||||
|
||||
/// See also [TabSearchRepository].
|
||||
@ProviderFor(TabSearchRepository)
|
||||
|
||||
+16
-15
@@ -6,6 +6,7 @@ import 'package:langchain/langchain.dart' as langchain;
|
||||
import 'package:langchain/langchain.dart';
|
||||
import 'package:langchain_openai/langchain_openai.dart';
|
||||
import 'package:lensai/core/models.dart';
|
||||
import 'package:lensai/extensions/nullable.dart';
|
||||
import 'package:lensai/features/geckoview/domain/providers.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/providers.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/features/vector_store/data/database/daos/vector.dart';
|
||||
@@ -41,21 +42,21 @@ class DocumentRepository extends _$DocumentRepository {
|
||||
chunkOverlap: chunkOverlap,
|
||||
);
|
||||
|
||||
final splittedWithSource = (splitted != null)
|
||||
? (
|
||||
mainDocumentId: splitted.mainDocumentId,
|
||||
parts: splitted.parts
|
||||
.map(
|
||||
(part) => part.copyWith(
|
||||
metadata: {
|
||||
...part.metadata,
|
||||
'source': part.id,
|
||||
},
|
||||
),
|
||||
)
|
||||
.toList()
|
||||
)
|
||||
: null;
|
||||
final splittedWithSource = splitted.mapNotNull(
|
||||
(splitted) => (
|
||||
mainDocumentId: splitted.mainDocumentId,
|
||||
parts: splitted.parts
|
||||
.map(
|
||||
(part) => part.copyWith(
|
||||
metadata: {
|
||||
...part.metadata,
|
||||
'source': part.id,
|
||||
},
|
||||
),
|
||||
)
|
||||
.toList()
|
||||
),
|
||||
);
|
||||
|
||||
await _vectorDao.insertDocuments(
|
||||
splittedWithSource?.parts ?? [doc],
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ part of 'document.dart';
|
||||
// **************************************************************************
|
||||
|
||||
String _$documentRepositoryHash() =>
|
||||
r'0c93116e0f9887d452b7c493dd4da45e5bf0c2d4';
|
||||
r'60e5a0c889d9659c8e0e254c59225c2041a0cb56';
|
||||
|
||||
/// See also [DocumentRepository].
|
||||
@ProviderFor(DocumentRepository)
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:lensai/core/uuid.dart';
|
||||
import 'package:lensai/extensions/nullable.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/models/container_data.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/domain/repositories/container.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/presentation/widgets/color_picker_dialog.dart';
|
||||
import 'package:lensai/features/user/domain/services/local_authentication.dart';
|
||||
|
||||
enum _DialogMode { create, edit }
|
||||
|
||||
const _timeoutOptions = <DropdownMenuItem<Duration?>>[
|
||||
DropdownMenuItem(
|
||||
child: Text('Immediately'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: Duration(minutes: 1),
|
||||
child: Text('1 minute'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: Duration(minutes: 5),
|
||||
child: Text('5 minutes'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: Duration(minutes: 15),
|
||||
child: Text('15 minutes'),
|
||||
),
|
||||
DropdownMenuItem(
|
||||
value: Duration(hours: 1),
|
||||
child: Text('1 hour'),
|
||||
),
|
||||
];
|
||||
|
||||
class ContainerEditScreen extends HookConsumerWidget {
|
||||
final _DialogMode _mode;
|
||||
|
||||
final ContainerData initialContainer;
|
||||
|
||||
const ContainerEditScreen._({
|
||||
required _DialogMode mode,
|
||||
required this.initialContainer,
|
||||
}) : _mode = mode;
|
||||
|
||||
factory ContainerEditScreen.create({
|
||||
required ContainerData initialContainer,
|
||||
}) {
|
||||
return ContainerEditScreen._(
|
||||
mode: _DialogMode.create,
|
||||
initialContainer: initialContainer,
|
||||
);
|
||||
}
|
||||
|
||||
factory ContainerEditScreen.edit({required ContainerData initialContainer}) {
|
||||
return ContainerEditScreen._(
|
||||
mode: _DialogMode.edit,
|
||||
initialContainer: initialContainer,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final selectedColor = useState(initialContainer.color);
|
||||
final contextualIdentity =
|
||||
useState(initialContainer.metadata.contextualIdentity);
|
||||
final authSettings = useState(initialContainer.metadata.authSettings);
|
||||
|
||||
final textController =
|
||||
useTextEditingController(text: initialContainer.name);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
switch (_mode) {
|
||||
_DialogMode.create => 'New Container',
|
||||
_DialogMode.edit => 'Edit Container',
|
||||
},
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
final name = textController.text.trim();
|
||||
final container = initialContainer.copyWith(
|
||||
name: name.isNotEmpty ? name : null,
|
||||
color: selectedColor.value,
|
||||
metadata: initialContainer.metadata.mapNotNull(
|
||||
(metadata) => metadata.copyWith(
|
||||
contextualIdentity: contextualIdentity.value,
|
||||
authSettings: authSettings.value,
|
||||
),
|
||||
) ??
|
||||
ContainerMetadata(
|
||||
iconData: null,
|
||||
contextualIdentity: contextualIdentity.value,
|
||||
authSettings: authSettings.value,
|
||||
),
|
||||
);
|
||||
|
||||
//Check for permissions, when auth is set or getting set
|
||||
if (initialContainer
|
||||
.metadata.authSettings.authenticationRequired ||
|
||||
container.metadata.authSettings.authenticationRequired) {
|
||||
final authResult = await ref
|
||||
.read(localAuthenticationServiceProvider.notifier)
|
||||
.authenticate(
|
||||
authKey: 'container_access::${container.id}',
|
||||
localizedReason:
|
||||
'Require authentication for container ${container.name ?? 'New Container'}',
|
||||
);
|
||||
|
||||
if (!authResult) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (_mode) {
|
||||
case _DialogMode.create:
|
||||
await ref
|
||||
.read(containerRepositoryProvider.notifier)
|
||||
.addContainer(container);
|
||||
case _DialogMode.edit:
|
||||
await ref
|
||||
.read(containerRepositoryProvider.notifier)
|
||||
.replaceContainer(container);
|
||||
}
|
||||
|
||||
if (context.mounted) {
|
||||
context.pop();
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.check),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: [
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
height: 24,
|
||||
width: 24,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: selectedColor.value,
|
||||
),
|
||||
),
|
||||
),
|
||||
label: const Text('Name'),
|
||||
),
|
||||
controller: textController,
|
||||
),
|
||||
TextButton.icon(
|
||||
label: const Text('Select Color'),
|
||||
icon: const Icon(Icons.colorize),
|
||||
onPressed: () async {
|
||||
final color = await showDialog<Color?>(
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
ColorPickerDialog(selectedColor.value),
|
||||
);
|
||||
|
||||
if (color != null) {
|
||||
selectedColor.value = color;
|
||||
}
|
||||
},
|
||||
),
|
||||
SwitchListTile.adaptive(
|
||||
value: contextualIdentity.value != null,
|
||||
title: const Text('Cookie Isolation'),
|
||||
secondary: const Icon(MdiIcons.cookieLock),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
onChanged: (_mode == _DialogMode.create)
|
||||
? (value) {
|
||||
contextualIdentity.value = value
|
||||
? initialContainer
|
||||
.metadata.contextualIdentity ??
|
||||
uuid.v4()
|
||||
: null;
|
||||
}
|
||||
: null,
|
||||
),
|
||||
SwitchListTile.adaptive(
|
||||
value: authSettings.value.authenticationRequired,
|
||||
title: const Text('Require Authentication'),
|
||||
secondary: const Icon(MdiIcons.fingerprint),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
onChanged: (value) {
|
||||
authSettings.value = authSettings.value.copyWith
|
||||
.authenticationRequired(value);
|
||||
},
|
||||
),
|
||||
if (authSettings.value.authenticationRequired)
|
||||
CheckboxListTile.adaptive(
|
||||
value: authSettings.value.lockOnAppBackground,
|
||||
title: const Text(
|
||||
'Auto-lock on background',
|
||||
),
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
onChanged: (value) {
|
||||
authSettings.value = authSettings.value.copyWith
|
||||
.lockOnAppBackground(value!);
|
||||
},
|
||||
),
|
||||
if (authSettings.value.authenticationRequired)
|
||||
CheckboxListTile.adaptive(
|
||||
value: authSettings.value.lockTimeout != null,
|
||||
title: const Text(
|
||||
'Timeout',
|
||||
),
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
onChanged: (value) {
|
||||
final newValue =
|
||||
value! ? _timeoutOptions[1].value : null;
|
||||
|
||||
authSettings.value =
|
||||
authSettings.value.copyWith.lockTimeout(newValue);
|
||||
},
|
||||
secondary: DropdownButton(
|
||||
value: authSettings.value.lockTimeout,
|
||||
items: _timeoutOptions,
|
||||
onChanged: (value) {
|
||||
authSettings.value =
|
||||
authSettings.value.copyWith.lockTimeout(value);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (_mode == _DialogMode.edit)
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: OutlinedButton.icon(
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: BorderSide(
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
foregroundColor: Theme.of(context).colorScheme.error,
|
||||
iconColor: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
label: const Text('Delete'),
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: () async {
|
||||
final result = await showDialog<bool?>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Delete Container'),
|
||||
content: const Text(
|
||||
'Are you sure you want to delete this container and close all attached tabs?',
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context, false);
|
||||
},
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context, true);
|
||||
},
|
||||
child: const Text('Delete'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (result == true) {
|
||||
await ref
|
||||
.read(containerRepositoryProvider.notifier)
|
||||
.deleteContainer(initialContainer.id);
|
||||
|
||||
if (context.mounted) {
|
||||
context.pop();
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
+39
-139
@@ -1,119 +1,17 @@
|
||||
import 'package:fading_scroll/fading_scroll.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:lensai/core/routing/routes.dart';
|
||||
import 'package:lensai/core/uuid.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/models/container_data.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/domain/providers.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/domain/providers/selected_container.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/domain/repositories/container.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/presentation/widgets/container_dialog.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/presentation/widgets/container_list_tile.dart';
|
||||
import 'package:skeletonizer/skeletonizer.dart';
|
||||
|
||||
class _ContainerTile extends HookWidget {
|
||||
final ContainerData container;
|
||||
final bool isSelected;
|
||||
|
||||
final void Function(ContainerResult edited) onEdit;
|
||||
final void Function() onDelete;
|
||||
final void Function() onTap;
|
||||
|
||||
const _ContainerTile(
|
||||
this.container, {
|
||||
required this.isSelected,
|
||||
required this.onEdit,
|
||||
required this.onDelete,
|
||||
required this.onTap,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final menuController = useMemoized(() => MenuController());
|
||||
|
||||
return ListTile(
|
||||
selected: isSelected,
|
||||
leading: CircleAvatar(backgroundColor: container.color),
|
||||
title: Text(container.name ?? 'New Container'),
|
||||
trailing: MenuAnchor(
|
||||
controller: menuController,
|
||||
builder: (context, controller, child) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 4.0),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (controller.isOpen) {
|
||||
controller.close();
|
||||
} else {
|
||||
controller.open();
|
||||
}
|
||||
},
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 15.0, horizontal: 8.0),
|
||||
child: Icon(Icons.more_vert),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
menuChildren: [
|
||||
MenuItemButton(
|
||||
onPressed: () async {
|
||||
final result = await showDialog<ContainerResult?>(
|
||||
context: context,
|
||||
builder: (context) => ContainerDialog.edit(
|
||||
name: container.name,
|
||||
initialColor: container.color,
|
||||
),
|
||||
);
|
||||
|
||||
if (result != null) {
|
||||
onEdit(result);
|
||||
}
|
||||
},
|
||||
leadingIcon: const Icon(Icons.edit),
|
||||
child: const Text('Edit'),
|
||||
),
|
||||
MenuItemButton(
|
||||
onPressed: () async {
|
||||
final result = await showDialog<bool?>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Delete Container'),
|
||||
content: const Text(
|
||||
'Are you sure you want to delete this container and close all attached tabs?',
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context, false);
|
||||
},
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context, true);
|
||||
},
|
||||
child: const Text('Delete'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (result == true) {
|
||||
onDelete();
|
||||
}
|
||||
},
|
||||
leadingIcon: const Icon(Icons.delete),
|
||||
child: const Text('Delete'),
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: onTap,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerListScreen extends HookConsumerWidget {
|
||||
const ContainerListScreen();
|
||||
|
||||
@@ -139,29 +37,36 @@ class ContainerListScreen extends HookConsumerWidget {
|
||||
itemCount: containers.length,
|
||||
itemBuilder: (context, index) {
|
||||
final container = containers[index];
|
||||
return _ContainerTile(
|
||||
container,
|
||||
return Slidable(
|
||||
key: ValueKey(container.id),
|
||||
isSelected: container.id == selectedContainer,
|
||||
onEdit: (edited) async {
|
||||
await ref
|
||||
.read(containerRepositoryProvider.notifier)
|
||||
.replaceContainer(
|
||||
id: container.id,
|
||||
name: edited.name,
|
||||
color: edited.color,
|
||||
);
|
||||
},
|
||||
onDelete: () async {
|
||||
await ref
|
||||
.read(containerRepositoryProvider.notifier)
|
||||
.deleteContainer(container.id);
|
||||
},
|
||||
onTap: () {
|
||||
ref
|
||||
.read(selectedContainerProvider.notifier)
|
||||
.toggleContainer(container.id);
|
||||
},
|
||||
endActionPane: ActionPane(
|
||||
motion: const ScrollMotion(),
|
||||
children: [
|
||||
SlidableAction(
|
||||
onPressed: (context) async {
|
||||
await ref
|
||||
.read(containerRepositoryProvider.notifier)
|
||||
.deleteContainer(container.id);
|
||||
},
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.errorContainer,
|
||||
foregroundColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.onErrorContainer,
|
||||
icon: Icons.delete,
|
||||
label: 'Delete',
|
||||
),
|
||||
],
|
||||
),
|
||||
child: ContainerListTile(
|
||||
container,
|
||||
isSelected: container.id == selectedContainer,
|
||||
onTap: () async {
|
||||
await ref
|
||||
.read(selectedContainerProvider.notifier)
|
||||
.toggleContainer(container.id);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -170,12 +75,9 @@ class ContainerListScreen extends HookConsumerWidget {
|
||||
error: (error, stackTrace) => SizedBox.shrink(),
|
||||
loading: () => ListView.builder(
|
||||
itemCount: 3,
|
||||
itemBuilder: (context, index) => _ContainerTile(
|
||||
itemBuilder: (context, index) => ContainerListTile(
|
||||
ContainerData(id: 'null', color: Colors.transparent),
|
||||
isSelected: false,
|
||||
onEdit: (_) {},
|
||||
onDelete: () {},
|
||||
onTap: () {},
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -188,17 +90,15 @@ class ContainerListScreen extends HookConsumerWidget {
|
||||
await ref.read(unusedRandomContainerColorProvider.future);
|
||||
|
||||
if (context.mounted) {
|
||||
final result = await showDialog<ContainerResult?>(
|
||||
context: context,
|
||||
builder: (context) => ContainerDialog.create(
|
||||
initialColor: initialColor,
|
||||
),
|
||||
final result = await context.push<ContainerData?>(
|
||||
ContainerCreateRoute().location,
|
||||
extra: ContainerData(id: uuid.v7(), color: initialColor),
|
||||
);
|
||||
|
||||
if (result != null) {
|
||||
await ref
|
||||
.read(containerRepositoryProvider.notifier)
|
||||
.addContainer(name: result.name, color: result.color);
|
||||
.addContainer(result);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/presentation/widgets/material_color_picker.dart';
|
||||
|
||||
class ColorPickerDialog extends HookWidget {
|
||||
final Color initialColor;
|
||||
|
||||
const ColorPickerDialog(this.initialColor);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final selectedColor = useState<Color>(initialColor);
|
||||
|
||||
return AlertDialog(
|
||||
titlePadding: const EdgeInsets.fromLTRB(24.0, 24.0, 24.0, 16.0),
|
||||
contentPadding: const EdgeInsets.only(
|
||||
left: 20.0,
|
||||
right: 20.0,
|
||||
bottom: 24.0,
|
||||
),
|
||||
insetPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 20.0,
|
||||
vertical: 24.0,
|
||||
),
|
||||
title: const Text('Select Color'),
|
||||
content: MaterialPicker(
|
||||
pickerColor: selectedColor.value,
|
||||
onColorChanged: (value) {
|
||||
selectedColor.value = value;
|
||||
},
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop<Color?>(context);
|
||||
},
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop<Color?>(context, selectedColor.value);
|
||||
},
|
||||
child: const Text('Select'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:lensai/core/providers.dart';
|
||||
import 'package:lensai/core/routing/routes.dart';
|
||||
import 'package:lensai/data/models/drag_data.dart';
|
||||
import 'package:lensai/extensions/nullable.dart';
|
||||
import 'package:lensai/features/geckoview/domain/repositories/tab.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/models/container_data.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/domain/providers.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/domain/repositories/container.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/domain/repositories/tab.dart';
|
||||
import 'package:lensai/presentation/hooks/overlay_portal_controller.dart';
|
||||
import 'package:lensai/presentation/widgets/selectable_chips.dart';
|
||||
@@ -90,12 +93,37 @@ class ContainerChips extends HookConsumerWidget {
|
||||
overlayController.hide();
|
||||
},
|
||||
onAcceptWithDetails: (details) async {
|
||||
await ref
|
||||
final containerId = await ref
|
||||
.read(tabDataRepositoryProvider.notifier)
|
||||
.assignContainer(
|
||||
details.data.tabId,
|
||||
container.id,
|
||||
);
|
||||
.containerTabId(details.data.tabId);
|
||||
|
||||
final containerData =
|
||||
await containerId.mapNotNull(
|
||||
(containerId) => ref
|
||||
.read(containerRepositoryProvider.notifier)
|
||||
.getContainerData(containerId),
|
||||
);
|
||||
|
||||
if (container.metadata.contextualIdentity ==
|
||||
containerData?.metadata.contextualIdentity) {
|
||||
await ref
|
||||
.read(tabDataRepositoryProvider.notifier)
|
||||
.assignContainer(
|
||||
details.data.tabId,
|
||||
container.id,
|
||||
);
|
||||
} else {
|
||||
await ref
|
||||
.read(tabRepositoryProvider.notifier)
|
||||
.duplicateTab(
|
||||
selectTabId: details.data.tabId,
|
||||
containerId: container.id,
|
||||
);
|
||||
|
||||
await ref
|
||||
.read(tabRepositoryProvider.notifier)
|
||||
.closeTab(details.data.tabId);
|
||||
}
|
||||
|
||||
dragTargetTabId.value = null;
|
||||
overlayController.hide();
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/presentation/widgets/material_color_picker.dart';
|
||||
|
||||
typedef ContainerResult = ({String? name, Color color});
|
||||
|
||||
enum _DialogMode { create, edit }
|
||||
|
||||
class ContainerDialog extends HookWidget {
|
||||
final _DialogMode _mode;
|
||||
|
||||
final String? initialName;
|
||||
final Color initialColor;
|
||||
|
||||
const ContainerDialog._({
|
||||
required _DialogMode mode,
|
||||
required this.initialColor,
|
||||
this.initialName,
|
||||
}) : _mode = mode;
|
||||
|
||||
factory ContainerDialog.create({required Color initialColor}) {
|
||||
return ContainerDialog._(
|
||||
mode: _DialogMode.create,
|
||||
initialColor: initialColor,
|
||||
);
|
||||
}
|
||||
|
||||
factory ContainerDialog.edit({
|
||||
required String? name,
|
||||
required Color initialColor,
|
||||
}) {
|
||||
return ContainerDialog._(
|
||||
mode: _DialogMode.edit,
|
||||
initialColor: initialColor,
|
||||
initialName: name,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final selectedColor = useState<Color>(initialColor);
|
||||
final textController = useTextEditingController(text: initialName);
|
||||
|
||||
return SimpleDialog(
|
||||
titlePadding: const EdgeInsets.fromLTRB(24.0, 24.0, 24.0, 16.0),
|
||||
contentPadding: const EdgeInsets.only(
|
||||
left: 20.0,
|
||||
right: 20.0,
|
||||
bottom: 24.0,
|
||||
),
|
||||
insetPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 20.0,
|
||||
vertical: 24.0,
|
||||
),
|
||||
title: Text(
|
||||
switch (_mode) {
|
||||
_DialogMode.create => 'New Container',
|
||||
_DialogMode.edit => 'Edit Container',
|
||||
},
|
||||
),
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
child: TextField(
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
height: 24,
|
||||
width: 24,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: selectedColor.value,
|
||||
),
|
||||
),
|
||||
),
|
||||
label: const Text('Name'),
|
||||
),
|
||||
controller: textController,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
MaterialPicker(
|
||||
pickerColor: selectedColor.value,
|
||||
onColorChanged: (value) {
|
||||
selectedColor.value = value;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
OverflowBar(
|
||||
alignment: MainAxisAlignment.end,
|
||||
spacing: 8.0,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop<ContainerResult?>(context);
|
||||
},
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
final name = textController.text.trim();
|
||||
Navigator.pop<ContainerResult?>(
|
||||
context,
|
||||
(
|
||||
name: name.isNotEmpty ? name : null,
|
||||
color: selectedColor.value,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
switch (_mode) {
|
||||
_DialogMode.create => 'Add',
|
||||
_DialogMode.edit => 'Edit',
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:lensai/core/routing/routes.dart';
|
||||
import 'package:lensai/features/geckoview/features/tabs/data/models/container_data.dart';
|
||||
|
||||
class ContainerListTile extends HookWidget {
|
||||
final ContainerData container;
|
||||
final bool isSelected;
|
||||
|
||||
final void Function()? onTap;
|
||||
|
||||
const ContainerListTile(
|
||||
this.container, {
|
||||
required this.isSelected,
|
||||
this.onTap,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTileTheme(
|
||||
selectedColor: Theme.of(context).colorScheme.onPrimaryContainer,
|
||||
selectedTileColor: Theme.of(context).colorScheme.primaryContainer,
|
||||
child: ListTile(
|
||||
selected: isSelected,
|
||||
leading: CircleAvatar(backgroundColor: container.color),
|
||||
title: Text(container.name ?? 'New Container'),
|
||||
trailing: IconButton(
|
||||
onPressed: () async {
|
||||
await context.push(
|
||||
ContainerEditRoute().location,
|
||||
extra: container,
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.chevron_right),
|
||||
),
|
||||
onTap: onTap,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import 'package:lensai/features/geckoview/features/preferences/data/models/preference_setting.dart';
|
||||
|
||||
enum PreferencePartition {
|
||||
user('user'),
|
||||
system('system');
|
||||
|
||||
final String key;
|
||||
|
||||
const PreferencePartition(this.key);
|
||||
}
|
||||
|
||||
Map<String, PreferenceSettingGroup> deserializePreferenceSettingGroups(
|
||||
PreferencePartition partition,
|
||||
Map<String, dynamic> content,
|
||||
) {
|
||||
final parititonedContent = content[partition.key] as Map<String, dynamic>;
|
||||
|
||||
return parititonedContent.map(
|
||||
(key, value) => MapEntry(
|
||||
key,
|
||||
PreferenceSettingGroup(
|
||||
// ignore: avoid_dynamic_calls
|
||||
description: value['description'] as String?,
|
||||
// ignore: avoid_dynamic_calls
|
||||
settings: (value['preferences'] as Map<String, dynamic>).map(
|
||||
(key, value) => MapEntry(
|
||||
key,
|
||||
PreferenceSetting.fromJson(value as Map<String, dynamic>),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user