add separate history download screen
This commit is contained in:
@@ -1143,6 +1143,13 @@ RouteBase get $historyRoute => GoRouteData.$route(
|
|||||||
path: '/history',
|
path: '/history',
|
||||||
name: 'HistoryRoute',
|
name: 'HistoryRoute',
|
||||||
factory: $HistoryRoute._fromState,
|
factory: $HistoryRoute._fromState,
|
||||||
|
routes: [
|
||||||
|
GoRouteData.$route(
|
||||||
|
path: 'downloads',
|
||||||
|
name: 'HistoryDownloadsRoute',
|
||||||
|
factory: $HistoryDownloadsRoute._fromState,
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
mixin $HistoryRoute on GoRouteData {
|
mixin $HistoryRoute on GoRouteData {
|
||||||
@@ -1165,6 +1172,27 @@ mixin $HistoryRoute on GoRouteData {
|
|||||||
void replace(BuildContext context) => context.replace(location);
|
void replace(BuildContext context) => context.replace(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mixin $HistoryDownloadsRoute on GoRouteData {
|
||||||
|
static HistoryDownloadsRoute _fromState(GoRouterState state) =>
|
||||||
|
const HistoryDownloadsRoute();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get location => GoRouteData.$location('/history/downloads');
|
||||||
|
|
||||||
|
@override
|
||||||
|
void go(BuildContext context) => context.go(location);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void pushReplacement(BuildContext context) =>
|
||||||
|
context.pushReplacement(location);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void replace(BuildContext context) => context.replace(location);
|
||||||
|
}
|
||||||
|
|
||||||
RouteBase get $profileListRoute => GoRouteData.$route(
|
RouteBase get $profileListRoute => GoRouteData.$route(
|
||||||
path: '/profiles',
|
path: '/profiles',
|
||||||
name: 'ProfileListRoute',
|
name: 'ProfileListRoute',
|
||||||
|
|||||||
@@ -19,7 +19,16 @@
|
|||||||
*/
|
*/
|
||||||
part of 'routes.dart';
|
part of 'routes.dart';
|
||||||
|
|
||||||
@TypedGoRoute<HistoryRoute>(name: 'HistoryRoute', path: '/history')
|
@TypedGoRoute<HistoryRoute>(
|
||||||
|
name: 'HistoryRoute',
|
||||||
|
path: '/history',
|
||||||
|
routes: [
|
||||||
|
TypedGoRoute<HistoryDownloadsRoute>(
|
||||||
|
name: 'HistoryDownloadsRoute',
|
||||||
|
path: 'downloads',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
class HistoryRoute extends GoRouteData with $HistoryRoute {
|
class HistoryRoute extends GoRouteData with $HistoryRoute {
|
||||||
const HistoryRoute();
|
const HistoryRoute();
|
||||||
|
|
||||||
@@ -28,3 +37,12 @@ class HistoryRoute extends GoRouteData with $HistoryRoute {
|
|||||||
return const HistoryScreen();
|
return const HistoryScreen();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class HistoryDownloadsRoute extends GoRouteData with $HistoryDownloadsRoute {
|
||||||
|
const HistoryDownloadsRoute();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, GoRouterState state) {
|
||||||
|
return const HistoryScreen(mode: HistoryScreenMode.downloads);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+14
-5
@@ -54,12 +54,12 @@ class BrowserNavigationDrawer extends HookConsumerWidget {
|
|||||||
|
|
||||||
return NavigationDrawer(
|
return NavigationDrawer(
|
||||||
backgroundColor: colorScheme.surface,
|
backgroundColor: colorScheme.surface,
|
||||||
|
header: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [_ProfileHeader(), _SyncTile(), const Divider()],
|
||||||
|
),
|
||||||
children: [
|
children: [
|
||||||
// Profile Header
|
|
||||||
_ProfileHeader(),
|
|
||||||
_SyncTile(),
|
|
||||||
const Divider(),
|
|
||||||
|
|
||||||
// Section 1: Tools & Configuration
|
// Section 1: Tools & Configuration
|
||||||
_ExtensionsSection(),
|
_ExtensionsSection(),
|
||||||
|
|
||||||
@@ -107,6 +107,15 @@ class BrowserNavigationDrawer extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(MdiIcons.fileDownload),
|
||||||
|
title: const Text('Downloads'),
|
||||||
|
onTap: () async {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
await const HistoryDownloadsRoute().push(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(MdiIcons.bookmarkMultiple),
|
leading: const Icon(MdiIcons.bookmarkMultiple),
|
||||||
title: const Text('Bookmarks'),
|
title: const Text('Bookmarks'),
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ part 'providers.g.dart';
|
|||||||
|
|
||||||
@Riverpod(keepAlive: true)
|
@Riverpod(keepAlive: true)
|
||||||
@JsonPersist()
|
@JsonPersist()
|
||||||
class HistoryFilter extends _$HistoryFilter {
|
class HistoryVisitsFilter extends _$HistoryVisitsFilter {
|
||||||
void updateVisitType(VisitType type, bool value) {
|
void updateVisitType(VisitType type, bool value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
state = state.copyWith.visitTypes({...state.visitTypes, type});
|
state = state.copyWith.visitTypes({...state.visitTypes, type});
|
||||||
@@ -51,16 +51,54 @@ class HistoryFilter extends _$HistoryFilter {
|
|||||||
HistoryFilterOptions build() {
|
HistoryFilterOptions build() {
|
||||||
persist(
|
persist(
|
||||||
ref.watch(riverpodDatabaseStorageProvider),
|
ref.watch(riverpodDatabaseStorageProvider),
|
||||||
key: 'HistoryFilterOptions',
|
key: 'HistoryVisitsFilterOptions',
|
||||||
);
|
);
|
||||||
|
|
||||||
return stateOrNull ?? HistoryFilterOptions.withDefaults();
|
return stateOrNull ?? HistoryFilterOptions.withDefaults();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Riverpod(keepAlive: true)
|
||||||
|
@JsonPersist()
|
||||||
|
class HistoryDownloadsFilter extends _$HistoryDownloadsFilter {
|
||||||
|
void reset() {
|
||||||
|
state = HistoryFilterOptions(
|
||||||
|
dateRange: null,
|
||||||
|
visitTypes: const {VisitType.download},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDateRange(DateTimeRange<DateTime>? range) {
|
||||||
|
state = state.copyWith.dateRange(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
HistoryFilterOptions build() {
|
||||||
|
persist(
|
||||||
|
ref.watch(riverpodDatabaseStorageProvider),
|
||||||
|
key: 'HistoryDownloadsFilterOptions',
|
||||||
|
);
|
||||||
|
|
||||||
|
return stateOrNull ??
|
||||||
|
HistoryFilterOptions(
|
||||||
|
dateRange: null,
|
||||||
|
visitTypes: const {VisitType.download},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Riverpod()
|
@Riverpod()
|
||||||
Future<List<VisitInfo>> browsingHistory(Ref ref) {
|
Future<List<VisitInfo>> browsingHistory(Ref ref) {
|
||||||
final options = ref.watch(historyFilterProvider);
|
final options = ref.watch(historyVisitsFilterProvider);
|
||||||
|
|
||||||
|
return ref
|
||||||
|
.read(historyRepositoryProvider.notifier)
|
||||||
|
.getDetailedVisits(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Riverpod()
|
||||||
|
Future<List<VisitInfo>> browsingDownloads(Ref ref) {
|
||||||
|
final options = ref.watch(historyDownloadsFilterProvider);
|
||||||
|
|
||||||
return ref
|
return ref
|
||||||
.read(historyRepositoryProvider.notifier)
|
.read(historyRepositoryProvider.notifier)
|
||||||
|
|||||||
@@ -9,30 +9,30 @@ part of 'providers.dart';
|
|||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
// ignore_for_file: type=lint, type=warning
|
// ignore_for_file: type=lint, type=warning
|
||||||
|
|
||||||
@ProviderFor(HistoryFilter)
|
@ProviderFor(HistoryVisitsFilter)
|
||||||
@JsonPersist()
|
@JsonPersist()
|
||||||
final historyFilterProvider = HistoryFilterProvider._();
|
final historyVisitsFilterProvider = HistoryVisitsFilterProvider._();
|
||||||
|
|
||||||
@JsonPersist()
|
@JsonPersist()
|
||||||
final class HistoryFilterProvider
|
final class HistoryVisitsFilterProvider
|
||||||
extends $NotifierProvider<HistoryFilter, HistoryFilterOptions> {
|
extends $NotifierProvider<HistoryVisitsFilter, HistoryFilterOptions> {
|
||||||
HistoryFilterProvider._()
|
HistoryVisitsFilterProvider._()
|
||||||
: super(
|
: super(
|
||||||
from: null,
|
from: null,
|
||||||
argument: null,
|
argument: null,
|
||||||
retry: null,
|
retry: null,
|
||||||
name: r'historyFilterProvider',
|
name: r'historyVisitsFilterProvider',
|
||||||
isAutoDispose: false,
|
isAutoDispose: false,
|
||||||
dependencies: null,
|
dependencies: null,
|
||||||
$allTransitiveDependencies: null,
|
$allTransitiveDependencies: null,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String debugGetCreateSourceHash() => _$historyFilterHash();
|
String debugGetCreateSourceHash() => _$historyVisitsFilterHash();
|
||||||
|
|
||||||
@$internal
|
@$internal
|
||||||
@override
|
@override
|
||||||
HistoryFilter create() => HistoryFilter();
|
HistoryVisitsFilter create() => HistoryVisitsFilter();
|
||||||
|
|
||||||
/// {@macro riverpod.override_with_value}
|
/// {@macro riverpod.override_with_value}
|
||||||
Override overrideWithValue(HistoryFilterOptions value) {
|
Override overrideWithValue(HistoryFilterOptions value) {
|
||||||
@@ -43,10 +43,69 @@ final class HistoryFilterProvider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$historyFilterHash() => r'685b92590011ec450ef0c19820e4a6750d26b79d';
|
String _$historyVisitsFilterHash() =>
|
||||||
|
r'a4bf6c41c9180166365084cb7c7981558ac7ad36';
|
||||||
|
|
||||||
@JsonPersist()
|
@JsonPersist()
|
||||||
abstract class _$HistoryFilterBase extends $Notifier<HistoryFilterOptions> {
|
abstract class _$HistoryVisitsFilterBase
|
||||||
|
extends $Notifier<HistoryFilterOptions> {
|
||||||
|
HistoryFilterOptions build();
|
||||||
|
@$mustCallSuper
|
||||||
|
@override
|
||||||
|
void runBuild() {
|
||||||
|
final ref = this.ref as $Ref<HistoryFilterOptions, HistoryFilterOptions>;
|
||||||
|
final element =
|
||||||
|
ref.element
|
||||||
|
as $ClassProviderElement<
|
||||||
|
AnyNotifier<HistoryFilterOptions, HistoryFilterOptions>,
|
||||||
|
HistoryFilterOptions,
|
||||||
|
Object?,
|
||||||
|
Object?
|
||||||
|
>;
|
||||||
|
element.handleCreate(ref, build);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ProviderFor(HistoryDownloadsFilter)
|
||||||
|
@JsonPersist()
|
||||||
|
final historyDownloadsFilterProvider = HistoryDownloadsFilterProvider._();
|
||||||
|
|
||||||
|
@JsonPersist()
|
||||||
|
final class HistoryDownloadsFilterProvider
|
||||||
|
extends $NotifierProvider<HistoryDownloadsFilter, HistoryFilterOptions> {
|
||||||
|
HistoryDownloadsFilterProvider._()
|
||||||
|
: super(
|
||||||
|
from: null,
|
||||||
|
argument: null,
|
||||||
|
retry: null,
|
||||||
|
name: r'historyDownloadsFilterProvider',
|
||||||
|
isAutoDispose: false,
|
||||||
|
dependencies: null,
|
||||||
|
$allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String debugGetCreateSourceHash() => _$historyDownloadsFilterHash();
|
||||||
|
|
||||||
|
@$internal
|
||||||
|
@override
|
||||||
|
HistoryDownloadsFilter create() => HistoryDownloadsFilter();
|
||||||
|
|
||||||
|
/// {@macro riverpod.override_with_value}
|
||||||
|
Override overrideWithValue(HistoryFilterOptions value) {
|
||||||
|
return $ProviderOverride(
|
||||||
|
origin: this,
|
||||||
|
providerOverride: $SyncValueProvider<HistoryFilterOptions>(value),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _$historyDownloadsFilterHash() =>
|
||||||
|
r'b44de5aced7dadba361fddf69b8462ff92252a99';
|
||||||
|
|
||||||
|
@JsonPersist()
|
||||||
|
abstract class _$HistoryDownloadsFilterBase
|
||||||
|
extends $Notifier<HistoryFilterOptions> {
|
||||||
HistoryFilterOptions build();
|
HistoryFilterOptions build();
|
||||||
@$mustCallSuper
|
@$mustCallSuper
|
||||||
@override
|
@override
|
||||||
@@ -101,17 +160,88 @@ final class BrowsingHistoryProvider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$browsingHistoryHash() => r'3b6ee5853387481d7544bc9733741a5519f84f40';
|
String _$browsingHistoryHash() => r'6f26228da28f6f67844551bf430025b161731bb2';
|
||||||
|
|
||||||
|
@ProviderFor(browsingDownloads)
|
||||||
|
final browsingDownloadsProvider = BrowsingDownloadsProvider._();
|
||||||
|
|
||||||
|
final class BrowsingDownloadsProvider
|
||||||
|
extends
|
||||||
|
$FunctionalProvider<
|
||||||
|
AsyncValue<List<VisitInfo>>,
|
||||||
|
List<VisitInfo>,
|
||||||
|
FutureOr<List<VisitInfo>>
|
||||||
|
>
|
||||||
|
with $FutureModifier<List<VisitInfo>>, $FutureProvider<List<VisitInfo>> {
|
||||||
|
BrowsingDownloadsProvider._()
|
||||||
|
: super(
|
||||||
|
from: null,
|
||||||
|
argument: null,
|
||||||
|
retry: null,
|
||||||
|
name: r'browsingDownloadsProvider',
|
||||||
|
isAutoDispose: true,
|
||||||
|
dependencies: null,
|
||||||
|
$allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String debugGetCreateSourceHash() => _$browsingDownloadsHash();
|
||||||
|
|
||||||
|
@$internal
|
||||||
|
@override
|
||||||
|
$FutureProviderElement<List<VisitInfo>> $createElement(
|
||||||
|
$ProviderPointer pointer,
|
||||||
|
) => $FutureProviderElement(pointer);
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOr<List<VisitInfo>> create(Ref ref) {
|
||||||
|
return browsingDownloads(ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _$browsingDownloadsHash() => r'd05e3c079c6de349ff9e910ca597ad7c78f87a16';
|
||||||
|
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
// JsonGenerator
|
// JsonGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
abstract class _$HistoryFilter extends _$HistoryFilterBase {
|
abstract class _$HistoryVisitsFilter extends _$HistoryVisitsFilterBase {
|
||||||
/// The default key used by [persist].
|
/// The default key used by [persist].
|
||||||
String get key {
|
String get key {
|
||||||
const resolvedKey = "HistoryFilter";
|
const resolvedKey = "HistoryVisitsFilter";
|
||||||
|
return resolvedKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A variant of [persist], for JSON-specific encoding.
|
||||||
|
///
|
||||||
|
/// You can override [key] to customize the key used for storage.
|
||||||
|
PersistResult persist(
|
||||||
|
FutureOr<Storage<String, String>> storage, {
|
||||||
|
String? key,
|
||||||
|
String Function(HistoryFilterOptions state)? encode,
|
||||||
|
HistoryFilterOptions Function(String encoded)? decode,
|
||||||
|
StorageOptions options = const StorageOptions(),
|
||||||
|
}) {
|
||||||
|
return NotifierPersistX(this).persist<String, String>(
|
||||||
|
storage,
|
||||||
|
key: key ?? this.key,
|
||||||
|
encode: encode ?? $jsonCodex.encode,
|
||||||
|
decode:
|
||||||
|
decode ??
|
||||||
|
(encoded) {
|
||||||
|
final e = $jsonCodex.decode(encoded);
|
||||||
|
return HistoryFilterOptions.fromJson(e as Map<String, Object?>);
|
||||||
|
},
|
||||||
|
options: options,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _$HistoryDownloadsFilter extends _$HistoryDownloadsFilterBase {
|
||||||
|
/// The default key used by [persist].
|
||||||
|
String get key {
|
||||||
|
const resolvedKey = "HistoryDownloadsFilter";
|
||||||
return resolvedKey;
|
return resolvedKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ class Section extends MultiSliver {
|
|||||||
required Set<VisitInfo> selectedItems,
|
required Set<VisitInfo> selectedItems,
|
||||||
required void Function(VisitInfo) onTap,
|
required void Function(VisitInfo) onTap,
|
||||||
required void Function(VisitInfo) onLongPress,
|
required void Function(VisitInfo) onLongPress,
|
||||||
|
required Future<void> Function(VisitInfo) onDelete,
|
||||||
}) : super(
|
}) : super(
|
||||||
pushPinnedChildren: true,
|
pushPinnedChildren: true,
|
||||||
children: [
|
children: [
|
||||||
@@ -103,37 +104,11 @@ class Section extends MultiSliver {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
subtitle: UriBreadcrumb(uri: uri),
|
subtitle: UriBreadcrumb(uri: uri),
|
||||||
trailing: Consumer(
|
trailing: IconButton(
|
||||||
builder: (context, ref, _) {
|
onPressed: () async {
|
||||||
return IconButton(
|
await onDelete(item);
|
||||||
onPressed: () async {
|
|
||||||
await ref
|
|
||||||
.read(historyRepositoryProvider.notifier)
|
|
||||||
.deleteVisit(item);
|
|
||||||
|
|
||||||
final downloadedFile = item.title.mapNotNull(
|
|
||||||
(title) => File(title),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (await downloadedFile?.exists() == true) {
|
|
||||||
if (context.mounted) {
|
|
||||||
final delete = await showDeleteFileDialog(
|
|
||||||
context,
|
|
||||||
downloadedFile.toString(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (delete?.delete == true) {
|
|
||||||
await downloadedFile!.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignore: unused_result
|
|
||||||
await ref.refresh(browsingHistoryProvider.future);
|
|
||||||
},
|
|
||||||
icon: const Icon(MdiIcons.closeCircle),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
icon: const Icon(MdiIcons.closeCircle),
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
onTap(item);
|
onTap(item);
|
||||||
@@ -196,21 +171,74 @@ class Section extends MultiSliver {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum HistoryScreenMode { history, downloads }
|
||||||
|
|
||||||
class HistoryScreen extends HookConsumerWidget {
|
class HistoryScreen extends HookConsumerWidget {
|
||||||
const HistoryScreen({super.key});
|
const HistoryScreen({super.key, this.mode = HistoryScreenMode.history});
|
||||||
|
|
||||||
|
final HistoryScreenMode mode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final historyFilter = ref.watch(historyFilterProvider);
|
final isDownloadsMode = mode == HistoryScreenMode.downloads;
|
||||||
|
|
||||||
final textFilterEnabled = useState(false);
|
final textFilterEnabled = useState(false);
|
||||||
final textFilterController = useTextEditingController();
|
final textFilterController = useTextEditingController();
|
||||||
|
|
||||||
final menuController = useMenuController();
|
final menuController = useMenuController();
|
||||||
|
|
||||||
final historyEntries = ref.watch(browsingHistoryProvider);
|
final historyFilter = isDownloadsMode
|
||||||
|
? ref.watch(historyDownloadsFilterProvider)
|
||||||
|
: ref.watch(historyVisitsFilterProvider);
|
||||||
|
final historyEntries = isDownloadsMode
|
||||||
|
? ref.watch(browsingDownloadsProvider)
|
||||||
|
: ref.watch(browsingHistoryProvider);
|
||||||
|
|
||||||
final selectedItems = useState(<VisitInfo>{});
|
final selectedItems = useState(<VisitInfo>{});
|
||||||
|
final defaultDownloadsFilter = HistoryFilterOptions(
|
||||||
|
dateRange: null,
|
||||||
|
visitTypes: const {VisitType.download},
|
||||||
|
);
|
||||||
|
final hasActiveFilter = isDownloadsMode
|
||||||
|
? historyFilter != defaultDownloadsFilter
|
||||||
|
: historyFilter != HistoryFilterOptions.withDefaults();
|
||||||
|
|
||||||
|
Future<void> refreshHistoryEntries() async {
|
||||||
|
if (isDownloadsMode) {
|
||||||
|
// ignore: unused_result
|
||||||
|
await ref.refresh(browsingDownloadsProvider.future);
|
||||||
|
} else {
|
||||||
|
// ignore: unused_result
|
||||||
|
await ref.refresh(browsingHistoryProvider.future);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDateRange(DateTimeRange<DateTime>? range) {
|
||||||
|
if (isDownloadsMode) {
|
||||||
|
ref.read(historyDownloadsFilterProvider.notifier).setDateRange(range);
|
||||||
|
} else {
|
||||||
|
ref.read(historyVisitsFilterProvider.notifier).setDateRange(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> deleteHistoryItem(VisitInfo item) async {
|
||||||
|
await ref.read(historyRepositoryProvider.notifier).deleteVisit(item);
|
||||||
|
|
||||||
|
final downloadedFile = item.title.mapNotNull((title) => File(title));
|
||||||
|
|
||||||
|
if (await downloadedFile?.exists() == true && context.mounted) {
|
||||||
|
final delete = await showDeleteFileDialog(
|
||||||
|
context,
|
||||||
|
downloadedFile.toString(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (delete?.delete == true) {
|
||||||
|
await downloadedFile!.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await refreshHistoryEntries();
|
||||||
|
}
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
@@ -220,7 +248,9 @@ class HistoryScreen extends HookConsumerWidget {
|
|||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
contentPadding: const EdgeInsets.only(top: 12),
|
contentPadding: const EdgeInsets.only(top: 12),
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
hintText: 'Filter history...',
|
hintText: isDownloadsMode
|
||||||
|
? 'Filter downloads...'
|
||||||
|
: 'Filter history...',
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -235,7 +265,7 @@ class HistoryScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
: selectedItems.value.isEmpty
|
: selectedItems.value.isEmpty
|
||||||
? const Text('History')
|
? Text(isDownloadsMode ? 'Downloads' : 'History')
|
||||||
: Text('${selectedItems.value.length} selected'),
|
: Text('${selectedItems.value.length} selected'),
|
||||||
actions: [
|
actions: [
|
||||||
if (selectedItems.value.isNotEmpty)
|
if (selectedItems.value.isNotEmpty)
|
||||||
@@ -272,8 +302,7 @@ class HistoryScreen extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
selectedItems.value = {};
|
selectedItems.value = {};
|
||||||
// ignore: unused_result
|
await refreshHistoryEntries();
|
||||||
await ref.refresh(browsingHistoryProvider.future);
|
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.delete),
|
icon: const Icon(Icons.delete),
|
||||||
)
|
)
|
||||||
@@ -282,11 +311,15 @@ class HistoryScreen extends HookConsumerWidget {
|
|||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await showDeleteDataDialog(
|
await showDeleteDataDialog(
|
||||||
context,
|
context,
|
||||||
initialSettings: {DeleteBrowsingDataType.history},
|
initialSettings: {
|
||||||
|
if (isDownloadsMode)
|
||||||
|
DeleteBrowsingDataType.downloads
|
||||||
|
else
|
||||||
|
DeleteBrowsingDataType.history,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// ignore: unused_result
|
await refreshHistoryEntries();
|
||||||
await ref.refresh(browsingHistoryProvider.future);
|
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.delete),
|
icon: const Icon(Icons.delete),
|
||||||
),
|
),
|
||||||
@@ -307,9 +340,7 @@ class HistoryScreen extends HookConsumerWidget {
|
|||||||
trailingIcon: historyFilter.dateRange.mapNotNull(
|
trailingIcon: historyFilter.dateRange.mapNotNull(
|
||||||
(_) => IconButton(
|
(_) => IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
ref
|
setDateRange(null);
|
||||||
.read(historyFilterProvider.notifier)
|
|
||||||
.setDateRange(null);
|
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.clear),
|
icon: const Icon(Icons.clear),
|
||||||
),
|
),
|
||||||
@@ -331,53 +362,52 @@ class HistoryScreen extends HookConsumerWidget {
|
|||||||
lastDate: DateTime.now(),
|
lastDate: DateTime.now(),
|
||||||
);
|
);
|
||||||
|
|
||||||
ref
|
setDateRange(
|
||||||
.read(historyFilterProvider.notifier)
|
range.mapNotNull(
|
||||||
.setDateRange(
|
(range) => DateTimeRange(
|
||||||
range.mapNotNull(
|
start: range.start,
|
||||||
(range) => DateTimeRange(
|
// Make sure to include last day fully.
|
||||||
start: range.start,
|
end: range.end.add(
|
||||||
//Make sure to include last day fully
|
const Duration(days: 1) -
|
||||||
end: range.end.add(
|
const Duration(milliseconds: 1),
|
||||||
const Duration(days: 1) -
|
|
||||||
const Duration(milliseconds: 1),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const Divider(),
|
if (!isDownloadsMode) const Divider(),
|
||||||
...{VisitType.link, VisitType.reload, VisitType.download}.map(
|
if (!isDownloadsMode)
|
||||||
(type) => CheckboxMenuButton(
|
...{VisitType.link, VisitType.reload, VisitType.download}.map(
|
||||||
closeOnActivate: false,
|
(type) => CheckboxMenuButton(
|
||||||
value: historyFilter.visitTypes.contains(type),
|
closeOnActivate: false,
|
||||||
onChanged: (value) {
|
value: historyFilter.visitTypes.contains(type),
|
||||||
if (value != null) {
|
onChanged: (value) {
|
||||||
ref
|
if (value != null) {
|
||||||
.read(historyFilterProvider.notifier)
|
ref
|
||||||
.updateVisitType(type, value);
|
.read(historyVisitsFilterProvider.notifier)
|
||||||
}
|
.updateVisitType(type, value);
|
||||||
},
|
}
|
||||||
child: switch (type) {
|
},
|
||||||
VisitType.link => const Text('Followed Links'),
|
child: switch (type) {
|
||||||
VisitType.typed => const Text('Typed Addresses'),
|
VisitType.link => const Text('Followed Links'),
|
||||||
VisitType.embed => const Text('Embedded Page Elements'),
|
VisitType.typed => const Text('Typed Addresses'),
|
||||||
VisitType.redirectPermanent => const Text(
|
VisitType.embed => const Text('Embedded Page Elements'),
|
||||||
'Temporary Redirects',
|
VisitType.redirectPermanent => const Text(
|
||||||
),
|
'Temporary Redirects',
|
||||||
VisitType.redirectTemporary => const Text(
|
),
|
||||||
'Permanent Redirects',
|
VisitType.redirectTemporary => const Text(
|
||||||
),
|
'Permanent Redirects',
|
||||||
VisitType.download => const Text('Downloads'),
|
),
|
||||||
VisitType.framedLink => const Text('Frames'),
|
VisitType.download => const Text('Downloads'),
|
||||||
VisitType.reload => const Text('Page Reloads'),
|
VisitType.framedLink => const Text('Frames'),
|
||||||
VisitType.bookmark => throw UnimplementedError(
|
VisitType.reload => const Text('Page Reloads'),
|
||||||
'VisitType.bookmark filter not implemented',
|
VisitType.bookmark => throw UnimplementedError(
|
||||||
),
|
'VisitType.bookmark filter not implemented',
|
||||||
},
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
const Divider(),
|
const Divider(),
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
leadingIcon: const Icon(MdiIcons.restore),
|
leadingIcon: const Icon(MdiIcons.restore),
|
||||||
@@ -385,8 +415,11 @@ class HistoryScreen extends HookConsumerWidget {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
textFilterController.clear();
|
textFilterController.clear();
|
||||||
textFilterEnabled.value = false;
|
textFilterEnabled.value = false;
|
||||||
|
if (isDownloadsMode) {
|
||||||
ref.read(historyFilterProvider.notifier).reset();
|
ref.read(historyDownloadsFilterProvider.notifier).reset();
|
||||||
|
} else {
|
||||||
|
ref.read(historyVisitsFilterProvider.notifier).reset();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -399,8 +432,7 @@ class HistoryScreen extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
icon: Badge(
|
icon: Badge(
|
||||||
isLabelVisible:
|
isLabelVisible: hasActiveFilter,
|
||||||
historyFilter != HistoryFilterOptions.withDefaults(),
|
|
||||||
child: const Icon(MdiIcons.filter),
|
child: const Icon(MdiIcons.filter),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -413,8 +445,7 @@ class HistoryScreen extends HookConsumerWidget {
|
|||||||
data: (data) {
|
data: (data) {
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
// ignore: unused_result
|
await refreshHistoryEntries();
|
||||||
await ref.refresh(browsingHistoryProvider.future);
|
|
||||||
},
|
},
|
||||||
child: HookBuilder(
|
child: HookBuilder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
@@ -464,6 +495,7 @@ class HistoryScreen extends HookConsumerWidget {
|
|||||||
items: value,
|
items: value,
|
||||||
selectedItems: selectedItems.value,
|
selectedItems: selectedItems.value,
|
||||||
onLongPress: toggleSelected,
|
onLongPress: toggleSelected,
|
||||||
|
onDelete: deleteHistoryItem,
|
||||||
onTap: (item) async {
|
onTap: (item) async {
|
||||||
if (selectedItems.value.isNotEmpty) {
|
if (selectedItems.value.isNotEmpty) {
|
||||||
toggleSelected(item);
|
toggleSelected(item);
|
||||||
@@ -492,7 +524,9 @@ class HistoryScreen extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
error: (error, stackTrace) => Center(
|
error: (error, stackTrace) => Center(
|
||||||
child: FailureWidget(
|
child: FailureWidget(
|
||||||
title: 'Failed to load History',
|
title: isDownloadsMode
|
||||||
|
? 'Failed to load Downloads'
|
||||||
|
: 'Failed to load History',
|
||||||
exception: error,
|
exception: error,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user