finished implementing history with removal options
This commit is contained in:
@@ -0,0 +1,60 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:nullability/nullability.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/browser/domain/services/browser_data.dart';
|
||||||
|
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
||||||
|
|
||||||
|
class DeleteDataDialog extends HookConsumerWidget {
|
||||||
|
final Set<DeleteBrowsingDataType> initialSettings;
|
||||||
|
|
||||||
|
const DeleteDataDialog({required this.initialSettings});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final selections = useState(initialSettings);
|
||||||
|
|
||||||
|
return SimpleDialog(
|
||||||
|
title: const Text('Delete Browsing Data'),
|
||||||
|
children: [
|
||||||
|
for (final type in DeleteBrowsingDataType.values)
|
||||||
|
CheckboxListTile.adaptive(
|
||||||
|
value: selections.value.contains(type),
|
||||||
|
controlAffinity: ListTileControlAffinity.leading,
|
||||||
|
title: Text(type.title),
|
||||||
|
subtitle: type.description.mapNotNull(
|
||||||
|
(description) => Text(description),
|
||||||
|
),
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value == true) {
|
||||||
|
selections.value = {...selections.value, type};
|
||||||
|
} else {
|
||||||
|
selections.value = {...selections.value}..remove(type);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 24.0),
|
||||||
|
child: FilledButton.icon(
|
||||||
|
onPressed: () async {
|
||||||
|
await ref
|
||||||
|
.read(browserDataServiceProvider.notifier)
|
||||||
|
.deleteData(selections.value);
|
||||||
|
|
||||||
|
if (context.mounted) {
|
||||||
|
context.pop();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
style: FilledButton.styleFrom(
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.error,
|
||||||
|
foregroundColor: Theme.of(context).colorScheme.onError,
|
||||||
|
),
|
||||||
|
label: const Text('Delete'),
|
||||||
|
icon: const Icon(Icons.delete_forever),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -92,6 +92,13 @@ class _BrowserViewState extends ConsumerState<BrowserView>
|
|||||||
await ref
|
await ref
|
||||||
.read(browserDataServiceProvider.notifier)
|
.read(browserDataServiceProvider.notifier)
|
||||||
.deleteDataOnEngineStart(settings.deleteBrowsingDataOnQuit);
|
.deleteDataOnEngineStart(settings.deleteBrowsingDataOnQuit);
|
||||||
|
|
||||||
|
if (settings.historyAutoCleanInterval > Duration.zero) {
|
||||||
|
await GeckoHistoryService().deleteVisitsBetween(
|
||||||
|
DateTime(0),
|
||||||
|
DateTime.now().subtract(settings.historyAutoCleanInterval),
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+5
-13
@@ -1,28 +1,20 @@
|
|||||||
import 'package:copy_with_extension/copy_with_extension.dart';
|
import 'package:copy_with_extension/copy_with_extension.dart';
|
||||||
import 'package:fast_equatable/fast_equatable.dart';
|
import 'package:fast_equatable/fast_equatable.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||||
|
|
||||||
part 'history_filter_options.g.dart';
|
part 'history_filter_options.g.dart';
|
||||||
|
|
||||||
@CopyWith()
|
@CopyWith()
|
||||||
class HistoryFilterOptions with FastEquatable {
|
class HistoryFilterOptions with FastEquatable {
|
||||||
final DateTime? start;
|
final DateTimeRange<DateTime>? dateRange;
|
||||||
final DateTime? end;
|
|
||||||
final Set<VisitType> visitTypes;
|
final Set<VisitType> visitTypes;
|
||||||
|
|
||||||
HistoryFilterOptions({
|
HistoryFilterOptions({required this.dateRange, required this.visitTypes});
|
||||||
required this.start,
|
|
||||||
required this.end,
|
|
||||||
required this.visitTypes,
|
|
||||||
});
|
|
||||||
|
|
||||||
HistoryFilterOptions.withDefaults()
|
HistoryFilterOptions.withDefaults()
|
||||||
: this(
|
: this(dateRange: null, visitTypes: {VisitType.link, VisitType.typed});
|
||||||
start: null,
|
|
||||||
end: null,
|
|
||||||
visitTypes: {VisitType.link, VisitType.typed},
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get hashParameters => [start, end, visitTypes];
|
List<Object?> get hashParameters => [dateRange, visitTypes];
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-18
@@ -7,9 +7,7 @@ part of 'history_filter_options.dart';
|
|||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
abstract class _$HistoryFilterOptionsCWProxy {
|
abstract class _$HistoryFilterOptionsCWProxy {
|
||||||
HistoryFilterOptions start(DateTime? start);
|
HistoryFilterOptions dateRange(DateTimeRange<DateTime>? dateRange);
|
||||||
|
|
||||||
HistoryFilterOptions end(DateTime? end);
|
|
||||||
|
|
||||||
HistoryFilterOptions visitTypes(Set<VisitType> visitTypes);
|
HistoryFilterOptions visitTypes(Set<VisitType> visitTypes);
|
||||||
|
|
||||||
@@ -20,8 +18,7 @@ abstract class _$HistoryFilterOptionsCWProxy {
|
|||||||
/// HistoryFilterOptions(...).copyWith(id: 12, name: "My name")
|
/// HistoryFilterOptions(...).copyWith(id: 12, name: "My name")
|
||||||
/// ````
|
/// ````
|
||||||
HistoryFilterOptions call({
|
HistoryFilterOptions call({
|
||||||
DateTime? start,
|
DateTimeRange<DateTime>? dateRange,
|
||||||
DateTime? end,
|
|
||||||
Set<VisitType> visitTypes,
|
Set<VisitType> visitTypes,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -34,10 +31,8 @@ class _$HistoryFilterOptionsCWProxyImpl
|
|||||||
final HistoryFilterOptions _value;
|
final HistoryFilterOptions _value;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
HistoryFilterOptions start(DateTime? start) => this(start: start);
|
HistoryFilterOptions dateRange(DateTimeRange<DateTime>? dateRange) =>
|
||||||
|
this(dateRange: dateRange);
|
||||||
@override
|
|
||||||
HistoryFilterOptions end(DateTime? end) => this(end: end);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
HistoryFilterOptions visitTypes(Set<VisitType> visitTypes) =>
|
HistoryFilterOptions visitTypes(Set<VisitType> visitTypes) =>
|
||||||
@@ -51,19 +46,14 @@ class _$HistoryFilterOptionsCWProxyImpl
|
|||||||
/// HistoryFilterOptions(...).copyWith(id: 12, name: "My name")
|
/// HistoryFilterOptions(...).copyWith(id: 12, name: "My name")
|
||||||
/// ````
|
/// ````
|
||||||
HistoryFilterOptions call({
|
HistoryFilterOptions call({
|
||||||
Object? start = const $CopyWithPlaceholder(),
|
Object? dateRange = const $CopyWithPlaceholder(),
|
||||||
Object? end = const $CopyWithPlaceholder(),
|
|
||||||
Object? visitTypes = const $CopyWithPlaceholder(),
|
Object? visitTypes = const $CopyWithPlaceholder(),
|
||||||
}) {
|
}) {
|
||||||
return HistoryFilterOptions(
|
return HistoryFilterOptions(
|
||||||
start: start == const $CopyWithPlaceholder()
|
dateRange: dateRange == const $CopyWithPlaceholder()
|
||||||
? _value.start
|
? _value.dateRange
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
: start as DateTime?,
|
: dateRange as DateTimeRange<DateTime>?,
|
||||||
end: end == const $CopyWithPlaceholder()
|
|
||||||
? _value.end
|
|
||||||
// ignore: cast_nullable_to_non_nullable
|
|
||||||
: end as DateTime?,
|
|
||||||
visitTypes: visitTypes == const $CopyWithPlaceholder()
|
visitTypes: visitTypes == const $CopyWithPlaceholder()
|
||||||
? _value.visitTypes
|
? _value.visitTypes
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
* You should have received a copy of the GNU Affero General Public License
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||||
import 'package:riverpod/riverpod.dart';
|
import 'package:riverpod/riverpod.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
@@ -34,6 +35,10 @@ class HistoryFilter extends _$HistoryFilter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setDateRange(DateTimeRange<DateTime>? range) {
|
||||||
|
state = state.copyWith.dateRange(range);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
HistoryFilterOptions build() {
|
HistoryFilterOptions build() {
|
||||||
return HistoryFilterOptions.withDefaults();
|
return HistoryFilterOptions.withDefaults();
|
||||||
@@ -47,8 +52,8 @@ Future<List<VisitInfo>> browsingHistory(Ref ref) {
|
|||||||
final service = GeckoHistoryService();
|
final service = GeckoHistoryService();
|
||||||
return service
|
return service
|
||||||
.getDetailedVisits(
|
.getDetailedVisits(
|
||||||
options.start ?? DateTime(0),
|
options.dateRange?.start ?? DateTime(0),
|
||||||
options.end ?? DateTime(9999),
|
options.dateRange?.end ?? DateTime(9999),
|
||||||
options.visitTypes,
|
options.visitTypes,
|
||||||
)
|
)
|
||||||
.then(
|
.then(
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ part of 'providers.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$browsingHistoryHash() => r'a684c34fc370474a771c5fcc7982a200de2294e5';
|
String _$browsingHistoryHash() => r'6447fd4d8209c3befb07f2d82fa41a24bd3accb1';
|
||||||
|
|
||||||
/// See also [browsingHistory].
|
/// See also [browsingHistory].
|
||||||
@ProviderFor(browsingHistory)
|
@ProviderFor(browsingHistory)
|
||||||
@@ -24,7 +24,7 @@ final browsingHistoryProvider =
|
|||||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
// ignore: unused_element
|
// ignore: unused_element
|
||||||
typedef BrowsingHistoryRef = AutoDisposeFutureProviderRef<List<VisitInfo>>;
|
typedef BrowsingHistoryRef = AutoDisposeFutureProviderRef<List<VisitInfo>>;
|
||||||
String _$historyFilterHash() => r'271d60ce79f96ac49d450f01d21cbfdad684ce53';
|
String _$historyFilterHash() => r'68a604fe857476f7c15be89352a7b10bc456c3d6';
|
||||||
|
|
||||||
/// See also [HistoryFilter].
|
/// See also [HistoryFilter].
|
||||||
@ProviderFor(HistoryFilter)
|
@ProviderFor(HistoryFilter)
|
||||||
|
|||||||
@@ -2,27 +2,34 @@ import 'package:collection/collection.dart';
|
|||||||
import 'package:fast_equatable/fast_equatable.dart';
|
import 'package:fast_equatable/fast_equatable.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
||||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:intl/intl.dart' show DateFormat;
|
import 'package:intl/intl.dart' show DateFormat;
|
||||||
import 'package:nullability/nullability.dart';
|
import 'package:nullability/nullability.dart';
|
||||||
import 'package:sliver_tools/sliver_tools.dart';
|
import 'package:sliver_tools/sliver_tools.dart';
|
||||||
import 'package:timeago/timeago.dart' as timeago;
|
import 'package:timeago/timeago.dart' as timeago;
|
||||||
|
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/delete_data.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/history/domain/providers.dart';
|
import 'package:weblibre/features/geckoview/features/history/domain/providers.dart';
|
||||||
|
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
||||||
import 'package:weblibre/presentation/hooks/menu_controller.dart';
|
import 'package:weblibre/presentation/hooks/menu_controller.dart';
|
||||||
import 'package:weblibre/presentation/widgets/failure_widget.dart';
|
import 'package:weblibre/presentation/widgets/failure_widget.dart';
|
||||||
import 'package:weblibre/presentation/widgets/url_icon.dart';
|
import 'package:weblibre/presentation/widgets/url_icon.dart';
|
||||||
|
|
||||||
class Section extends MultiSliver {
|
class Section extends MultiSliver {
|
||||||
static final _datePattern = DateFormat('yMMMMd').addPattern('Hm');
|
static final _datePattern = DateFormat.MMMd().addPattern('Hm');
|
||||||
|
|
||||||
Section({
|
Section({
|
||||||
Key? key,
|
super.key,
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
required String title,
|
required String title,
|
||||||
required List<VisitInfo> items,
|
required List<VisitInfo> items,
|
||||||
|
required Set<VisitInfo> selectedItems,
|
||||||
|
required void Function(VisitInfo) onTap,
|
||||||
|
required void Function(VisitInfo) onLongPress,
|
||||||
}) : super(
|
}) : super(
|
||||||
key: key,
|
|
||||||
pushPinnedChildren: true,
|
pushPinnedChildren: true,
|
||||||
children: [
|
children: [
|
||||||
SliverPinnedHeader(
|
SliverPinnedHeader(
|
||||||
@@ -50,16 +57,40 @@ class Section extends MultiSliver {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: UrlIcon([Uri.parse(item.url)], iconSize: 24),
|
leading: selectedItems.contains(item)
|
||||||
|
? const CircleAvatar(
|
||||||
|
radius: 12,
|
||||||
|
child: Icon(Icons.check, size: 12),
|
||||||
|
)
|
||||||
|
: UrlIcon([Uri.parse(item.url)], iconSize: 24),
|
||||||
title: item.title.mapNotNull((title) => Text(title)),
|
title: item.title.mapNotNull((title) => Text(title)),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
item.url,
|
item.url,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
|
trailing: Consumer(
|
||||||
|
builder: (context, ref, _) {
|
||||||
|
return IconButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final service = GeckoHistoryService();
|
||||||
|
await service.deleteVisit(item);
|
||||||
|
// ignore: unused_result
|
||||||
|
await ref.refresh(browsingHistoryProvider.future);
|
||||||
|
},
|
||||||
|
icon: const Icon(MdiIcons.closeCircle),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
onTap(item);
|
||||||
|
},
|
||||||
|
onLongPress: () {
|
||||||
|
onLongPress(item);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 54),
|
padding: const EdgeInsets.only(left: 54, right: 16),
|
||||||
child: Wrap(
|
child: Wrap(
|
||||||
spacing: 8.0,
|
spacing: 8.0,
|
||||||
children: [
|
children: [
|
||||||
@@ -113,43 +144,126 @@ class HistoryScreen extends HookConsumerWidget {
|
|||||||
|
|
||||||
final historyEntries = ref.watch(browsingHistoryProvider);
|
final historyEntries = ref.watch(browsingHistoryProvider);
|
||||||
|
|
||||||
|
final selectedItems = useState(<VisitInfo>{});
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
|
title: selectedItems.value.isEmpty
|
||||||
|
? const Text('History')
|
||||||
|
: Text('${selectedItems.value.length} selected'),
|
||||||
actions: [
|
actions: [
|
||||||
|
if (selectedItems.value.isNotEmpty)
|
||||||
|
IconButton(
|
||||||
|
onPressed: () async {
|
||||||
|
for (final item in selectedItems.value) {
|
||||||
|
final service = GeckoHistoryService();
|
||||||
|
await service.deleteVisit(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedItems.value = {};
|
||||||
|
// ignore: unused_result
|
||||||
|
await ref.refresh(browsingHistoryProvider.future);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.delete),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
IconButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return const DeleteDataDialog(
|
||||||
|
initialSettings: {DeleteBrowsingDataType.history},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// ignore: unused_result
|
||||||
|
await ref.refresh(browsingHistoryProvider.future);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.delete),
|
||||||
|
),
|
||||||
MenuAnchor(
|
MenuAnchor(
|
||||||
controller: menuController,
|
controller: menuController,
|
||||||
menuChildren: [
|
menuChildren: [
|
||||||
...VisitType.values
|
MenuItemButton(
|
||||||
.whereNot(
|
leadingIcon: const Icon(MdiIcons.calendarRange),
|
||||||
(element) => const {VisitType.bookmark}.contains(element),
|
trailingIcon: historyFilter.dateRange.mapNotNull(
|
||||||
)
|
(_) => IconButton(
|
||||||
.map(
|
onPressed: () {
|
||||||
(type) => CheckboxMenuButton(
|
ref
|
||||||
value: historyFilter.visitTypes.contains(type),
|
.read(historyFilterProvider.notifier)
|
||||||
onChanged: (value) {
|
.setDateRange(null);
|
||||||
if (value != null) {
|
},
|
||||||
ref
|
icon: const Icon(Icons.clear),
|
||||||
.read(historyFilterProvider.notifier)
|
|
||||||
.updateVisitType(type, value);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: switch (type) {
|
|
||||||
VisitType.link => const Text('Followed Links'),
|
|
||||||
VisitType.typed => const Text('Typed Addresses'),
|
|
||||||
VisitType.embed => const Text('Embedded Page Elements'),
|
|
||||||
VisitType.redirectPermanent => const Text(
|
|
||||||
'Temporary Redirects',
|
|
||||||
),
|
|
||||||
VisitType.redirectTemporary => const Text(
|
|
||||||
'Permanent Redirects',
|
|
||||||
),
|
|
||||||
VisitType.download => const Text('Downloads'),
|
|
||||||
VisitType.framedLink => const Text('Frames'),
|
|
||||||
VisitType.reload => const Text('Page Reloads'),
|
|
||||||
VisitType.bookmark => throw UnimplementedError(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
child:
|
||||||
|
historyFilter.dateRange.mapNotNull(
|
||||||
|
(range) => Text(
|
||||||
|
'${DateFormat.yMd().format(range.start)} - ${DateFormat.yMd().format(range.end)}',
|
||||||
|
),
|
||||||
|
) ??
|
||||||
|
const Text('Filter Date'),
|
||||||
|
onPressed: () async {
|
||||||
|
final range = await showDateRangePicker(
|
||||||
|
context: context,
|
||||||
|
initialDateRange: historyFilter.dateRange,
|
||||||
|
firstDate: DateTime.now().subtract(
|
||||||
|
const Duration(days: 365),
|
||||||
|
),
|
||||||
|
lastDate: DateTime.now(),
|
||||||
|
);
|
||||||
|
|
||||||
|
ref
|
||||||
|
.read(historyFilterProvider.notifier)
|
||||||
|
.setDateRange(
|
||||||
|
range.mapNotNull(
|
||||||
|
(range) => DateTimeRange(
|
||||||
|
start: range.start,
|
||||||
|
//Make sure to include last day fully
|
||||||
|
end: range.end.add(
|
||||||
|
const Duration(days: 1) -
|
||||||
|
const Duration(milliseconds: 1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
...{
|
||||||
|
VisitType.link,
|
||||||
|
VisitType.typed,
|
||||||
|
VisitType.reload,
|
||||||
|
VisitType.download,
|
||||||
|
}.map(
|
||||||
|
(type) => CheckboxMenuButton(
|
||||||
|
value: historyFilter.visitTypes.contains(type),
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
ref
|
||||||
|
.read(historyFilterProvider.notifier)
|
||||||
|
.updateVisitType(type, value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: switch (type) {
|
||||||
|
VisitType.link => const Text('Followed Links'),
|
||||||
|
VisitType.typed => const Text('Typed Addresses'),
|
||||||
|
VisitType.embed => const Text('Embedded Page Elements'),
|
||||||
|
VisitType.redirectPermanent => const Text(
|
||||||
|
'Temporary Redirects',
|
||||||
|
),
|
||||||
|
VisitType.redirectTemporary => const Text(
|
||||||
|
'Permanent Redirects',
|
||||||
|
),
|
||||||
|
VisitType.download => const Text('Downloads'),
|
||||||
|
VisitType.framedLink => const Text('Frames'),
|
||||||
|
VisitType.reload => const Text('Page Reloads'),
|
||||||
|
VisitType.bookmark => throw UnimplementedError(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -159,7 +273,7 @@ class HistoryScreen extends HookConsumerWidget {
|
|||||||
menuController.open();
|
menuController.open();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.more_vert),
|
icon: const Icon(MdiIcons.filter),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -182,10 +296,41 @@ class HistoryScreen extends HookConsumerWidget {
|
|||||||
[EquatableValue(data)],
|
[EquatableValue(data)],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void toggleSelected(VisitInfo item) {
|
||||||
|
if (selectedItems.value.contains(item)) {
|
||||||
|
selectedItems.value = {...selectedItems.value}
|
||||||
|
..remove(item);
|
||||||
|
} else {
|
||||||
|
selectedItems.value = {...selectedItems.value, item};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return CustomScrollView(
|
return CustomScrollView(
|
||||||
slivers: [
|
slivers: [
|
||||||
for (final MapEntry(:key, :value) in groups.entries)
|
for (final MapEntry(:key, :value) in groups.entries)
|
||||||
Section(context: context, title: key, items: value),
|
Section(
|
||||||
|
context: context,
|
||||||
|
title: key,
|
||||||
|
items: value,
|
||||||
|
selectedItems: selectedItems.value,
|
||||||
|
onLongPress: toggleSelected,
|
||||||
|
onTap: (item) async {
|
||||||
|
if (selectedItems.value.isNotEmpty) {
|
||||||
|
toggleSelected(item);
|
||||||
|
} else {
|
||||||
|
await ref
|
||||||
|
.read(tabRepositoryProvider.notifier)
|
||||||
|
.addTab(
|
||||||
|
url: Uri.parse(item.url),
|
||||||
|
private: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (context.mounted) {
|
||||||
|
context.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:nullability/nullability.dart';
|
import 'package:nullability/nullability.dart';
|
||||||
import 'package:weblibre/core/routing/routes.dart';
|
import 'package:weblibre/core/routing/routes.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/delete_data.dart';
|
||||||
import 'package:weblibre/features/settings/presentation/controllers/save_settings.dart';
|
import 'package:weblibre/features/settings/presentation/controllers/save_settings.dart';
|
||||||
import 'package:weblibre/features/user/data/models/engine_settings.dart';
|
import 'package:weblibre/features/user/data/models/engine_settings.dart';
|
||||||
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
||||||
@@ -49,7 +50,7 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
|
|||||||
SwitchListTile.adaptive(
|
SwitchListTile.adaptive(
|
||||||
title: const Text('Incognito Mode'),
|
title: const Text('Incognito Mode'),
|
||||||
subtitle: const Text(
|
subtitle: const Text(
|
||||||
'Deletes all browsing data upon app restart for enhanced privacy.',
|
'Deletes selected browsing data upon app restart for enhanced privacy.',
|
||||||
),
|
),
|
||||||
secondary: const Icon(MdiIcons.incognito),
|
secondary: const Icon(MdiIcons.incognito),
|
||||||
value: generalSettings.deleteBrowsingDataOnQuit != null,
|
value: generalSettings.deleteBrowsingDataOnQuit != null,
|
||||||
@@ -114,6 +115,94 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
title: const Text('Delete Browsing Data'),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 8.0,
|
||||||
|
horizontal: 16.0,
|
||||||
|
),
|
||||||
|
leading: const Icon(MdiIcons.delete),
|
||||||
|
trailing: const Icon(Icons.chevron_right),
|
||||||
|
onTap: () async {
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return const DeleteDataDialog(initialSettings: {});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16.0,
|
||||||
|
vertical: 8,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const ListTile(
|
||||||
|
title: Text('Auto-Clear History'),
|
||||||
|
subtitle: Text(
|
||||||
|
'Automatically delete browsing history older than the selected time period',
|
||||||
|
),
|
||||||
|
leading: Icon(MdiIcons.deleteClock),
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 40.0),
|
||||||
|
child: DropdownMenu(
|
||||||
|
initialSelection:
|
||||||
|
generalSettings.historyAutoCleanInterval,
|
||||||
|
inputDecorationTheme: InputDecorationTheme(
|
||||||
|
prefixIconConstraints: BoxConstraints.tight(
|
||||||
|
const Size.square(24),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
width: double.infinity,
|
||||||
|
dropdownMenuEntries: const [
|
||||||
|
DropdownMenuEntry(
|
||||||
|
value: Duration.zero,
|
||||||
|
label: 'Never',
|
||||||
|
),
|
||||||
|
DropdownMenuEntry(
|
||||||
|
value: Duration(days: 1),
|
||||||
|
label: '1 Day',
|
||||||
|
),
|
||||||
|
DropdownMenuEntry(
|
||||||
|
value: Duration(days: 7),
|
||||||
|
label: '1 Week',
|
||||||
|
),
|
||||||
|
DropdownMenuEntry(
|
||||||
|
value: Duration(days: 14),
|
||||||
|
label: '2 Weeks',
|
||||||
|
),
|
||||||
|
DropdownMenuEntry(
|
||||||
|
value: Duration(days: 30),
|
||||||
|
label: '1 Month',
|
||||||
|
),
|
||||||
|
DropdownMenuEntry(
|
||||||
|
value: Duration(days: 90),
|
||||||
|
label: '3 Months',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
onSelected: (value) async {
|
||||||
|
await ref
|
||||||
|
.read(
|
||||||
|
saveGeneralSettingsControllerProvider.notifier,
|
||||||
|
)
|
||||||
|
.save(
|
||||||
|
(currentSettings) => currentSettings.copyWith
|
||||||
|
.historyAutoCleanInterval(
|
||||||
|
value ?? Duration.zero,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
SwitchListTile.adaptive(
|
SwitchListTile.adaptive(
|
||||||
title: const Text('Global Privacy Control (GPC)'),
|
title: const Text('Global Privacy Control (GPC)'),
|
||||||
secondary: const Icon(MdiIcons.incognitoCircleOff),
|
secondary: const Icon(MdiIcons.incognitoCircleOff),
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ class GeneralSettings with FastEquatable {
|
|||||||
final TabIntentOpenSetting tabIntentOpenSetting;
|
final TabIntentOpenSetting tabIntentOpenSetting;
|
||||||
final bool autoHideTabBar;
|
final bool autoHideTabBar;
|
||||||
final TabBarSwipeAction tabBarSwipeAction;
|
final TabBarSwipeAction tabBarSwipeAction;
|
||||||
|
final Duration historyAutoCleanInterval;
|
||||||
|
|
||||||
GeneralSettings({
|
GeneralSettings({
|
||||||
required this.themeMode,
|
required this.themeMode,
|
||||||
@@ -78,6 +79,7 @@ class GeneralSettings with FastEquatable {
|
|||||||
required this.tabIntentOpenSetting,
|
required this.tabIntentOpenSetting,
|
||||||
required this.autoHideTabBar,
|
required this.autoHideTabBar,
|
||||||
required this.tabBarSwipeAction,
|
required this.tabBarSwipeAction,
|
||||||
|
required this.historyAutoCleanInterval,
|
||||||
});
|
});
|
||||||
|
|
||||||
GeneralSettings.withDefaults({
|
GeneralSettings.withDefaults({
|
||||||
@@ -94,6 +96,7 @@ class GeneralSettings with FastEquatable {
|
|||||||
TabIntentOpenSetting? tabIntentOpenSetting,
|
TabIntentOpenSetting? tabIntentOpenSetting,
|
||||||
bool? autoHideTabBar,
|
bool? autoHideTabBar,
|
||||||
TabBarSwipeAction? tabBarSwipeAction,
|
TabBarSwipeAction? tabBarSwipeAction,
|
||||||
|
Duration? historyAutoCleanInterval,
|
||||||
}) : themeMode = themeMode ?? ThemeMode.dark,
|
}) : themeMode = themeMode ?? ThemeMode.dark,
|
||||||
enableReadability = enableReadability ?? true,
|
enableReadability = enableReadability ?? true,
|
||||||
enforceReadability = enforceReadability ?? false,
|
enforceReadability = enforceReadability ?? false,
|
||||||
@@ -107,7 +110,9 @@ class GeneralSettings with FastEquatable {
|
|||||||
tabIntentOpenSetting = tabIntentOpenSetting ?? TabIntentOpenSetting.ask,
|
tabIntentOpenSetting = tabIntentOpenSetting ?? TabIntentOpenSetting.ask,
|
||||||
autoHideTabBar = autoHideTabBar ?? true,
|
autoHideTabBar = autoHideTabBar ?? true,
|
||||||
tabBarSwipeAction =
|
tabBarSwipeAction =
|
||||||
tabBarSwipeAction ?? TabBarSwipeAction.switchLastOpened;
|
tabBarSwipeAction ?? TabBarSwipeAction.switchLastOpened,
|
||||||
|
historyAutoCleanInterval =
|
||||||
|
historyAutoCleanInterval ?? const Duration(days: 90);
|
||||||
|
|
||||||
factory GeneralSettings.fromJson(Map<String, dynamic> json) =>
|
factory GeneralSettings.fromJson(Map<String, dynamic> json) =>
|
||||||
_$GeneralSettingsFromJson(json);
|
_$GeneralSettingsFromJson(json);
|
||||||
@@ -129,5 +134,6 @@ class GeneralSettings with FastEquatable {
|
|||||||
tabIntentOpenSetting,
|
tabIntentOpenSetting,
|
||||||
autoHideTabBar,
|
autoHideTabBar,
|
||||||
tabBarSwipeAction,
|
tabBarSwipeAction,
|
||||||
|
historyAutoCleanInterval,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ abstract class _$GeneralSettingsCWProxy {
|
|||||||
|
|
||||||
GeneralSettings tabBarSwipeAction(TabBarSwipeAction tabBarSwipeAction);
|
GeneralSettings tabBarSwipeAction(TabBarSwipeAction tabBarSwipeAction);
|
||||||
|
|
||||||
|
GeneralSettings historyAutoCleanInterval(Duration historyAutoCleanInterval);
|
||||||
|
|
||||||
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `GeneralSettings(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `GeneralSettings(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
||||||
///
|
///
|
||||||
/// Usage
|
/// Usage
|
||||||
@@ -59,6 +61,7 @@ abstract class _$GeneralSettingsCWProxy {
|
|||||||
TabIntentOpenSetting tabIntentOpenSetting,
|
TabIntentOpenSetting tabIntentOpenSetting,
|
||||||
bool autoHideTabBar,
|
bool autoHideTabBar,
|
||||||
TabBarSwipeAction tabBarSwipeAction,
|
TabBarSwipeAction tabBarSwipeAction,
|
||||||
|
Duration historyAutoCleanInterval,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,6 +125,10 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
|
|||||||
GeneralSettings tabBarSwipeAction(TabBarSwipeAction tabBarSwipeAction) =>
|
GeneralSettings tabBarSwipeAction(TabBarSwipeAction tabBarSwipeAction) =>
|
||||||
this(tabBarSwipeAction: tabBarSwipeAction);
|
this(tabBarSwipeAction: tabBarSwipeAction);
|
||||||
|
|
||||||
|
@override
|
||||||
|
GeneralSettings historyAutoCleanInterval(Duration historyAutoCleanInterval) =>
|
||||||
|
this(historyAutoCleanInterval: historyAutoCleanInterval);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `GeneralSettings(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `GeneralSettings(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
|
||||||
///
|
///
|
||||||
@@ -143,6 +150,7 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
|
|||||||
Object? tabIntentOpenSetting = const $CopyWithPlaceholder(),
|
Object? tabIntentOpenSetting = const $CopyWithPlaceholder(),
|
||||||
Object? autoHideTabBar = const $CopyWithPlaceholder(),
|
Object? autoHideTabBar = const $CopyWithPlaceholder(),
|
||||||
Object? tabBarSwipeAction = const $CopyWithPlaceholder(),
|
Object? tabBarSwipeAction = const $CopyWithPlaceholder(),
|
||||||
|
Object? historyAutoCleanInterval = const $CopyWithPlaceholder(),
|
||||||
}) {
|
}) {
|
||||||
return GeneralSettings(
|
return GeneralSettings(
|
||||||
themeMode: themeMode == const $CopyWithPlaceholder()
|
themeMode: themeMode == const $CopyWithPlaceholder()
|
||||||
@@ -203,6 +211,11 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
|
|||||||
? _value.tabBarSwipeAction
|
? _value.tabBarSwipeAction
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
: tabBarSwipeAction as TabBarSwipeAction,
|
: tabBarSwipeAction as TabBarSwipeAction,
|
||||||
|
historyAutoCleanInterval:
|
||||||
|
historyAutoCleanInterval == const $CopyWithPlaceholder()
|
||||||
|
? _value.historyAutoCleanInterval
|
||||||
|
// ignore: cast_nullable_to_non_nullable
|
||||||
|
: historyAutoCleanInterval as Duration,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,6 +260,11 @@ GeneralSettings _$GeneralSettingsFromJson(Map<String, dynamic> json) =>
|
|||||||
_$TabBarSwipeActionEnumMap,
|
_$TabBarSwipeActionEnumMap,
|
||||||
json['tabBarSwipeAction'],
|
json['tabBarSwipeAction'],
|
||||||
),
|
),
|
||||||
|
historyAutoCleanInterval: json['historyAutoCleanInterval'] == null
|
||||||
|
? null
|
||||||
|
: Duration(
|
||||||
|
microseconds: (json['historyAutoCleanInterval'] as num).toInt(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$GeneralSettingsToJson(
|
Map<String, dynamic> _$GeneralSettingsToJson(
|
||||||
@@ -270,6 +288,7 @@ Map<String, dynamic> _$GeneralSettingsToJson(
|
|||||||
_$TabIntentOpenSettingEnumMap[instance.tabIntentOpenSetting]!,
|
_$TabIntentOpenSettingEnumMap[instance.tabIntentOpenSetting]!,
|
||||||
'autoHideTabBar': instance.autoHideTabBar,
|
'autoHideTabBar': instance.autoHideTabBar,
|
||||||
'tabBarSwipeAction': _$TabBarSwipeActionEnumMap[instance.tabBarSwipeAction]!,
|
'tabBarSwipeAction': _$TabBarSwipeActionEnumMap[instance.tabBarSwipeAction]!,
|
||||||
|
'historyAutoCleanInterval': instance.historyAutoCleanInterval.inMicroseconds,
|
||||||
};
|
};
|
||||||
|
|
||||||
const _$ThemeModeEnumMap = {
|
const _$ThemeModeEnumMap = {
|
||||||
|
|||||||
@@ -96,6 +96,10 @@ class GeneralSettingsRepository extends _$GeneralSettingsRepository {
|
|||||||
DriftSqlType.string,
|
DriftSqlType.string,
|
||||||
db.typeMapping,
|
db.typeMapping,
|
||||||
),
|
),
|
||||||
|
'historyAutoCleanInterval': settings['historyAutoCleanInterval']?.readAs(
|
||||||
|
DriftSqlType.int,
|
||||||
|
db.typeMapping,
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ final generalSettingsWithDefaultsProvider =
|
|||||||
typedef GeneralSettingsWithDefaultsRef =
|
typedef GeneralSettingsWithDefaultsRef =
|
||||||
AutoDisposeProviderRef<GeneralSettings>;
|
AutoDisposeProviderRef<GeneralSettings>;
|
||||||
String _$generalSettingsRepositoryHash() =>
|
String _$generalSettingsRepositoryHash() =>
|
||||||
r'0d207ae4ae9fd94b1257eb8aa4cfd73d4a47caf2';
|
r'3e2a07b8956094cd9376a254d9aedaa80a3c45c4';
|
||||||
|
|
||||||
/// See also [GeneralSettingsRepository].
|
/// See also [GeneralSettingsRepository].
|
||||||
@ProviderFor(GeneralSettingsRepository)
|
@ProviderFor(GeneralSettingsRepository)
|
||||||
|
|||||||
+28
@@ -69,4 +69,32 @@ class GeckoHistoryApiImpl() : GeckoHistoryApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun deleteVisit(
|
||||||
|
url: String,
|
||||||
|
timestamp: Long,
|
||||||
|
callback: (Result<Unit>) -> Unit
|
||||||
|
) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
components.core.historyStorage.deleteVisit(url, timestamp);
|
||||||
|
|
||||||
|
callback(Result.success(Unit))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deleteVisitsBetween(
|
||||||
|
startMillis: Long,
|
||||||
|
endMillis: Long,
|
||||||
|
callback: (Result<Unit>) -> Unit
|
||||||
|
) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
components.core.historyStorage.deleteVisitsBetween(startMillis, endMillis);
|
||||||
|
|
||||||
|
callback(Result.success(Unit))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+42
@@ -4750,6 +4750,8 @@ interface GeckoDeleteBrowsingDataController {
|
|||||||
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||||
interface GeckoHistoryApi {
|
interface GeckoHistoryApi {
|
||||||
fun getDetailedVisits(startMillis: Long, endMillis: Long, excludeTypes: List<VisitType>, callback: (Result<List<VisitInfo>>) -> Unit)
|
fun getDetailedVisits(startMillis: Long, endMillis: Long, excludeTypes: List<VisitType>, callback: (Result<List<VisitInfo>>) -> Unit)
|
||||||
|
fun deleteVisit(url: String, timestamp: Long, callback: (Result<Unit>) -> Unit)
|
||||||
|
fun deleteVisitsBetween(startMillis: Long, endMillis: Long, callback: (Result<Unit>) -> Unit)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/** The codec used by GeckoHistoryApi. */
|
/** The codec used by GeckoHistoryApi. */
|
||||||
@@ -4782,6 +4784,46 @@ interface GeckoHistoryApi {
|
|||||||
channel.setMessageHandler(null)
|
channel.setMessageHandler(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
run {
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoHistoryApi.deleteVisit$separatedMessageChannelSuffix", codec)
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler { message, reply ->
|
||||||
|
val args = message as List<Any?>
|
||||||
|
val urlArg = args[0] as String
|
||||||
|
val timestampArg = args[1] as Long
|
||||||
|
api.deleteVisit(urlArg, timestampArg) { result: Result<Unit> ->
|
||||||
|
val error = result.exceptionOrNull()
|
||||||
|
if (error != null) {
|
||||||
|
reply.reply(GeckoPigeonUtils.wrapError(error))
|
||||||
|
} else {
|
||||||
|
reply.reply(GeckoPigeonUtils.wrapResult(null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
run {
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoHistoryApi.deleteVisitsBetween$separatedMessageChannelSuffix", codec)
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler { message, reply ->
|
||||||
|
val args = message as List<Any?>
|
||||||
|
val startMillisArg = args[0] as Long
|
||||||
|
val endMillisArg = args[1] as Long
|
||||||
|
api.deleteVisitsBetween(startMillisArg, endMillisArg) { result: Result<Unit> ->
|
||||||
|
val error = result.exceptionOrNull()
|
||||||
|
if (error != null) {
|
||||||
|
reply.reply(GeckoPigeonUtils.wrapError(error))
|
||||||
|
} else {
|
||||||
|
reply.reply(GeckoPigeonUtils.wrapResult(null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,4 +24,15 @@ class GeckoHistoryService {
|
|||||||
VisitType.values.toSet().difference(types).toList(),
|
VisitType.values.toSet().difference(types).toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> deleteVisit(VisitInfo info) {
|
||||||
|
return _api.deleteVisit(info.url, info.visitTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> deleteVisitsBetween(DateTime start, DateTime end) {
|
||||||
|
return _api.deleteVisitsBetween(
|
||||||
|
start.millisecondsSinceEpoch,
|
||||||
|
end.millisecondsSinceEpoch,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5731,6 +5731,52 @@ class GeckoHistoryApi {
|
|||||||
return (pigeonVar_replyList[0] as List<Object?>?)!.cast<VisitInfo>();
|
return (pigeonVar_replyList[0] as List<Object?>?)!.cast<VisitInfo>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> deleteVisit(String url, int timestamp) async {
|
||||||
|
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoHistoryApi.deleteVisit$pigeonVar_messageChannelSuffix';
|
||||||
|
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||||
|
pigeonVar_channelName,
|
||||||
|
pigeonChannelCodec,
|
||||||
|
binaryMessenger: pigeonVar_binaryMessenger,
|
||||||
|
);
|
||||||
|
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[url, timestamp]);
|
||||||
|
final List<Object?>? pigeonVar_replyList =
|
||||||
|
await pigeonVar_sendFuture as List<Object?>?;
|
||||||
|
if (pigeonVar_replyList == null) {
|
||||||
|
throw _createConnectionError(pigeonVar_channelName);
|
||||||
|
} else if (pigeonVar_replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: pigeonVar_replyList[0]! as String,
|
||||||
|
message: pigeonVar_replyList[1] as String?,
|
||||||
|
details: pigeonVar_replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> deleteVisitsBetween(int startMillis, int endMillis) async {
|
||||||
|
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoHistoryApi.deleteVisitsBetween$pigeonVar_messageChannelSuffix';
|
||||||
|
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||||
|
pigeonVar_channelName,
|
||||||
|
pigeonChannelCodec,
|
||||||
|
binaryMessenger: pigeonVar_binaryMessenger,
|
||||||
|
);
|
||||||
|
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[startMillis, endMillis]);
|
||||||
|
final List<Object?>? pigeonVar_replyList =
|
||||||
|
await pigeonVar_sendFuture as List<Object?>?;
|
||||||
|
if (pigeonVar_replyList == null) {
|
||||||
|
throw _createConnectionError(pigeonVar_channelName);
|
||||||
|
} else if (pigeonVar_replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: pigeonVar_replyList[0]! as String,
|
||||||
|
message: pigeonVar_replyList[1] as String?,
|
||||||
|
details: pigeonVar_replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GeckoDownloadsApi {
|
class GeckoDownloadsApi {
|
||||||
|
|||||||
@@ -1248,6 +1248,12 @@ abstract class GeckoHistoryApi {
|
|||||||
int endMillis,
|
int endMillis,
|
||||||
List<VisitType> excludeTypes,
|
List<VisitType> excludeTypes,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@async
|
||||||
|
void deleteVisit(String url, int timestamp);
|
||||||
|
|
||||||
|
@async
|
||||||
|
void deleteVisitsBetween(int startMillis, int endMillis);
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostApi()
|
@HostApi()
|
||||||
|
|||||||
Reference in New Issue
Block a user