bookmarks feature initial

This commit is contained in:
Fabian Freund
2025-11-30 08:06:53 +01:00
parent d117ab4356
commit a2f4cf2cb1
26 changed files with 3169 additions and 198 deletions
+88
View File
@@ -79,6 +79,26 @@ part of 'routes.dart';
), ),
], ],
), ),
TypedGoRoute<BookmarkListRoute>(
name: 'BookmarkListRoute',
path: 'bookmarks/:entryGuid',
),
TypedGoRoute<BookmarkFolderAddRoute>(
name: 'BookmarkFolderAddRoute',
path: 'createFolder',
),
TypedGoRoute<BookmarkFolderEditRoute>(
name: 'BookmarkFolderEditRoute',
path: 'editFolder',
),
TypedGoRoute<BookmarkEntryAddRoute>(
name: 'BookmarkEntryAddRoute',
path: 'createEntry',
),
TypedGoRoute<BookmarkEntryEditRoute>(
name: 'BookmarkEntryEditRoute',
path: 'editEntry',
),
], ],
) )
class BrowserRoute extends GoRouteData with $BrowserRoute { class BrowserRoute extends GoRouteData with $BrowserRoute {
@@ -282,3 +302,71 @@ class EditProfileRoute extends GoRouteData with $EditProfileRoute {
); );
} }
} }
class BookmarkListRoute extends GoRouteData with $BookmarkListRoute {
final String entryGuid;
const BookmarkListRoute({required this.entryGuid});
@override
Widget build(BuildContext context, GoRouterState state) {
return BookmarkListScreen(entryGuid: entryGuid);
}
}
class BookmarkFolderAddRoute extends GoRouteData with $BookmarkFolderAddRoute {
final String? parentGuid;
const BookmarkFolderAddRoute({required this.parentGuid});
@override
Widget build(BuildContext context, GoRouterState state) {
return BookmarkFolderEditScreen(parentGuid: parentGuid, folder: null);
}
}
class BookmarkFolderEditRoute extends GoRouteData
with $BookmarkFolderEditRoute {
final String folder;
const BookmarkFolderEditRoute({required this.folder});
@override
Widget build(BuildContext context, GoRouterState state) {
return BookmarkFolderEditScreen(
folder: BookmarkFolder.fromJson(
jsonDecode(folder) as Map<String, dynamic>,
),
);
}
}
class BookmarkEntryAddRoute extends GoRouteData with $BookmarkEntryAddRoute {
final String bookmarkInfo;
const BookmarkEntryAddRoute({required this.bookmarkInfo});
@override
Widget build(BuildContext context, GoRouterState state) {
return BookmarkEntryEditScreen(
initialInfo: BookmarkInfo.decode(jsonDecode(bookmarkInfo) as Object),
exisitingEntry: null,
);
}
}
class BookmarkEntryEditRoute extends GoRouteData with $BookmarkEntryEditRoute {
final String bookmarkEntry;
const BookmarkEntryEditRoute({required this.bookmarkEntry});
@override
Widget build(BuildContext context, GoRouterState state) {
return BookmarkEntryEditScreen(
initialInfo: null,
exisitingEntry: BookmarkEntry.fromJson(
jsonDecode(bookmarkEntry) as Map<String, dynamic>,
),
);
}
}
+5
View File
@@ -20,6 +20,7 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:nullability/nullability.dart'; import 'package:nullability/nullability.dart';
import 'package:weblibre/core/routing/widgets/dialog_page.dart'; import 'package:weblibre/core/routing/widgets/dialog_page.dart';
@@ -32,6 +33,10 @@ import 'package:weblibre/features/bangs/presentation/screens/edit.dart';
import 'package:weblibre/features/bangs/presentation/screens/menu.dart'; import 'package:weblibre/features/bangs/presentation/screens/menu.dart';
import 'package:weblibre/features/bangs/presentation/screens/search.dart'; import 'package:weblibre/features/bangs/presentation/screens/search.dart';
import 'package:weblibre/features/bangs/presentation/screens/user.dart'; import 'package:weblibre/features/bangs/presentation/screens/user.dart';
import 'package:weblibre/features/geckoview/features/bookmarks/domain/entities/bookmark_item.dart';
import 'package:weblibre/features/geckoview/features/bookmarks/presentation/screens/bookmark_entry_edit.dart';
import 'package:weblibre/features/geckoview/features/bookmarks/presentation/screens/bookmark_folder_edit.dart';
import 'package:weblibre/features/geckoview/features/bookmarks/presentation/screens/bookmark_list.dart';
import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/open_shared_content.dart'; import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/open_shared_content.dart';
import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/tab_tree.dart'; import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/tab_tree.dart';
import 'package:weblibre/features/geckoview/features/browser/presentation/screens/browser.dart'; import 'package:weblibre/features/geckoview/features/browser/presentation/screens/browser.dart';
+162
View File
@@ -464,6 +464,31 @@ RouteBase get $browserRoute => GoRouteData.$route(
), ),
], ],
), ),
GoRouteData.$route(
path: 'bookmarks/:entryGuid',
name: 'BookmarkListRoute',
factory: $BookmarkListRoute._fromState,
),
GoRouteData.$route(
path: 'createFolder',
name: 'BookmarkFolderAddRoute',
factory: $BookmarkFolderAddRoute._fromState,
),
GoRouteData.$route(
path: 'editFolder',
name: 'BookmarkFolderEditRoute',
factory: $BookmarkFolderEditRoute._fromState,
),
GoRouteData.$route(
path: 'createEntry',
name: 'BookmarkEntryAddRoute',
factory: $BookmarkEntryAddRoute._fromState,
),
GoRouteData.$route(
path: 'editEntry',
name: 'BookmarkEntryEditRoute',
factory: $BookmarkEntryEditRoute._fromState,
),
], ],
); );
@@ -876,6 +901,143 @@ mixin $CreateProfileRoute on GoRouteData {
void replace(BuildContext context) => context.replace(location); void replace(BuildContext context) => context.replace(location);
} }
mixin $BookmarkListRoute on GoRouteData {
static BookmarkListRoute _fromState(GoRouterState state) =>
BookmarkListRoute(entryGuid: state.pathParameters['entryGuid']!);
BookmarkListRoute get _self => this as BookmarkListRoute;
@override
String get location => GoRouteData.$location(
'/browser/bookmarks/${Uri.encodeComponent(_self.entryGuid)}',
);
@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);
}
mixin $BookmarkFolderAddRoute on GoRouteData {
static BookmarkFolderAddRoute _fromState(GoRouterState state) =>
BookmarkFolderAddRoute(
parentGuid: state.uri.queryParameters['parent-guid'],
);
BookmarkFolderAddRoute get _self => this as BookmarkFolderAddRoute;
@override
String get location => GoRouteData.$location(
'/browser/createFolder',
queryParams: {
if (_self.parentGuid != null) 'parent-guid': _self.parentGuid,
},
);
@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);
}
mixin $BookmarkFolderEditRoute on GoRouteData {
static BookmarkFolderEditRoute _fromState(GoRouterState state) =>
BookmarkFolderEditRoute(folder: state.uri.queryParameters['folder']!);
BookmarkFolderEditRoute get _self => this as BookmarkFolderEditRoute;
@override
String get location => GoRouteData.$location(
'/browser/editFolder',
queryParams: {'folder': _self.folder},
);
@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);
}
mixin $BookmarkEntryAddRoute on GoRouteData {
static BookmarkEntryAddRoute _fromState(GoRouterState state) =>
BookmarkEntryAddRoute(
bookmarkInfo: state.uri.queryParameters['bookmark-info']!,
);
BookmarkEntryAddRoute get _self => this as BookmarkEntryAddRoute;
@override
String get location => GoRouteData.$location(
'/browser/createEntry',
queryParams: {'bookmark-info': _self.bookmarkInfo},
);
@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);
}
mixin $BookmarkEntryEditRoute on GoRouteData {
static BookmarkEntryEditRoute _fromState(GoRouterState state) =>
BookmarkEntryEditRoute(
bookmarkEntry: state.uri.queryParameters['bookmark-entry']!,
);
BookmarkEntryEditRoute get _self => this as BookmarkEntryEditRoute;
@override
String get location => GoRouteData.$location(
'/browser/editEntry',
queryParams: {'bookmark-entry': _self.bookmarkEntry},
);
@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);
}
T? _$convertMapValue<T>( T? _$convertMapValue<T>(
String key, String key,
Map<String, String> map, Map<String, String> map,
@@ -0,0 +1,145 @@
import 'package:copy_with_extension/copy_with_extension.dart';
import 'package:fast_equatable/fast_equatable.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:json_annotation/json_annotation.dart';
part 'bookmark_item.g.dart';
sealed class BookmarkItem {
abstract final String guid;
abstract final String? parentGuid;
abstract final String title;
abstract final int dateAdded;
abstract final int? position;
const BookmarkItem();
static BookmarkItem? parseRecursive(BookmarkNode node) {
return switch (node.type) {
BookmarkNodeType.item => BookmarkEntry(
guid: node.guid,
parentGuid: node.parentGuid,
url: Uri.parse(node.url!),
title: node.title ?? node.url!,
previewImageUrl: Uri.parse(node.url!),
position: node.position,
dateAdded: node.dateAdded,
),
BookmarkNodeType.folder => BookmarkFolder(
guid: node.guid,
parentGuid: node.parentGuid,
title: node.title ?? "Unnamed Folder",
position: node.position,
dateAdded: node.dateAdded,
children: node.children?.map(parseRecursive).nonNulls.toList(),
),
BookmarkNodeType.separator => null,
};
}
factory BookmarkItem.fromJson(Map<String, dynamic> json) {
return (json.containsKey('url'))
? BookmarkEntry.fromJson(json)
: BookmarkFolder.fromJson(json);
}
Map<String, dynamic> toJson();
BookmarkItem clone();
}
@CopyWith()
@JsonSerializable()
class BookmarkEntry extends BookmarkItem with FastEquatable {
@override
final String guid;
@override
final String? parentGuid;
final Uri url;
@override
final String title;
final Uri previewImageUrl;
@override
final int? position;
@override
final int dateAdded;
BookmarkEntry({
required this.guid,
required this.parentGuid,
required this.url,
required this.title,
required this.previewImageUrl,
required this.position,
required this.dateAdded,
});
factory BookmarkEntry.fromJson(Map<String, dynamic> json) =>
_$BookmarkEntryFromJson(json);
@override
Map<String, dynamic> toJson() => _$BookmarkEntryToJson(this);
@override
BookmarkItem clone() {
return copyWith();
}
@override
List<Object?> get hashParameters => [
guid,
parentGuid,
url,
title,
previewImageUrl,
position,
dateAdded,
];
}
@CopyWith()
@JsonSerializable()
class BookmarkFolder extends BookmarkItem with FastEquatable {
@override
final String guid;
@override
final String? parentGuid;
@override
final String title;
@override
final int? position;
@override
final int dateAdded;
final List<BookmarkItem>? children;
BookmarkFolder({
required this.guid,
required this.parentGuid,
required String title,
required this.position,
required this.dateAdded,
required this.children,
}) : title = bookmarkRootDisplayNames[guid] ?? title;
factory BookmarkFolder.fromJson(Map<String, dynamic> json) =>
_$BookmarkFolderFromJson(json);
@override
Map<String, dynamic> toJson() => _$BookmarkFolderToJson(this);
@override
BookmarkItem clone() {
return copyWith();
}
@override
List<Object?> get hashParameters => [
guid,
parentGuid,
title,
position,
dateAdded,
children,
];
}
@@ -0,0 +1,284 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'bookmark_item.dart';
// **************************************************************************
// CopyWithGenerator
// **************************************************************************
abstract class _$BookmarkEntryCWProxy {
BookmarkEntry guid(String guid);
BookmarkEntry parentGuid(String? parentGuid);
BookmarkEntry url(Uri url);
BookmarkEntry title(String title);
BookmarkEntry previewImageUrl(Uri previewImageUrl);
BookmarkEntry position(int? position);
BookmarkEntry dateAdded(int dateAdded);
/// Creates a new instance with the provided field values.
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `BookmarkEntry(...).copyWith.fieldName(value)`.
///
/// Example:
/// ```dart
/// BookmarkEntry(...).copyWith(id: 12, name: "My name")
/// ```
BookmarkEntry call({
String guid,
String? parentGuid,
Uri url,
String title,
Uri previewImageUrl,
int? position,
int dateAdded,
});
}
/// Callable proxy for `copyWith` functionality.
/// Use as `instanceOfBookmarkEntry.copyWith(...)` or call `instanceOfBookmarkEntry.copyWith.fieldName(value)` for a single field.
class _$BookmarkEntryCWProxyImpl implements _$BookmarkEntryCWProxy {
const _$BookmarkEntryCWProxyImpl(this._value);
final BookmarkEntry _value;
@override
BookmarkEntry guid(String guid) => call(guid: guid);
@override
BookmarkEntry parentGuid(String? parentGuid) => call(parentGuid: parentGuid);
@override
BookmarkEntry url(Uri url) => call(url: url);
@override
BookmarkEntry title(String title) => call(title: title);
@override
BookmarkEntry previewImageUrl(Uri previewImageUrl) =>
call(previewImageUrl: previewImageUrl);
@override
BookmarkEntry position(int? position) => call(position: position);
@override
BookmarkEntry dateAdded(int dateAdded) => call(dateAdded: dateAdded);
@override
/// Creates a new instance with the provided field values.
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `BookmarkEntry(...).copyWith.fieldName(value)`.
///
/// Example:
/// ```dart
/// BookmarkEntry(...).copyWith(id: 12, name: "My name")
/// ```
BookmarkEntry call({
Object? guid = const $CopyWithPlaceholder(),
Object? parentGuid = const $CopyWithPlaceholder(),
Object? url = const $CopyWithPlaceholder(),
Object? title = const $CopyWithPlaceholder(),
Object? previewImageUrl = const $CopyWithPlaceholder(),
Object? position = const $CopyWithPlaceholder(),
Object? dateAdded = const $CopyWithPlaceholder(),
}) {
return BookmarkEntry(
guid: guid == const $CopyWithPlaceholder() || guid == null
? _value.guid
// ignore: cast_nullable_to_non_nullable
: guid as String,
parentGuid: parentGuid == const $CopyWithPlaceholder()
? _value.parentGuid
// ignore: cast_nullable_to_non_nullable
: parentGuid as String?,
url: url == const $CopyWithPlaceholder() || url == null
? _value.url
// ignore: cast_nullable_to_non_nullable
: url as Uri,
title: title == const $CopyWithPlaceholder() || title == null
? _value.title
// ignore: cast_nullable_to_non_nullable
: title as String,
previewImageUrl:
previewImageUrl == const $CopyWithPlaceholder() ||
previewImageUrl == null
? _value.previewImageUrl
// ignore: cast_nullable_to_non_nullable
: previewImageUrl as Uri,
position: position == const $CopyWithPlaceholder()
? _value.position
// ignore: cast_nullable_to_non_nullable
: position as int?,
dateAdded: dateAdded == const $CopyWithPlaceholder() || dateAdded == null
? _value.dateAdded
// ignore: cast_nullable_to_non_nullable
: dateAdded as int,
);
}
}
extension $BookmarkEntryCopyWith on BookmarkEntry {
/// Returns a callable class used to build a new instance with modified fields.
/// Example: `instanceOfBookmarkEntry.copyWith(...)` or `instanceOfBookmarkEntry.copyWith.fieldName(...)`.
// ignore: library_private_types_in_public_api
_$BookmarkEntryCWProxy get copyWith => _$BookmarkEntryCWProxyImpl(this);
}
abstract class _$BookmarkFolderCWProxy {
BookmarkFolder guid(String guid);
BookmarkFolder parentGuid(String? parentGuid);
BookmarkFolder title(String title);
BookmarkFolder position(int? position);
BookmarkFolder dateAdded(int dateAdded);
BookmarkFolder children(List<BookmarkItem>? children);
/// Creates a new instance with the provided field values.
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `BookmarkFolder(...).copyWith.fieldName(value)`.
///
/// Example:
/// ```dart
/// BookmarkFolder(...).copyWith(id: 12, name: "My name")
/// ```
BookmarkFolder call({
String guid,
String? parentGuid,
String title,
int? position,
int dateAdded,
List<BookmarkItem>? children,
});
}
/// Callable proxy for `copyWith` functionality.
/// Use as `instanceOfBookmarkFolder.copyWith(...)` or call `instanceOfBookmarkFolder.copyWith.fieldName(value)` for a single field.
class _$BookmarkFolderCWProxyImpl implements _$BookmarkFolderCWProxy {
const _$BookmarkFolderCWProxyImpl(this._value);
final BookmarkFolder _value;
@override
BookmarkFolder guid(String guid) => call(guid: guid);
@override
BookmarkFolder parentGuid(String? parentGuid) => call(parentGuid: parentGuid);
@override
BookmarkFolder title(String title) => call(title: title);
@override
BookmarkFolder position(int? position) => call(position: position);
@override
BookmarkFolder dateAdded(int dateAdded) => call(dateAdded: dateAdded);
@override
BookmarkFolder children(List<BookmarkItem>? children) =>
call(children: children);
@override
/// Creates a new instance with the provided field values.
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `BookmarkFolder(...).copyWith.fieldName(value)`.
///
/// Example:
/// ```dart
/// BookmarkFolder(...).copyWith(id: 12, name: "My name")
/// ```
BookmarkFolder call({
Object? guid = const $CopyWithPlaceholder(),
Object? parentGuid = const $CopyWithPlaceholder(),
Object? title = const $CopyWithPlaceholder(),
Object? position = const $CopyWithPlaceholder(),
Object? dateAdded = const $CopyWithPlaceholder(),
Object? children = const $CopyWithPlaceholder(),
}) {
return BookmarkFolder(
guid: guid == const $CopyWithPlaceholder() || guid == null
? _value.guid
// ignore: cast_nullable_to_non_nullable
: guid as String,
parentGuid: parentGuid == const $CopyWithPlaceholder()
? _value.parentGuid
// ignore: cast_nullable_to_non_nullable
: parentGuid as String?,
title: title == const $CopyWithPlaceholder() || title == null
? _value.title
// ignore: cast_nullable_to_non_nullable
: title as String,
position: position == const $CopyWithPlaceholder()
? _value.position
// ignore: cast_nullable_to_non_nullable
: position as int?,
dateAdded: dateAdded == const $CopyWithPlaceholder() || dateAdded == null
? _value.dateAdded
// ignore: cast_nullable_to_non_nullable
: dateAdded as int,
children: children == const $CopyWithPlaceholder()
? _value.children
// ignore: cast_nullable_to_non_nullable
: children as List<BookmarkItem>?,
);
}
}
extension $BookmarkFolderCopyWith on BookmarkFolder {
/// Returns a callable class used to build a new instance with modified fields.
/// Example: `instanceOfBookmarkFolder.copyWith(...)` or `instanceOfBookmarkFolder.copyWith.fieldName(...)`.
// ignore: library_private_types_in_public_api
_$BookmarkFolderCWProxy get copyWith => _$BookmarkFolderCWProxyImpl(this);
}
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
BookmarkEntry _$BookmarkEntryFromJson(Map<String, dynamic> json) =>
BookmarkEntry(
guid: json['guid'] as String,
parentGuid: json['parentGuid'] as String?,
url: Uri.parse(json['url'] as String),
title: json['title'] as String,
previewImageUrl: Uri.parse(json['previewImageUrl'] as String),
position: (json['position'] as num?)?.toInt(),
dateAdded: (json['dateAdded'] as num).toInt(),
);
Map<String, dynamic> _$BookmarkEntryToJson(BookmarkEntry instance) =>
<String, dynamic>{
'guid': instance.guid,
'parentGuid': instance.parentGuid,
'url': instance.url.toString(),
'title': instance.title,
'previewImageUrl': instance.previewImageUrl.toString(),
'position': instance.position,
'dateAdded': instance.dateAdded,
};
BookmarkFolder _$BookmarkFolderFromJson(Map<String, dynamic> json) =>
BookmarkFolder(
guid: json['guid'] as String,
parentGuid: json['parentGuid'] as String?,
title: json['title'] as String,
position: (json['position'] as num?)?.toInt(),
dateAdded: (json['dateAdded'] as num).toInt(),
children: (json['children'] as List<dynamic>?)
?.map((e) => BookmarkItem.fromJson(e as Map<String, dynamic>))
.toList(),
);
Map<String, dynamic> _$BookmarkFolderToJson(BookmarkFolder instance) =>
<String, dynamic>{
'guid': instance.guid,
'parentGuid': instance.parentGuid,
'title': instance.title,
'position': instance.position,
'dateAdded': instance.dateAdded,
'children': instance.children?.map((e) => e.toJson()).toList(),
};
@@ -0,0 +1,68 @@
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:weblibre/features/geckoview/features/bookmarks/domain/entities/bookmark_item.dart';
import 'package:weblibre/features/geckoview/features/bookmarks/domain/repositories/bookmarks.dart';
part 'bookmarks.g.dart';
T? _selectChildRecursive<T extends BookmarkItem>(
List<BookmarkItem> children,
String guid,
) {
for (final child in children) {
if (child.guid == guid && child is T) {
return child;
}
if (child case final BookmarkFolder folder) {
if (folder.children != null) {
final result = _selectChildRecursive<T>(folder.children!, guid);
if (result != null) {
return result;
}
}
}
}
return null;
}
T _cloneAndFilterChildrenType<T extends BookmarkItem>(T node) {
if (node is BookmarkFolder) {
if (node.children != null) {
return node.copyWith.children(
node.children
?.whereType<T>()
.map((e) => _cloneAndFilterChildrenType<T>(e))
.toList(),
)
as T;
}
}
return node.clone() as T;
}
@Riverpod()
AsyncValue<T?> bookmarks<T extends BookmarkItem>(Ref ref, String entryGuid) {
final bookmarksAsync = ref.watch(bookmarksRepositoryProvider);
return bookmarksAsync.whenData((bookmarkNode) {
T? selectedNode;
if (bookmarkNode != null && bookmarkNode is T) {
if (bookmarkNode.guid == entryGuid) {
selectedNode = bookmarkNode;
} else if (bookmarkNode case final BookmarkFolder folder) {
if (folder.children != null) {
selectedNode = _selectChildRecursive<T>(folder.children!, entryGuid);
}
}
}
if (selectedNode != null) {
return _cloneAndFilterChildrenType<T>(selectedNode);
}
return null;
});
}
@@ -0,0 +1,110 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'bookmarks.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
@ProviderFor(bookmarks)
const bookmarksProvider = BookmarksFamily._();
final class BookmarksProvider<T extends BookmarkItem>
extends $FunctionalProvider<AsyncValue<T?>, AsyncValue<T?>, AsyncValue<T?>>
with $Provider<AsyncValue<T?>> {
const BookmarksProvider._({
required BookmarksFamily super.from,
required String super.argument,
}) : super(
retry: null,
name: r'bookmarksProvider',
isAutoDispose: true,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$bookmarksHash();
@override
String toString() {
return r'bookmarksProvider'
'<${T}>'
'($argument)';
}
@$internal
@override
$ProviderElement<AsyncValue<T?>> $createElement($ProviderPointer pointer) =>
$ProviderElement(pointer);
@override
AsyncValue<T?> create(Ref ref) {
final argument = this.argument as String;
return bookmarks<T>(ref, argument);
}
$R _captureGenerics<$R>($R Function<T extends BookmarkItem>() cb) {
return cb<T>();
}
/// {@macro riverpod.override_with_value}
Override overrideWithValue(AsyncValue<T?> value) {
return $ProviderOverride(
origin: this,
providerOverride: $SyncValueProvider<AsyncValue<T?>>(value),
);
}
@override
bool operator ==(Object other) {
return other is BookmarksProvider &&
other.runtimeType == runtimeType &&
other.argument == argument;
}
@override
int get hashCode {
return Object.hash(runtimeType, argument);
}
}
String _$bookmarksHash() => r'dfa19aea04f352b8a6cefe35a0b283c9fddb214f';
final class BookmarksFamily extends $Family {
const BookmarksFamily._()
: super(
retry: null,
name: r'bookmarksProvider',
dependencies: null,
$allTransitiveDependencies: null,
isAutoDispose: true,
);
BookmarksProvider<T> call<T extends BookmarkItem>(String entryGuid) =>
BookmarksProvider<T>._(argument: entryGuid, from: this);
@override
String toString() => r'bookmarksProvider';
/// {@macro riverpod.override_with}
Override overrideWith(
AsyncValue<T?> Function<T extends BookmarkItem>(Ref ref, String args)
create,
) => $FamilyOverride(
from: this,
createElement: (pointer) {
final provider = pointer.origin as BookmarksProvider;
return provider._captureGenerics(<T extends BookmarkItem>() {
provider as BookmarksProvider<T>;
final argument = provider.argument as String;
return provider
.$view(create: (ref) => create(ref, argument))
.$createElement(pointer);
});
},
);
}
@@ -0,0 +1,60 @@
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:nullability/nullability.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:weblibre/features/geckoview/features/bookmarks/domain/entities/bookmark_item.dart';
part 'bookmarks.g.dart';
@Riverpod(keepAlive: true)
class BookmarksRepository extends _$BookmarksRepository {
final _service = GeckoBookmarksService();
Future<void> addBookmark({
required String parentGuid,
required Uri url,
required String title,
}) async {
await _service.addItem(parentGuid, url, title, null);
ref.invalidateSelf();
}
Future<void> addFolder({
required String parentGuid,
required String title,
}) async {
await _service.addFolder(parentGuid, title, null);
ref.invalidateSelf();
}
Future<void> editBookmark({
required String guid,
String? title,
Uri? url,
String? parentGuid,
}) async {
await _service.updateNode(
guid,
BookmarkInfo(title: title, url: url?.toString(), parentGuid: parentGuid),
);
ref.invalidateSelf();
}
Future<void> editFolder({required String guid, required String title}) async {
await _service.updateNode(guid, BookmarkInfo(title: title));
ref.invalidateSelf();
}
Future<void> delete(String guid) async {
await _service.deleteNode(guid);
ref.invalidateSelf();
}
@override
Future<BookmarkItem?> build() async {
final node = await _service.getTree(
BookmarkRoot.mobile.id,
recursive: true,
);
return node.mapNotNull(BookmarkItem.parseRecursive);
}
}
@@ -0,0 +1,56 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'bookmarks.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint, type=warning
@ProviderFor(BookmarksRepository)
const bookmarksRepositoryProvider = BookmarksRepositoryProvider._();
final class BookmarksRepositoryProvider
extends $AsyncNotifierProvider<BookmarksRepository, BookmarkItem?> {
const BookmarksRepositoryProvider._()
: super(
from: null,
argument: null,
retry: null,
name: r'bookmarksRepositoryProvider',
isAutoDispose: false,
dependencies: null,
$allTransitiveDependencies: null,
);
@override
String debugGetCreateSourceHash() => _$bookmarksRepositoryHash();
@$internal
@override
BookmarksRepository create() => BookmarksRepository();
}
String _$bookmarksRepositoryHash() =>
r'b8fa5b5699c053b91fcabd46dc97b7192e32d068';
abstract class _$BookmarksRepository extends $AsyncNotifier<BookmarkItem?> {
FutureOr<BookmarkItem?> build();
@$mustCallSuper
@override
void runBuild() {
final created = build();
final ref = this.ref as $Ref<AsyncValue<BookmarkItem?>, BookmarkItem?>;
final element =
ref.element
as $ClassProviderElement<
AnyNotifier<AsyncValue<BookmarkItem?>, BookmarkItem?>,
AsyncValue<BookmarkItem?>,
Object?,
Object?
>;
element.handleValue(ref, created);
}
}
@@ -0,0 +1,277 @@
import 'package:animated_tree_view/animated_tree_view.dart';
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:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:weblibre/features/geckoview/features/bookmarks/domain/entities/bookmark_item.dart';
import 'package:weblibre/features/geckoview/features/bookmarks/domain/providers/bookmarks.dart';
import 'package:weblibre/features/geckoview/features/bookmarks/domain/repositories/bookmarks.dart';
import 'package:weblibre/presentation/widgets/failure_widget.dart';
import 'package:weblibre/utils/form_validators.dart';
import 'package:weblibre/utils/uri_parser.dart' as uri_parser;
class BookmarkEntryEditScreen extends HookConsumerWidget {
final BookmarkInfo? initialInfo;
final BookmarkEntry? exisitingEntry;
const BookmarkEntryEditScreen({
required this.exisitingEntry,
required this.initialInfo,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final formKey = useMemoized(() => GlobalKey<FormState>());
final folderList = ref.watch(
bookmarksProvider<BookmarkFolder>(BookmarkRoot.mobile.id),
);
final nameTextController = useTextEditingController(
text: initialInfo?.title ?? exisitingEntry?.title,
);
final urlTextController = useTextEditingController(
text: initialInfo?.url ?? exisitingEntry?.url.toString(),
);
final parentGuid = useState(
initialInfo?.parentGuid ??
exisitingEntry?.parentGuid ??
BookmarkRoot.mobile.id,
);
return Scaffold(
appBar: AppBar(
title: (exisitingEntry != null)
? const Text('Edit Bookmark')
: const Text('Create Bookmark'),
actions: [
IconButton(
onPressed: () async {
if (formKey.currentState?.validate() ?? false) {
final newUrl = uri_parser.tryParseUrl(
urlTextController.text,
eagerParsing: true,
)!;
if (exisitingEntry != null) {
await ref
.read(bookmarksRepositoryProvider.notifier)
.editBookmark(
guid: exisitingEntry!.guid,
title:
(nameTextController.text != exisitingEntry!.title)
? nameTextController.text
: null,
parentGuid:
(parentGuid.value != exisitingEntry!.parentGuid)
? parentGuid.value
: null,
url: (newUrl != exisitingEntry!.url) ? newUrl : null,
);
if (context.mounted) {
context.pop();
}
} else {
await ref
.read(bookmarksRepositoryProvider.notifier)
.addBookmark(
parentGuid: parentGuid.value,
title: nameTextController.text,
url: newUrl,
);
if (context.mounted) {
context.pop();
}
}
}
},
icon: const Icon(Icons.check),
),
],
),
body: SafeArea(
child: Form(
key: formKey,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: ListView(
children: [
TextFormField(
controller: nameTextController,
decoration: const InputDecoration(
label: Text('Name'),
floatingLabelBehavior: FloatingLabelBehavior.always,
),
minLines: 1,
maxLines: 3,
validator: validateRequired,
),
const SizedBox(height: 8),
TextFormField(
controller: urlTextController,
keyboardType: TextInputType.url,
minLines: 1,
maxLines: 10,
decoration: const InputDecoration(
label: Text('URL'),
hintText: 'https://example.com/',
floatingLabelBehavior: FloatingLabelBehavior.always,
),
validator: (value) {
return validateUrl(
value,
onlyHttpProtocol: true,
eagerParsing: true,
);
},
),
const SizedBox(height: 16),
Text('Folder', style: Theme.of(context).textTheme.labelMedium),
folderList.when(
data: (list) {
TreeNode<BookmarkFolder> addChildren(
TreeNode<BookmarkFolder>? parent,
BookmarkFolder item,
) {
final node = TreeNode(
key: item.guid,
data: item,
parent: parent,
);
final targetNode = (parent?..add(node)) ?? node;
if (item.children != null) {
for (final child in item.children!) {
addChildren(node, child as BookmarkFolder);
}
}
return targetNode;
}
final root = (list != null)
? addChildren(null, list)
: TreeNode<BookmarkFolder>.root();
return TreeView.simple(
tree: root,
shrinkWrap: true,
onTreeReady: (controller) {
controller.expandAllChildren(root);
},
expansionIndicatorBuilder: (context, tree) =>
ChevronIndicator.upDown(
tree: tree,
padding: const EdgeInsets.symmetric(
vertical: 16.0,
horizontal: 12.0,
),
),
builder: (context, item) {
final isSelected = item.data?.guid == parentGuid.value;
return Padding(
padding: const EdgeInsets.only(right: 16.0),
child: switch (item.data) {
final BookmarkFolder folder => ListTile(
selected: isSelected,
leading: (item.isExpanded)
? const Icon(MdiIcons.folderOpen)
: const Icon(MdiIcons.folder),
trailing: isSelected
? const Icon(Icons.check)
: null,
title: Text(folder.title),
onTap: () {
parentGuid.value = folder.guid;
},
),
null => const SizedBox.shrink(),
},
);
},
);
},
error: (error, stackTrace) => Center(
child: FailureWidget(
title: 'Failed to load Bookmark Folders',
exception: error,
onRetry: () {
// ignore: unused_result
ref.refresh(
bookmarksProvider<BookmarkFolder>(
BookmarkRoot.mobile.id,
),
);
},
),
),
loading: () => const SizedBox.shrink(),
),
const SizedBox(height: 16),
if (exisitingEntry != null)
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(MdiIcons.bookmarkRemove),
onPressed: () async {
final result = await showDialog<bool?>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
icon: const Icon(Icons.warning),
title: const Text('Delete Bookmark'),
content: const Text(
'Are you sure you want to delete this Bookmark?',
),
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(bookmarksRepositoryProvider.notifier)
.delete(exisitingEntry!.guid);
if (context.mounted) {
context.pop();
}
}
},
),
),
],
),
),
),
),
);
}
}
@@ -0,0 +1,133 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.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:weblibre/features/geckoview/features/bookmarks/domain/entities/bookmark_item.dart';
import 'package:weblibre/features/geckoview/features/bookmarks/domain/repositories/bookmarks.dart';
import 'package:weblibre/utils/form_validators.dart';
class BookmarkFolderEditScreen extends HookConsumerWidget {
final String? parentGuid;
final BookmarkFolder? folder;
const BookmarkFolderEditScreen({required this.folder, this.parentGuid});
@override
Widget build(BuildContext context, WidgetRef ref) {
final formKey = useMemoized(() => GlobalKey<FormState>());
final nameTextController = useTextEditingController(text: folder?.title);
return Scaffold(
appBar: AppBar(
title: (folder != null)
? const Text('Edit Folder')
: const Text('Create Folder'),
actions: [
IconButton(
onPressed: () async {
if (formKey.currentState?.validate() ?? false) {
if (folder != null) {
await ref
.read(bookmarksRepositoryProvider.notifier)
.editFolder(
guid: folder!.guid,
title: nameTextController.text,
);
if (context.mounted) {
context.pop();
}
} else {
await ref
.read(bookmarksRepositoryProvider.notifier)
.addFolder(
parentGuid: parentGuid ?? BookmarkRoot.mobile.id,
title: nameTextController.text,
);
if (context.mounted) {
context.pop();
}
}
}
},
icon: const Icon(Icons.check),
),
],
),
body: Form(
key: formKey,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: ListView(
children: [
TextFormField(
controller: nameTextController,
decoration: const InputDecoration(
label: Text('Name'),
floatingLabelBehavior: FloatingLabelBehavior.always,
),
validator: validateRequired,
),
const SizedBox(height: 16),
if (folder != null)
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(
icon: const Icon(Icons.warning),
title: const Text('Delete Folder'),
content: const Text(
'Are you sure you want to delete this Folder including all bookmarks?',
),
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(bookmarksRepositoryProvider.notifier)
.delete(folder!.guid);
if (context.mounted) {
context.pop();
}
}
},
),
),
],
),
),
),
);
}
}
@@ -0,0 +1,198 @@
import 'dart:convert';
import 'package:animated_tree_view/animated_tree_view.dart';
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:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:weblibre/core/routing/routes.dart';
import 'package:weblibre/features/geckoview/features/bookmarks/domain/entities/bookmark_item.dart';
import 'package:weblibre/features/geckoview/features/bookmarks/domain/providers/bookmarks.dart';
import 'package:weblibre/presentation/hooks/menu_controller.dart';
import 'package:weblibre/presentation/widgets/failure_widget.dart';
import 'package:weblibre/presentation/widgets/uri_breadcrumb.dart';
import 'package:weblibre/presentation/widgets/url_icon.dart';
class BookmarkListScreen extends HookConsumerWidget {
final String entryGuid;
const BookmarkListScreen({super.key, required this.entryGuid});
@override
Widget build(BuildContext context, WidgetRef ref) {
final bookmarkList = ref.watch(bookmarksProvider<BookmarkItem>(entryGuid));
return Scaffold(
appBar: AppBar(title: const Text('Bookmarks')),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.only(left: 12.0),
child: bookmarkList.when(
data: (list) {
TreeNode<BookmarkItem> addChildren(
TreeNode<BookmarkItem>? parent,
BookmarkItem item,
) {
final node = TreeNode(
key: item.guid,
data: item,
parent: parent,
);
final targetNode = (parent?..add(node)) ?? node;
if (item is BookmarkFolder && item.children != null) {
for (final child in item.children!) {
addChildren(node, child);
}
}
return targetNode;
}
final root = (list != null)
? addChildren(null, list)
: TreeNode<BookmarkItem>.root();
return TreeView.simple(
tree: root,
onTreeReady: (controller) {
controller.expandNode(root);
},
expansionIndicatorBuilder: (context, tree) =>
ChevronIndicator.upDown(
tree: tree,
padding: const EdgeInsets.symmetric(
vertical: 16.0,
horizontal: 12.0,
),
),
builder: (context, item) {
return switch (item.data) {
final BookmarkEntry bookmark => ListTile(
contentPadding: EdgeInsets.zero,
leading: UrlIcon([bookmark.url], iconSize: 34.0),
trailing: IconButton(
icon: const Icon(Icons.edit),
onPressed: () async {
await BookmarkEntryEditRoute(
bookmarkEntry: jsonEncode(bookmark.toJson()),
).push(context);
},
),
title: Text(bookmark.title),
subtitle: UriBreadcrumb(uri: bookmark.url),
onTap: () async {
final result = await OpenSharedContentRoute(
sharedUrl: bookmark.url.toString(),
).push<bool>(context);
if (result == true) {
if (context.mounted) {
const BrowserRoute().go(context);
}
}
},
),
final BookmarkFolder folder => Padding(
padding: const EdgeInsets.only(right: 42.0),
child: HookBuilder(
builder: (context) {
final controller = useMenuController();
return ListTile(
contentPadding: EdgeInsets.zero,
leading: (item.isExpanded)
? const Icon(MdiIcons.folderOpen)
: const Icon(MdiIcons.folder),
title: Text(folder.title),
trailing: MenuAnchor(
controller: controller,
builder: (context, controller, child) => InkWell(
onTap: () {
if (controller.isOpen) {
controller.close();
} else {
controller.open();
}
},
child: const Padding(
padding: EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 15.0,
),
child: Icon(MdiIcons.dotsVertical),
),
),
menuChildren: [
if (!bookmarkRootIds.contains(folder.guid))
MenuItemButton(
leadingIcon: const Icon(
MdiIcons.folderEdit,
),
child: const Text('Edit'),
onPressed: () async {
await BookmarkFolderEditRoute(
folder: jsonEncode(folder.toJson()),
).push(context);
},
),
MenuItemButton(
leadingIcon: const Icon(MdiIcons.folderPlus),
child: const Text('Add Subfolder'),
onPressed: () async {
await BookmarkFolderAddRoute(
parentGuid: folder.guid,
).push(context);
},
),
MenuItemButton(
leadingIcon: const Icon(
MdiIcons.bookmarkPlus,
),
child: const Text('Add Bookmark'),
onPressed: () async {
await BookmarkEntryAddRoute(
bookmarkInfo: jsonEncode(
BookmarkInfo(
parentGuid: folder.guid,
).encode(),
),
).push(context);
},
),
],
),
onTap: () async {
if (folder.guid != entryGuid) {
await BookmarkListRoute(
entryGuid: folder.guid,
).push(context);
}
},
);
},
),
),
null => const Center(child: Text('Empty')),
};
},
);
},
error: (error, stackTrace) => Center(
child: FailureWidget(
title: 'Failed to load Bookmarks',
exception: error,
onRetry: () {
// ignore: unused_result
ref.refresh(bookmarksProvider<BookmarkItem>(entryGuid));
},
),
),
loading: () => const SizedBox.shrink(),
),
),
),
);
}
}
@@ -53,7 +53,7 @@ class OpenSharedContent extends HookConsumerWidget {
); );
if (context.mounted) { if (context.mounted) {
context.pop(); context.pop(true);
} }
} }
} }
@@ -437,6 +437,15 @@ class BrowserBottomAppBar extends HookConsumerWidget {
), ),
), ),
const Divider(), const Divider(),
MenuItemButton(
onPressed: () async {
await BookmarkListRoute(
entryGuid: BookmarkRoot.mobile.id,
).push(context);
},
leadingIcon: const Icon(MdiIcons.bookmarkMultiple),
child: const Text('Bookmarks'),
),
MenuItemButton( MenuItemButton(
onPressed: () async { onPressed: () async {
await const BangMenuRoute().push(context); await const BangMenuRoute().push(context);
@@ -17,12 +17,14 @@
* 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 'dart:convert';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.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_material_design_icons/flutter_material_design_icons.dart';
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:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
@@ -164,6 +166,22 @@ class TabMenu extends HookConsumerWidget {
await ui_helper.launchUrlFeedback(context, tabState.url); await ui_helper.launchUrlFeedback(context, tabState.url);
}, },
), ),
MenuItemButton(
leadingIcon: const Icon(MdiIcons.bookmarkPlus),
child: const Text('Add Bookmark'),
onPressed: () async {
final tabState = ref.read(tabStateProvider(selectedTabId))!;
await BookmarkEntryAddRoute(
bookmarkInfo: jsonEncode(
BookmarkInfo(
title: tabState.title,
url: tabState.url.toString(),
).encode(),
),
).push(context);
},
),
MenuItemButton( MenuItemButton(
leadingIcon: const Icon(MdiIcons.tabPlus), leadingIcon: const Icon(MdiIcons.tabPlus),
child: const Text('Clone tab'), child: const Text('Clone tab'),
@@ -27,14 +27,14 @@ class ProfileRepository extends _$ProfileRepository {
throw Exception('Could not create profile'); throw Exception('Could not create profile');
} }
state = await AsyncValue.guard(_readProfiles); ref.invalidateSelf();
return profile; return profile;
} }
Future<void> updateProfileMetadata(Profile profile) async { Future<void> updateProfileMetadata(Profile profile) async {
await filesystem.updateProfileMetadata(profile); await filesystem.updateProfileMetadata(profile);
state = await AsyncValue.guard(_readProfiles); ref.invalidateSelf();
} }
Future<bool> deleteProfile(String id) async { Future<bool> deleteProfile(String id) async {
@@ -45,7 +45,7 @@ class ProfileRepository extends _$ProfileRepository {
await filesystem.getProfileDir(uuid).delete(recursive: true); await filesystem.getProfileDir(uuid).delete(recursive: true);
state = await AsyncValue.guard(_readProfiles); ref.invalidateSelf();
return true; return true;
} }
@@ -33,7 +33,7 @@ final class ProfileRepositoryProvider
ProfileRepository create() => ProfileRepository(); ProfileRepository create() => ProfileRepository();
} }
String _$profileRepositoryHash() => r'1357d42738d40e8e447ab8879292e81ad7b80b61'; String _$profileRepositoryHash() => r'249240ce0e775d4fd9ee3558e6b05326d09d79de';
abstract class _$ProfileRepository extends $AsyncNotifier<List<Profile>> { abstract class _$ProfileRepository extends $AsyncNotifier<List<Profile>> {
FutureOr<List<Profile>> build(); FutureOr<List<Profile>> build();
+1
View File
@@ -8,6 +8,7 @@ environment:
sdk: '>=3.8.0 <4.0.0' sdk: '>=3.8.0 <4.0.0'
dependencies: dependencies:
animated_tree_view: ^2.3.0
background_fetch: ^1.5.0 background_fetch: ^1.5.0
collection: ^1.19.1 collection: ^1.19.1
copy_with_extension: ^10.0.1 copy_with_extension: ^10.0.1
@@ -0,0 +1,211 @@
package eu.weblibre.flutter_mozilla_components.api
import eu.weblibre.flutter_mozilla_components.GlobalComponents
import eu.weblibre.flutter_mozilla_components.pigeons.BookmarkInfo
import eu.weblibre.flutter_mozilla_components.pigeons.BookmarkNode
import eu.weblibre.flutter_mozilla_components.pigeons.BookmarkNodeType
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoBookmarksApi
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class GeckoBookmarksApiImpl() : GeckoBookmarksApi {
companion object {
private val coroutineScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
}
private val components by lazy {
requireNotNull(GlobalComponents.components) { "Components not initialized" }
}
fun mozilla.components.concept.storage.BookmarkNode.toPigeonBookmarkNode(): BookmarkNode {
return BookmarkNode(
type = when (this.type) {
mozilla.components.concept.storage.BookmarkNodeType.ITEM -> BookmarkNodeType.ITEM
mozilla.components.concept.storage.BookmarkNodeType.FOLDER -> BookmarkNodeType.FOLDER
mozilla.components.concept.storage.BookmarkNodeType.SEPARATOR -> BookmarkNodeType.SEPARATOR
},
guid = this.guid,
parentGuid = this.parentGuid,
position = this.position?.toLong(),
title = this.title,
url = this.url,
dateAdded = this.dateAdded,
lastModified = this.lastModified,
children = this.children?.map { it.toPigeonBookmarkNode() }
)
}
fun BookmarkNode.toConceptStorageBookmarkNode(): mozilla.components.concept.storage.BookmarkNode {
return mozilla.components.concept.storage.BookmarkNode(
type = when (this.type) {
BookmarkNodeType.ITEM -> mozilla.components.concept.storage.BookmarkNodeType.ITEM
BookmarkNodeType.FOLDER -> mozilla.components.concept.storage.BookmarkNodeType.FOLDER
BookmarkNodeType.SEPARATOR -> mozilla.components.concept.storage.BookmarkNodeType.SEPARATOR
},
guid = this.guid,
parentGuid = this.parentGuid,
position = this.position?.toUInt(),
title = this.title,
url = this.url,
dateAdded = this.dateAdded,
lastModified = this.lastModified,
children = this.children?.map { it.toConceptStorageBookmarkNode() }
)
}
override fun getTree(
guid: String,
recursive: Boolean,
callback: (Result<BookmarkNode?>) -> Unit
) {
coroutineScope.launch {
withContext(Dispatchers.Main) {
val node = components.core.bookmarksStorage.getTree(guid, recursive)
node.fold(
{ node ->
callback(Result.success(node?.toPigeonBookmarkNode()))
},
{ e -> callback(Result.failure(e)) })
}
}
}
override fun getBookmark(
guid: String,
callback: (Result<BookmarkNode?>) -> Unit
) {
coroutineScope.launch {
withContext(Dispatchers.Main) {
components.core.bookmarksStorage.getBookmark(guid).fold(
{ node -> callback(Result.success(node?.toPigeonBookmarkNode())) },
{ e -> callback(Result.failure(e)) }
)
}
}
}
override fun getBookmarksWithUrl(
url: String,
callback: (Result<List<BookmarkNode>>) -> Unit
) {
coroutineScope.launch {
withContext(Dispatchers.Main) {
components.core.bookmarksStorage.getBookmarksWithUrl(url).fold(
{ nodes -> callback(Result.success(nodes.map { it.toPigeonBookmarkNode() })) },
{ e -> callback(Result.failure(e)) }
)
}
}
}
override fun getRecentBookmarks(
limit: Long,
maxAge: Long?,
currentTime: Long,
callback: (Result<List<BookmarkNode>>) -> Unit
) {
coroutineScope.launch {
withContext(Dispatchers.Main) {
components.core.bookmarksStorage.getRecentBookmarks(
limit = limit.toInt(),
maxAge = maxAge,
currentTime = currentTime
).fold(
{ nodes -> callback(Result.success(nodes.map { it.toPigeonBookmarkNode() })) },
{ e -> callback(Result.failure(e)) }
)
}
}
}
override fun searchBookmarks(
query: String,
limit: Long,
callback: (Result<List<BookmarkNode>>) -> Unit
) {
coroutineScope.launch {
withContext(Dispatchers.Main) {
components.core.bookmarksStorage.searchBookmarks(query, limit.toInt()).fold(
{ nodes -> callback(Result.success(nodes.map { it.toPigeonBookmarkNode() })) },
{ e -> callback(Result.failure(e)) }
)
}
}
}
override fun addItem(
parentGuid: String,
url: String,
title: String,
position: Long?,
callback: (Result<String>) -> Unit
) {
coroutineScope.launch {
withContext(Dispatchers.Main) {
components.core.bookmarksStorage.addItem(parentGuid, url, title, position?.toUInt())
.fold(
{ guid -> callback(Result.success(guid)) },
{ e -> callback(Result.failure(e)) }
)
}
}
}
override fun addFolder(
parentGuid: String,
title: String,
position: Long?,
callback: (Result<String>) -> Unit
) {
coroutineScope.launch {
withContext(Dispatchers.Main) {
components.core.bookmarksStorage.addFolder(parentGuid, title, position?.toUInt())
.fold(
{ guid -> callback(Result.success(guid)) },
{ e -> callback(Result.failure(e)) }
)
}
}
}
override fun updateNode(
guid: String,
info: BookmarkInfo,
callback: (Result<Unit>) -> Unit
) {
coroutineScope.launch {
withContext(Dispatchers.Main) {
val conceptInfo = mozilla.components.concept.storage.BookmarkInfo(
parentGuid = info.parentGuid,
position = info.position?.toUInt(),
title = info.title,
url = info.url
)
components.core.bookmarksStorage.updateNode(guid, conceptInfo).fold(
{ callback(Result.success(Unit)) },
{ e -> callback(Result.failure(e)) }
)
}
}
}
override fun deleteNode(
guid: String,
callback: (Result<Boolean>) -> Unit
) {
coroutineScope.launch {
withContext(Dispatchers.Main) {
components.core.bookmarksStorage.deleteNode(guid).fold(
{ deleted -> callback(Result.success(deleted)) },
{ e -> callback(Result.failure(e)) }
)
}
}
}
}
@@ -21,6 +21,7 @@ import eu.weblibre.flutter_mozilla_components.pigeons.BrowserExtensionEvents
import eu.weblibre.flutter_mozilla_components.pigeons.ContentBlocking import eu.weblibre.flutter_mozilla_components.pigeons.ContentBlocking
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoAddonEvents import eu.weblibre.flutter_mozilla_components.pigeons.GeckoAddonEvents
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoAddonsApi import eu.weblibre.flutter_mozilla_components.pigeons.GeckoAddonsApi
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoBookmarksApi
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoBrowserApi import eu.weblibre.flutter_mozilla_components.pigeons.GeckoBrowserApi
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoBrowserExtensionApi import eu.weblibre.flutter_mozilla_components.pigeons.GeckoBrowserExtensionApi
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoContainerProxyApi import eu.weblibre.flutter_mozilla_components.pigeons.GeckoContainerProxyApi
@@ -55,6 +56,7 @@ import mozilla.components.support.base.log.Log
import mozilla.components.support.base.log.sink.LogSink import mozilla.components.support.base.log.sink.LogSink
import org.mozilla.gecko.util.ThreadUtils.runOnUiThread import org.mozilla.gecko.util.ThreadUtils.runOnUiThread
import org.mozilla.geckoview.BuildConfig as GeckoViewBuildConfig import org.mozilla.geckoview.BuildConfig as GeckoViewBuildConfig
import mozilla.appservices.places.BookmarkRoot
class PriorityAwareLogSink( class PriorityAwareLogSink(
private val minLogPriority: Log.Priority, private val minLogPriority: Log.Priority,
@@ -259,6 +261,7 @@ class GeckoBrowserApiImpl : GeckoBrowserApi {
) )
GeckoHistoryApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoHistoryApiImpl()) GeckoHistoryApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoHistoryApiImpl())
GeckoFetchApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoFetchApiImpl()) GeckoFetchApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoFetchApiImpl())
GeckoBookmarksApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoBookmarksApiImpl())
ReaderViewEvents.setUp( ReaderViewEvents.setUp(
_flutterPluginBinding.binaryMessenger, _flutterPluginBinding.binaryMessenger,
@@ -30,6 +30,7 @@ import mozilla.components.browser.session.storage.SessionStorage
import mozilla.components.browser.state.engine.EngineMiddleware import mozilla.components.browser.state.engine.EngineMiddleware
import mozilla.components.browser.state.engine.middleware.SessionPrioritizationMiddleware import mozilla.components.browser.state.engine.middleware.SessionPrioritizationMiddleware
import mozilla.components.browser.state.store.BrowserStore import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.browser.storage.sync.PlacesBookmarksStorage
import mozilla.components.browser.storage.sync.PlacesHistoryStorage import mozilla.components.browser.storage.sync.PlacesHistoryStorage
import mozilla.components.browser.thumbnails.ThumbnailsMiddleware import mozilla.components.browser.thumbnails.ThumbnailsMiddleware
import mozilla.components.browser.thumbnails.storage.ThumbnailStorage import mozilla.components.browser.thumbnails.storage.ThumbnailStorage
@@ -239,11 +240,13 @@ class Core(
* private sessions). * private sessions).
*/ */
val lazyHistoryStorage = lazy { PlacesHistoryStorage(context) } val lazyHistoryStorage = lazy { PlacesHistoryStorage(context) }
val lazyBookmarksStorage = lazy { PlacesBookmarksStorage(context) }
/** /**
* A convenience accessor to the [PlacesHistoryStorage]. * A convenience accessor to the [PlacesHistoryStorage].
*/ */
val historyStorage by lazy { lazyHistoryStorage.value } val historyStorage by lazy { lazyHistoryStorage.value }
val bookmarksStorage by lazy { lazyBookmarksStorage.value }
val permissionStorage by lazy { PermissionStorage(geckoSitePermissionsStorage) } val permissionStorage by lazy { PermissionStorage(geckoSitePermissionsStorage) }
@@ -424,6 +424,18 @@ enum class GeckoFetchCookiePolicy(val raw: Int) {
} }
} }
enum class BookmarkNodeType(val raw: Int) {
ITEM(0),
FOLDER(1),
SEPARATOR(2);
companion object {
fun ofRaw(raw: Int): BookmarkNodeType? {
return values().firstOrNull { it.raw == raw }
}
}
}
/** /**
* Translation options that map to the Gecko Translations Options. * Translation options that map to the Gecko Translations Options.
* *
@@ -2448,6 +2460,99 @@ data class GeckoFetchResponse (
override fun hashCode(): Int = toList().hashCode() override fun hashCode(): Int = toList().hashCode()
} }
/** Generated class from Pigeon that represents data sent in messages. */
data class BookmarkNode (
val type: BookmarkNodeType,
val guid: String,
val parentGuid: String? = null,
val position: Long? = null,
val title: String? = null,
val url: String? = null,
val dateAdded: Long,
val lastModified: Long,
val children: List<BookmarkNode>? = null
)
{
companion object {
fun fromList(pigeonVar_list: List<Any?>): BookmarkNode {
val type = pigeonVar_list[0] as BookmarkNodeType
val guid = pigeonVar_list[1] as String
val parentGuid = pigeonVar_list[2] as String?
val position = pigeonVar_list[3] as Long?
val title = pigeonVar_list[4] as String?
val url = pigeonVar_list[5] as String?
val dateAdded = pigeonVar_list[6] as Long
val lastModified = pigeonVar_list[7] as Long
val children = pigeonVar_list[8] as List<BookmarkNode>?
return BookmarkNode(type, guid, parentGuid, position, title, url, dateAdded, lastModified, children)
}
}
fun toList(): List<Any?> {
return listOf(
type,
guid,
parentGuid,
position,
title,
url,
dateAdded,
lastModified,
children,
)
}
override fun equals(other: Any?): Boolean {
if (other !is BookmarkNode) {
return false
}
if (this === other) {
return true
}
return GeckoPigeonUtils.deepEquals(toList(), other.toList()) }
override fun hashCode(): Int = toList().hashCode()
}
/**
* Class for making alterations to any bookmark node
*
* Generated class from Pigeon that represents data sent in messages.
*/
data class BookmarkInfo (
val parentGuid: String? = null,
val position: Long? = null,
val title: String? = null,
val url: String? = null
)
{
companion object {
fun fromList(pigeonVar_list: List<Any?>): BookmarkInfo {
val parentGuid = pigeonVar_list[0] as String?
val position = pigeonVar_list[1] as Long?
val title = pigeonVar_list[2] as String?
val url = pigeonVar_list[3] as String?
return BookmarkInfo(parentGuid, position, title, url)
}
}
fun toList(): List<Any?> {
return listOf(
parentGuid,
position,
title,
url,
)
}
override fun equals(other: Any?): Boolean {
if (other !is BookmarkInfo) {
return false
}
if (this === other) {
return true
}
return GeckoPigeonUtils.deepEquals(toList(), other.toList()) }
override fun hashCode(): Int = toList().hashCode()
}
private open class GeckoPigeonCodec : StandardMessageCodec() { private open class GeckoPigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return when (type) { return when (type) {
@@ -2562,245 +2667,260 @@ private open class GeckoPigeonCodec : StandardMessageCodec() {
} }
} }
151.toByte() -> { 151.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as Long?)?.let {
TranslationOptions.fromList(it) BookmarkNodeType.ofRaw(it.toInt())
} }
} }
152.toByte() -> { 152.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
ReaderState.fromList(it) TranslationOptions.fromList(it)
} }
} }
153.toByte() -> { 153.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
LastMediaAccessState.fromList(it) ReaderState.fromList(it)
} }
} }
154.toByte() -> { 154.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
HistoryMetadataKey.fromList(it) LastMediaAccessState.fromList(it)
} }
} }
155.toByte() -> { 155.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
PackageCategoryValue.fromList(it) HistoryMetadataKey.fromList(it)
} }
} }
156.toByte() -> { 156.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
ExternalPackage.fromList(it) PackageCategoryValue.fromList(it)
} }
} }
157.toByte() -> { 157.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
LoadUrlFlagsValue.fromList(it) ExternalPackage.fromList(it)
} }
} }
158.toByte() -> { 158.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
SourceValue.fromList(it) LoadUrlFlagsValue.fromList(it)
} }
} }
159.toByte() -> { 159.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
TabState.fromList(it) SourceValue.fromList(it)
} }
} }
160.toByte() -> { 160.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
RecoverableTab.fromList(it) TabState.fromList(it)
} }
} }
161.toByte() -> { 161.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
RecoverableBrowserState.fromList(it) RecoverableTab.fromList(it)
} }
} }
162.toByte() -> { 162.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
IconRequest.fromList(it) RecoverableBrowserState.fromList(it)
} }
} }
163.toByte() -> { 163.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
ResourceSize.fromList(it) IconRequest.fromList(it)
} }
} }
164.toByte() -> { 164.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
Resource.fromList(it) ResourceSize.fromList(it)
} }
} }
165.toByte() -> { 165.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
IconResult.fromList(it) Resource.fromList(it)
} }
} }
166.toByte() -> { 166.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
CookiePartitionKey.fromList(it) IconResult.fromList(it)
} }
} }
167.toByte() -> { 167.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
Cookie.fromList(it) CookiePartitionKey.fromList(it)
} }
} }
168.toByte() -> { 168.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
VisitInfo.fromList(it) Cookie.fromList(it)
} }
} }
169.toByte() -> { 169.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
HistoryItem.fromList(it) VisitInfo.fromList(it)
} }
} }
170.toByte() -> { 170.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
HistoryState.fromList(it) HistoryItem.fromList(it)
} }
} }
171.toByte() -> { 171.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
ReaderableState.fromList(it) HistoryState.fromList(it)
} }
} }
172.toByte() -> { 172.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
SecurityInfoState.fromList(it) ReaderableState.fromList(it)
} }
} }
173.toByte() -> { 173.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
TabContentState.fromList(it) SecurityInfoState.fromList(it)
} }
} }
174.toByte() -> { 174.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
FindResultState.fromList(it) TabContentState.fromList(it)
} }
} }
175.toByte() -> { 175.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
CustomSelectionAction.fromList(it) FindResultState.fromList(it)
} }
} }
176.toByte() -> { 176.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
WebExtensionData.fromList(it) CustomSelectionAction.fromList(it)
} }
} }
177.toByte() -> { 177.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
GeckoSuggestion.fromList(it) WebExtensionData.fromList(it)
} }
} }
178.toByte() -> { 178.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
TabContent.fromList(it) GeckoSuggestion.fromList(it)
} }
} }
179.toByte() -> { 179.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
ContentBlocking.fromList(it) TabContent.fromList(it)
} }
} }
180.toByte() -> { 180.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
DohSettings.fromList(it) ContentBlocking.fromList(it)
} }
} }
181.toByte() -> { 181.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
GeckoEngineSettings.fromList(it) DohSettings.fromList(it)
} }
} }
182.toByte() -> { 182.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
AutocompleteResult.fromList(it) GeckoEngineSettings.fromList(it)
} }
} }
183.toByte() -> { 183.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
UnknownHitResult.fromList(it) AutocompleteResult.fromList(it)
} }
} }
184.toByte() -> { 184.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
ImageHitResult.fromList(it) UnknownHitResult.fromList(it)
} }
} }
185.toByte() -> { 185.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
VideoHitResult.fromList(it) ImageHitResult.fromList(it)
} }
} }
186.toByte() -> { 186.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
AudioHitResult.fromList(it) VideoHitResult.fromList(it)
} }
} }
187.toByte() -> { 187.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
ImageSrcHitResult.fromList(it) AudioHitResult.fromList(it)
} }
} }
188.toByte() -> { 188.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
PhoneHitResult.fromList(it) ImageSrcHitResult.fromList(it)
} }
} }
189.toByte() -> { 189.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
EmailHitResult.fromList(it) PhoneHitResult.fromList(it)
} }
} }
190.toByte() -> { 190.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
GeoHitResult.fromList(it) EmailHitResult.fromList(it)
} }
} }
191.toByte() -> { 191.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
DownloadState.fromList(it) GeoHitResult.fromList(it)
} }
} }
192.toByte() -> { 192.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
ShareInternetResourceState.fromList(it) DownloadState.fromList(it)
} }
} }
193.toByte() -> { 193.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
AddonCollection.fromList(it) ShareInternetResourceState.fromList(it)
} }
} }
194.toByte() -> { 194.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
GeckoPref.fromList(it) AddonCollection.fromList(it)
} }
} }
195.toByte() -> { 195.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
ContainerSiteAssignment.fromList(it) GeckoPref.fromList(it)
} }
} }
196.toByte() -> { 196.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
GeckoHeader.fromList(it) ContainerSiteAssignment.fromList(it)
} }
} }
197.toByte() -> { 197.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
GeckoFetchRequest.fromList(it) GeckoHeader.fromList(it)
} }
} }
198.toByte() -> { 198.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
GeckoFetchRequest.fromList(it)
}
}
199.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
GeckoFetchResponse.fromList(it) GeckoFetchResponse.fromList(it)
} }
} }
200.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
BookmarkNode.fromList(it)
}
}
201.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
BookmarkInfo.fromList(it)
}
}
else -> super.readValueOfType(type, buffer) else -> super.readValueOfType(type, buffer)
} }
} }
@@ -2894,198 +3014,210 @@ private open class GeckoPigeonCodec : StandardMessageCodec() {
stream.write(150) stream.write(150)
writeValue(stream, value.raw.toLong()) writeValue(stream, value.raw.toLong())
} }
is TranslationOptions -> { is BookmarkNodeType -> {
stream.write(151) stream.write(151)
writeValue(stream, value.toList()) writeValue(stream, value.raw.toLong())
} }
is ReaderState -> { is TranslationOptions -> {
stream.write(152) stream.write(152)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is LastMediaAccessState -> { is ReaderState -> {
stream.write(153) stream.write(153)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is HistoryMetadataKey -> { is LastMediaAccessState -> {
stream.write(154) stream.write(154)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is PackageCategoryValue -> { is HistoryMetadataKey -> {
stream.write(155) stream.write(155)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is ExternalPackage -> { is PackageCategoryValue -> {
stream.write(156) stream.write(156)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is LoadUrlFlagsValue -> { is ExternalPackage -> {
stream.write(157) stream.write(157)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is SourceValue -> { is LoadUrlFlagsValue -> {
stream.write(158) stream.write(158)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is TabState -> { is SourceValue -> {
stream.write(159) stream.write(159)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is RecoverableTab -> { is TabState -> {
stream.write(160) stream.write(160)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is RecoverableBrowserState -> { is RecoverableTab -> {
stream.write(161) stream.write(161)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is IconRequest -> { is RecoverableBrowserState -> {
stream.write(162) stream.write(162)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is ResourceSize -> { is IconRequest -> {
stream.write(163) stream.write(163)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is Resource -> { is ResourceSize -> {
stream.write(164) stream.write(164)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is IconResult -> { is Resource -> {
stream.write(165) stream.write(165)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is CookiePartitionKey -> { is IconResult -> {
stream.write(166) stream.write(166)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is Cookie -> { is CookiePartitionKey -> {
stream.write(167) stream.write(167)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is VisitInfo -> { is Cookie -> {
stream.write(168) stream.write(168)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is HistoryItem -> { is VisitInfo -> {
stream.write(169) stream.write(169)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is HistoryState -> { is HistoryItem -> {
stream.write(170) stream.write(170)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is ReaderableState -> { is HistoryState -> {
stream.write(171) stream.write(171)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is SecurityInfoState -> { is ReaderableState -> {
stream.write(172) stream.write(172)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is TabContentState -> { is SecurityInfoState -> {
stream.write(173) stream.write(173)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is FindResultState -> { is TabContentState -> {
stream.write(174) stream.write(174)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is CustomSelectionAction -> { is FindResultState -> {
stream.write(175) stream.write(175)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is WebExtensionData -> { is CustomSelectionAction -> {
stream.write(176) stream.write(176)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is GeckoSuggestion -> { is WebExtensionData -> {
stream.write(177) stream.write(177)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is TabContent -> { is GeckoSuggestion -> {
stream.write(178) stream.write(178)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is ContentBlocking -> { is TabContent -> {
stream.write(179) stream.write(179)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is DohSettings -> { is ContentBlocking -> {
stream.write(180) stream.write(180)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is GeckoEngineSettings -> { is DohSettings -> {
stream.write(181) stream.write(181)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is AutocompleteResult -> { is GeckoEngineSettings -> {
stream.write(182) stream.write(182)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is UnknownHitResult -> { is AutocompleteResult -> {
stream.write(183) stream.write(183)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is ImageHitResult -> { is UnknownHitResult -> {
stream.write(184) stream.write(184)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is VideoHitResult -> { is ImageHitResult -> {
stream.write(185) stream.write(185)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is AudioHitResult -> { is VideoHitResult -> {
stream.write(186) stream.write(186)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is ImageSrcHitResult -> { is AudioHitResult -> {
stream.write(187) stream.write(187)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is PhoneHitResult -> { is ImageSrcHitResult -> {
stream.write(188) stream.write(188)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is EmailHitResult -> { is PhoneHitResult -> {
stream.write(189) stream.write(189)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is GeoHitResult -> { is EmailHitResult -> {
stream.write(190) stream.write(190)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is DownloadState -> { is GeoHitResult -> {
stream.write(191) stream.write(191)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is ShareInternetResourceState -> { is DownloadState -> {
stream.write(192) stream.write(192)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is AddonCollection -> { is ShareInternetResourceState -> {
stream.write(193) stream.write(193)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is GeckoPref -> { is AddonCollection -> {
stream.write(194) stream.write(194)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is ContainerSiteAssignment -> { is GeckoPref -> {
stream.write(195) stream.write(195)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is GeckoHeader -> { is ContainerSiteAssignment -> {
stream.write(196) stream.write(196)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is GeckoFetchRequest -> { is GeckoHeader -> {
stream.write(197) stream.write(197)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is GeckoFetchResponse -> { is GeckoFetchRequest -> {
stream.write(198) stream.write(198)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is GeckoFetchResponse -> {
stream.write(199)
writeValue(stream, value.toList())
}
is BookmarkNode -> {
stream.write(200)
writeValue(stream, value.toList())
}
is BookmarkInfo -> {
stream.write(201)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value) else -> super.writeValue(stream, value)
} }
} }
@@ -5594,3 +5726,286 @@ interface GeckoFetchApi {
} }
} }
} }
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface GeckoBookmarksApi {
/**
* Produces a bookmarks tree for the given guid string.
*
* @param guid The bookmark guid to obtain.
* @param recursive Whether to recurse and obtain all levels of children.
* @return The populated root starting from the guid.
*/
fun getTree(guid: String, recursive: Boolean, callback: (Result<BookmarkNode?>) -> Unit)
/**
* Obtains the details of a bookmark without children, if one exists with that guid. Otherwise, null.
*
* @param guid The bookmark guid to obtain.
* @return The bookmark node or null if it does not exist.
*/
fun getBookmark(guid: String, callback: (Result<BookmarkNode?>) -> Unit)
/**
* Produces a list of all bookmarks with the given URL.
*
* @param url The URL string.
* @return The list of bookmarks that match the URL
*/
fun getBookmarksWithUrl(url: String, callback: (Result<List<BookmarkNode>>) -> Unit)
/**
* Produces a list of the most recently added bookmarks.
*
* @param limit The maximum number of entries to return.
* @param maxAge Optional parameter used to filter out entries older than this number of milliseconds.
* @param currentTime Optional parameter for current time. Defaults toSystem.currentTimeMillis()
* @return The list of bookmarks that have been recently added up to the limit number of items.
*/
fun getRecentBookmarks(limit: Long, maxAge: Long?, currentTime: Long, callback: (Result<List<BookmarkNode>>) -> Unit)
/**
* Searches bookmarks with a query string.
*
* @param query The query string to search.
* @param limit The maximum number of entries to return.
* @return The list of matching bookmark nodes up to the limit number of items.
*/
fun searchBookmarks(query: String, limit: Long, callback: (Result<List<BookmarkNode>>) -> Unit)
/**
* Adds a new bookmark item to a given node.
*
* Sync behavior: will add new bookmark item to remote devices.
*
* @param parentGuid The parent guid of the new node.
* @param url The URL of the bookmark item to add.
* @param title The title of the bookmark item to add.
* @param position The optional position to add the new node or null to append.
* @return The guid of the newly inserted bookmark item.
*/
fun addItem(parentGuid: String, url: String, title: String, position: Long?, callback: (Result<String>) -> Unit)
/**
* Adds a new bookmark folder to a given node.
*
* Sync behavior: will add new separator to remote devices.
*
* @param parentGuid The parent guid of the new node.
* @param title The title of the bookmark folder to add.
* @param position The optional position to add the new node or null to append.
* @return The guid of the newly inserted bookmark item.
*/
fun addFolder(parentGuid: String, title: String, position: Long?, callback: (Result<String>) -> Unit)
/**
* Edits the properties of an existing bookmark item and/or moves an existing one underneath a new parent guid.
*
* Sync behavior: will alter bookmark item on remote devices.
*
* @param guid The guid of the item to update.
* @param info The info to change in the bookmark.
*/
fun updateNode(guid: String, info: BookmarkInfo, callback: (Result<Unit>) -> Unit)
/**
* Deletes a bookmark node and all of its children, if any.
*
* Sync behavior: will remove bookmark from remote devices.
*
* @return Whether the bookmark existed or not.
*/
fun deleteNode(guid: String, callback: (Result<Boolean>) -> Unit)
companion object {
/** The codec used by GeckoBookmarksApi. */
val codec: MessageCodec<Any?> by lazy {
GeckoPigeonCodec()
}
/** Sets up an instance of `GeckoBookmarksApi` to handle messages through the `binaryMessenger`. */
@JvmOverloads
fun setUp(binaryMessenger: BinaryMessenger, api: GeckoBookmarksApi?, messageChannelSuffix: String = "") {
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoBookmarksApi.getTree$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val guidArg = args[0] as String
val recursiveArg = args[1] as Boolean
api.getTree(guidArg, recursiveArg) { result: Result<BookmarkNode?> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(GeckoPigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(GeckoPigeonUtils.wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoBookmarksApi.getBookmark$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val guidArg = args[0] as String
api.getBookmark(guidArg) { result: Result<BookmarkNode?> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(GeckoPigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(GeckoPigeonUtils.wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoBookmarksApi.getBookmarksWithUrl$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val urlArg = args[0] as String
api.getBookmarksWithUrl(urlArg) { result: Result<List<BookmarkNode>> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(GeckoPigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(GeckoPigeonUtils.wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoBookmarksApi.getRecentBookmarks$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val limitArg = args[0] as Long
val maxAgeArg = args[1] as Long?
val currentTimeArg = args[2] as Long
api.getRecentBookmarks(limitArg, maxAgeArg, currentTimeArg) { result: Result<List<BookmarkNode>> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(GeckoPigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(GeckoPigeonUtils.wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoBookmarksApi.searchBookmarks$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val queryArg = args[0] as String
val limitArg = args[1] as Long
api.searchBookmarks(queryArg, limitArg) { result: Result<List<BookmarkNode>> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(GeckoPigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(GeckoPigeonUtils.wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoBookmarksApi.addItem$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val parentGuidArg = args[0] as String
val urlArg = args[1] as String
val titleArg = args[2] as String
val positionArg = args[3] as Long?
api.addItem(parentGuidArg, urlArg, titleArg, positionArg) { result: Result<String> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(GeckoPigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(GeckoPigeonUtils.wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoBookmarksApi.addFolder$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val parentGuidArg = args[0] as String
val titleArg = args[1] as String
val positionArg = args[2] as Long?
api.addFolder(parentGuidArg, titleArg, positionArg) { result: Result<String> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(GeckoPigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(GeckoPigeonUtils.wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoBookmarksApi.updateNode$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val guidArg = args[0] as String
val infoArg = args[1] as BookmarkInfo
api.updateNode(guidArg, infoArg) { 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.GeckoBookmarksApi.deleteNode$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val guidArg = args[0] as String
api.deleteNode(guidArg) { result: Result<Boolean> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(GeckoPigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(GeckoPigeonUtils.wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
}
}
}
@@ -8,6 +8,7 @@ export 'src/data/models/load_url_flags.dart';
export 'src/data/models/source.dart'; export 'src/data/models/source.dart';
export 'src/domain/entities/default_selection_actions.dart'; export 'src/domain/entities/default_selection_actions.dart';
export 'src/domain/services/gecko_addon.dart'; export 'src/domain/services/gecko_addon.dart';
export 'src/domain/services/gecko_bookmarks.dart';
export 'src/domain/services/gecko_browser.dart'; export 'src/domain/services/gecko_browser.dart';
export 'src/domain/services/gecko_browser_extension.dart'; export 'src/domain/services/gecko_browser_extension.dart';
export 'src/domain/services/gecko_container_proxy.dart'; export 'src/domain/services/gecko_container_proxy.dart';
@@ -34,6 +35,9 @@ export 'src/pigeons/gecko.g.dart'
show show
AddonCollection, AddonCollection,
AudioHitResult, AudioHitResult,
BookmarkInfo,
BookmarkNode,
BookmarkNodeType,
BounceTrackingProtectionMode, BounceTrackingProtectionMode,
ColorScheme, ColorScheme,
ContentBlocking, ContentBlocking,
@@ -0,0 +1,131 @@
import 'package:flutter_mozilla_components/src/pigeons/gecko.g.dart';
/// Enumeration of the ids of the roots of the bookmarks tree.
///
/// There are 5 "roots" in the bookmark tree. The actual root
/// (which has no parent), and it's 4 children (which have the
/// actual root as their parent).
///
/// You cannot delete or move any of these items.
enum BookmarkRoot {
root("root________", "Default"),
menu("menu________", "Menu"),
toolbar("toolbar_____", "Toolbar"),
unfiled("unfiled_____", "Unified"),
mobile("mobile______", "WebLibre");
final String id;
final String displayName;
const BookmarkRoot(this.id, this.displayName);
}
final bookmarkRootIds = BookmarkRoot.values.map((e) => e.id).toSet();
final bookmarkRootDisplayNames = Map.fromEntries(
BookmarkRoot.values.map((e) => MapEntry(e.id, e.displayName)),
);
final _api = GeckoBookmarksApi();
class GeckoBookmarksService {
/// Produces a bookmarks tree for the given guid string.
///
/// @param guid The bookmark guid to obtain.
/// @param recursive Whether to recurse and obtain all levels of children.
/// @return The populated root starting from the guid.
Future<BookmarkNode?> getTree(String guid, {bool recursive = false}) {
return _api.getTree(guid, recursive);
}
/// Obtains the details of a bookmark without children, if one exists with that guid. Otherwise, null.
///
/// @param guid The bookmark guid to obtain.
/// @return The bookmark node or null if it does not exist.
Future<BookmarkNode?> getBookmark(String guid) {
return _api.getBookmark(guid);
}
/// Produces a list of all bookmarks with the given URL.
///
/// @param url The URL string.
/// @return The list of bookmarks that match the URL
Future<List<BookmarkNode>> getBookmarksWithUrl(Uri url) {
return _api.getBookmarksWithUrl(url.toString());
}
/// Produces a list of the most recently added bookmarks.
///
/// @param limit The maximum number of entries to return.
/// @param maxAge Optional parameter used to filter out entries older than this number of milliseconds.
/// @param currentTime Optional parameter for current time. Defaults toSystem.currentTimeMillis()
/// @return The list of bookmarks that have been recently added up to the limit number of items.
Future<List<BookmarkNode>> getRecentBookmarks(
int limit, {
Duration maxAge = Duration.zero,
DateTime? currentTime,
}) {
return _api.getRecentBookmarks(
limit,
maxAge.inMilliseconds,
(currentTime ?? DateTime.now()).millisecondsSinceEpoch,
);
}
/// Searches bookmarks with a query string.
///
/// @param query The query string to search.
/// @param limit The maximum number of entries to return.
/// @return The list of matching bookmark nodes up to the limit number of items.
Future<List<BookmarkNode>> searchBookmarks(String query, {int limit = 10}) {
return _api.searchBookmarks(query, limit);
}
/// Adds a new bookmark item to a given node.
///
/// Sync behavior: will add new bookmark item to remote devices.
///
/// @param parentGuid The parent guid of the new node.
/// @param url The URL of the bookmark item to add.
/// @param title The title of the bookmark item to add.
/// @param position The optional position to add the new node or null to append.
/// @return The guid of the newly inserted bookmark item.
Future<String> addItem(
String parentGuid,
Uri url,
String title,
int? position,
) {
return _api.addItem(parentGuid, url.toString(), title, position);
}
/// Adds a new bookmark folder to a given node.
///
/// Sync behavior: will add new separator to remote devices.
///
/// @param parentGuid The parent guid of the new node.
/// @param title The title of the bookmark folder to add.
/// @param position The optional position to add the new node or null to append.
/// @return The guid of the newly inserted bookmark item.
Future<String> addFolder(String parentGuid, String title, int? position) {
return _api.addFolder(parentGuid, title, position);
}
/// Edits the properties of an existing bookmark item and/or moves an existing one underneath a new parent guid.
///
/// Sync behavior: will alter bookmark item on remote devices.
///
/// @param guid The guid of the item to update.
/// @param info The info to change in the bookmark.
Future<void> updateNode(String guid, BookmarkInfo info) {
return _api.updateNode(guid, info);
}
/// Deletes a bookmark node and all of its children, if any.
///
/// Sync behavior: will remove bookmark from remote devices.
///
/// @return Whether the bookmark existed or not.
Future<bool> deleteNode(String guid) {
return _api.deleteNode(guid);
}
}
@@ -238,6 +238,12 @@ enum GeckoFetchCookiePolicy {
omit, omit,
} }
enum BookmarkNodeType {
item,
folder,
separator,
}
/// Translation options that map to the Gecko Translations Options. /// Translation options that map to the Gecko Translations Options.
/// ///
/// @property downloadModel If the necessary models should be downloaded on request. If false, then /// @property downloadModel If the necessary models should be downloaded on request. If false, then
@@ -3139,6 +3145,144 @@ class GeckoFetchResponse {
; ;
} }
class BookmarkNode {
BookmarkNode({
required this.type,
required this.guid,
this.parentGuid,
this.position,
this.title,
this.url,
required this.dateAdded,
required this.lastModified,
this.children,
});
BookmarkNodeType type;
String guid;
String? parentGuid;
int? position;
String? title;
String? url;
int dateAdded;
int lastModified;
List<BookmarkNode>? children;
List<Object?> _toList() {
return <Object?>[
type,
guid,
parentGuid,
position,
title,
url,
dateAdded,
lastModified,
children,
];
}
Object encode() {
return _toList(); }
static BookmarkNode decode(Object result) {
result as List<Object?>;
return BookmarkNode(
type: result[0]! as BookmarkNodeType,
guid: result[1]! as String,
parentGuid: result[2] as String?,
position: result[3] as int?,
title: result[4] as String?,
url: result[5] as String?,
dateAdded: result[6]! as int,
lastModified: result[7]! as int,
children: (result[8] as List<Object?>?)?.cast<BookmarkNode>(),
);
}
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
bool operator ==(Object other) {
if (other is! BookmarkNode || other.runtimeType != runtimeType) {
return false;
}
if (identical(this, other)) {
return true;
}
return _deepEquals(encode(), other.encode());
}
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
int get hashCode => Object.hashAll(_toList())
;
}
/// Class for making alterations to any bookmark node
class BookmarkInfo {
BookmarkInfo({
this.parentGuid,
this.position,
this.title,
this.url,
});
String? parentGuid;
int? position;
String? title;
String? url;
List<Object?> _toList() {
return <Object?>[
parentGuid,
position,
title,
url,
];
}
Object encode() {
return _toList(); }
static BookmarkInfo decode(Object result) {
result as List<Object?>;
return BookmarkInfo(
parentGuid: result[0] as String?,
position: result[1] as int?,
title: result[2] as String?,
url: result[3] as String?,
);
}
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
bool operator ==(Object other) {
if (other is! BookmarkInfo || other.runtimeType != runtimeType) {
return false;
}
if (identical(this, other)) {
return true;
}
return _deepEquals(encode(), other.encode());
}
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
int get hashCode => Object.hashAll(_toList())
;
}
class _PigeonCodec extends StandardMessageCodec { class _PigeonCodec extends StandardMessageCodec {
const _PigeonCodec(); const _PigeonCodec();
@@ -3213,150 +3357,159 @@ class _PigeonCodec extends StandardMessageCodec {
} else if (value is GeckoFetchCookiePolicy) { } else if (value is GeckoFetchCookiePolicy) {
buffer.putUint8(150); buffer.putUint8(150);
writeValue(buffer, value.index); writeValue(buffer, value.index);
} else if (value is TranslationOptions) { } else if (value is BookmarkNodeType) {
buffer.putUint8(151); buffer.putUint8(151);
writeValue(buffer, value.encode()); writeValue(buffer, value.index);
} else if (value is ReaderState) { } else if (value is TranslationOptions) {
buffer.putUint8(152); buffer.putUint8(152);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is LastMediaAccessState) { } else if (value is ReaderState) {
buffer.putUint8(153); buffer.putUint8(153);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is HistoryMetadataKey) { } else if (value is LastMediaAccessState) {
buffer.putUint8(154); buffer.putUint8(154);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is PackageCategoryValue) { } else if (value is HistoryMetadataKey) {
buffer.putUint8(155); buffer.putUint8(155);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is ExternalPackage) { } else if (value is PackageCategoryValue) {
buffer.putUint8(156); buffer.putUint8(156);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is LoadUrlFlagsValue) { } else if (value is ExternalPackage) {
buffer.putUint8(157); buffer.putUint8(157);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is SourceValue) { } else if (value is LoadUrlFlagsValue) {
buffer.putUint8(158); buffer.putUint8(158);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is TabState) { } else if (value is SourceValue) {
buffer.putUint8(159); buffer.putUint8(159);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is RecoverableTab) { } else if (value is TabState) {
buffer.putUint8(160); buffer.putUint8(160);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is RecoverableBrowserState) { } else if (value is RecoverableTab) {
buffer.putUint8(161); buffer.putUint8(161);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is IconRequest) { } else if (value is RecoverableBrowserState) {
buffer.putUint8(162); buffer.putUint8(162);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is ResourceSize) { } else if (value is IconRequest) {
buffer.putUint8(163); buffer.putUint8(163);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is Resource) { } else if (value is ResourceSize) {
buffer.putUint8(164); buffer.putUint8(164);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is IconResult) { } else if (value is Resource) {
buffer.putUint8(165); buffer.putUint8(165);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is CookiePartitionKey) { } else if (value is IconResult) {
buffer.putUint8(166); buffer.putUint8(166);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is Cookie) { } else if (value is CookiePartitionKey) {
buffer.putUint8(167); buffer.putUint8(167);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is VisitInfo) { } else if (value is Cookie) {
buffer.putUint8(168); buffer.putUint8(168);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is HistoryItem) { } else if (value is VisitInfo) {
buffer.putUint8(169); buffer.putUint8(169);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is HistoryState) { } else if (value is HistoryItem) {
buffer.putUint8(170); buffer.putUint8(170);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is ReaderableState) { } else if (value is HistoryState) {
buffer.putUint8(171); buffer.putUint8(171);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is SecurityInfoState) { } else if (value is ReaderableState) {
buffer.putUint8(172); buffer.putUint8(172);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is TabContentState) { } else if (value is SecurityInfoState) {
buffer.putUint8(173); buffer.putUint8(173);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is FindResultState) { } else if (value is TabContentState) {
buffer.putUint8(174); buffer.putUint8(174);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is CustomSelectionAction) { } else if (value is FindResultState) {
buffer.putUint8(175); buffer.putUint8(175);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is WebExtensionData) { } else if (value is CustomSelectionAction) {
buffer.putUint8(176); buffer.putUint8(176);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is GeckoSuggestion) { } else if (value is WebExtensionData) {
buffer.putUint8(177); buffer.putUint8(177);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is TabContent) { } else if (value is GeckoSuggestion) {
buffer.putUint8(178); buffer.putUint8(178);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is ContentBlocking) { } else if (value is TabContent) {
buffer.putUint8(179); buffer.putUint8(179);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is DohSettings) { } else if (value is ContentBlocking) {
buffer.putUint8(180); buffer.putUint8(180);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is GeckoEngineSettings) { } else if (value is DohSettings) {
buffer.putUint8(181); buffer.putUint8(181);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is AutocompleteResult) { } else if (value is GeckoEngineSettings) {
buffer.putUint8(182); buffer.putUint8(182);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is UnknownHitResult) { } else if (value is AutocompleteResult) {
buffer.putUint8(183); buffer.putUint8(183);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is ImageHitResult) { } else if (value is UnknownHitResult) {
buffer.putUint8(184); buffer.putUint8(184);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is VideoHitResult) { } else if (value is ImageHitResult) {
buffer.putUint8(185); buffer.putUint8(185);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is AudioHitResult) { } else if (value is VideoHitResult) {
buffer.putUint8(186); buffer.putUint8(186);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is ImageSrcHitResult) { } else if (value is AudioHitResult) {
buffer.putUint8(187); buffer.putUint8(187);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is PhoneHitResult) { } else if (value is ImageSrcHitResult) {
buffer.putUint8(188); buffer.putUint8(188);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is EmailHitResult) { } else if (value is PhoneHitResult) {
buffer.putUint8(189); buffer.putUint8(189);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is GeoHitResult) { } else if (value is EmailHitResult) {
buffer.putUint8(190); buffer.putUint8(190);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is DownloadState) { } else if (value is GeoHitResult) {
buffer.putUint8(191); buffer.putUint8(191);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is ShareInternetResourceState) { } else if (value is DownloadState) {
buffer.putUint8(192); buffer.putUint8(192);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is AddonCollection) { } else if (value is ShareInternetResourceState) {
buffer.putUint8(193); buffer.putUint8(193);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is GeckoPref) { } else if (value is AddonCollection) {
buffer.putUint8(194); buffer.putUint8(194);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is ContainerSiteAssignment) { } else if (value is GeckoPref) {
buffer.putUint8(195); buffer.putUint8(195);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is GeckoHeader) { } else if (value is ContainerSiteAssignment) {
buffer.putUint8(196); buffer.putUint8(196);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is GeckoFetchRequest) { } else if (value is GeckoHeader) {
buffer.putUint8(197); buffer.putUint8(197);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is GeckoFetchResponse) { } else if (value is GeckoFetchRequest) {
buffer.putUint8(198); buffer.putUint8(198);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is GeckoFetchResponse) {
buffer.putUint8(199);
writeValue(buffer, value.encode());
} else if (value is BookmarkNode) {
buffer.putUint8(200);
writeValue(buffer, value.encode());
} else if (value is BookmarkInfo) {
buffer.putUint8(201);
writeValue(buffer, value.encode());
} else { } else {
super.writeValue(buffer, value); super.writeValue(buffer, value);
} }
@@ -3432,101 +3585,108 @@ class _PigeonCodec extends StandardMessageCodec {
final int? value = readValue(buffer) as int?; final int? value = readValue(buffer) as int?;
return value == null ? null : GeckoFetchCookiePolicy.values[value]; return value == null ? null : GeckoFetchCookiePolicy.values[value];
case 151: case 151:
return TranslationOptions.decode(readValue(buffer)!); final int? value = readValue(buffer) as int?;
return value == null ? null : BookmarkNodeType.values[value];
case 152: case 152:
return ReaderState.decode(readValue(buffer)!); return TranslationOptions.decode(readValue(buffer)!);
case 153: case 153:
return LastMediaAccessState.decode(readValue(buffer)!); return ReaderState.decode(readValue(buffer)!);
case 154: case 154:
return HistoryMetadataKey.decode(readValue(buffer)!); return LastMediaAccessState.decode(readValue(buffer)!);
case 155: case 155:
return PackageCategoryValue.decode(readValue(buffer)!); return HistoryMetadataKey.decode(readValue(buffer)!);
case 156: case 156:
return ExternalPackage.decode(readValue(buffer)!); return PackageCategoryValue.decode(readValue(buffer)!);
case 157: case 157:
return LoadUrlFlagsValue.decode(readValue(buffer)!); return ExternalPackage.decode(readValue(buffer)!);
case 158: case 158:
return SourceValue.decode(readValue(buffer)!); return LoadUrlFlagsValue.decode(readValue(buffer)!);
case 159: case 159:
return TabState.decode(readValue(buffer)!); return SourceValue.decode(readValue(buffer)!);
case 160: case 160:
return RecoverableTab.decode(readValue(buffer)!); return TabState.decode(readValue(buffer)!);
case 161: case 161:
return RecoverableBrowserState.decode(readValue(buffer)!); return RecoverableTab.decode(readValue(buffer)!);
case 162: case 162:
return IconRequest.decode(readValue(buffer)!); return RecoverableBrowserState.decode(readValue(buffer)!);
case 163: case 163:
return ResourceSize.decode(readValue(buffer)!); return IconRequest.decode(readValue(buffer)!);
case 164: case 164:
return Resource.decode(readValue(buffer)!); return ResourceSize.decode(readValue(buffer)!);
case 165: case 165:
return IconResult.decode(readValue(buffer)!); return Resource.decode(readValue(buffer)!);
case 166: case 166:
return CookiePartitionKey.decode(readValue(buffer)!); return IconResult.decode(readValue(buffer)!);
case 167: case 167:
return Cookie.decode(readValue(buffer)!); return CookiePartitionKey.decode(readValue(buffer)!);
case 168: case 168:
return VisitInfo.decode(readValue(buffer)!); return Cookie.decode(readValue(buffer)!);
case 169: case 169:
return HistoryItem.decode(readValue(buffer)!); return VisitInfo.decode(readValue(buffer)!);
case 170: case 170:
return HistoryState.decode(readValue(buffer)!); return HistoryItem.decode(readValue(buffer)!);
case 171: case 171:
return ReaderableState.decode(readValue(buffer)!); return HistoryState.decode(readValue(buffer)!);
case 172: case 172:
return SecurityInfoState.decode(readValue(buffer)!); return ReaderableState.decode(readValue(buffer)!);
case 173: case 173:
return TabContentState.decode(readValue(buffer)!); return SecurityInfoState.decode(readValue(buffer)!);
case 174: case 174:
return FindResultState.decode(readValue(buffer)!); return TabContentState.decode(readValue(buffer)!);
case 175: case 175:
return CustomSelectionAction.decode(readValue(buffer)!); return FindResultState.decode(readValue(buffer)!);
case 176: case 176:
return WebExtensionData.decode(readValue(buffer)!); return CustomSelectionAction.decode(readValue(buffer)!);
case 177: case 177:
return GeckoSuggestion.decode(readValue(buffer)!); return WebExtensionData.decode(readValue(buffer)!);
case 178: case 178:
return TabContent.decode(readValue(buffer)!); return GeckoSuggestion.decode(readValue(buffer)!);
case 179: case 179:
return ContentBlocking.decode(readValue(buffer)!); return TabContent.decode(readValue(buffer)!);
case 180: case 180:
return DohSettings.decode(readValue(buffer)!); return ContentBlocking.decode(readValue(buffer)!);
case 181: case 181:
return GeckoEngineSettings.decode(readValue(buffer)!); return DohSettings.decode(readValue(buffer)!);
case 182: case 182:
return AutocompleteResult.decode(readValue(buffer)!); return GeckoEngineSettings.decode(readValue(buffer)!);
case 183: case 183:
return UnknownHitResult.decode(readValue(buffer)!); return AutocompleteResult.decode(readValue(buffer)!);
case 184: case 184:
return ImageHitResult.decode(readValue(buffer)!); return UnknownHitResult.decode(readValue(buffer)!);
case 185: case 185:
return VideoHitResult.decode(readValue(buffer)!); return ImageHitResult.decode(readValue(buffer)!);
case 186: case 186:
return AudioHitResult.decode(readValue(buffer)!); return VideoHitResult.decode(readValue(buffer)!);
case 187: case 187:
return ImageSrcHitResult.decode(readValue(buffer)!); return AudioHitResult.decode(readValue(buffer)!);
case 188: case 188:
return PhoneHitResult.decode(readValue(buffer)!); return ImageSrcHitResult.decode(readValue(buffer)!);
case 189: case 189:
return EmailHitResult.decode(readValue(buffer)!); return PhoneHitResult.decode(readValue(buffer)!);
case 190: case 190:
return GeoHitResult.decode(readValue(buffer)!); return EmailHitResult.decode(readValue(buffer)!);
case 191: case 191:
return DownloadState.decode(readValue(buffer)!); return GeoHitResult.decode(readValue(buffer)!);
case 192: case 192:
return ShareInternetResourceState.decode(readValue(buffer)!); return DownloadState.decode(readValue(buffer)!);
case 193: case 193:
return AddonCollection.decode(readValue(buffer)!); return ShareInternetResourceState.decode(readValue(buffer)!);
case 194: case 194:
return GeckoPref.decode(readValue(buffer)!); return AddonCollection.decode(readValue(buffer)!);
case 195: case 195:
return ContainerSiteAssignment.decode(readValue(buffer)!); return GeckoPref.decode(readValue(buffer)!);
case 196: case 196:
return GeckoHeader.decode(readValue(buffer)!); return ContainerSiteAssignment.decode(readValue(buffer)!);
case 197: case 197:
return GeckoFetchRequest.decode(readValue(buffer)!); return GeckoHeader.decode(readValue(buffer)!);
case 198: case 198:
return GeckoFetchRequest.decode(readValue(buffer)!);
case 199:
return GeckoFetchResponse.decode(readValue(buffer)!); return GeckoFetchResponse.decode(readValue(buffer)!);
case 200:
return BookmarkNode.decode(readValue(buffer)!);
case 201:
return BookmarkInfo.decode(readValue(buffer)!);
default: default:
return super.readValueOfType(type, buffer); return super.readValueOfType(type, buffer);
} }
@@ -6734,3 +6894,306 @@ class GeckoFetchApi {
} }
} }
} }
class GeckoBookmarksApi {
/// Constructor for [GeckoBookmarksApi]. The [binaryMessenger] named argument is
/// available for dependency injection. If it is left null, the default
/// BinaryMessenger will be used which routes to the host platform.
GeckoBookmarksApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})
: pigeonVar_binaryMessenger = binaryMessenger,
pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
final BinaryMessenger? pigeonVar_binaryMessenger;
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
final String pigeonVar_messageChannelSuffix;
/// Produces a bookmarks tree for the given guid string.
///
/// @param guid The bookmark guid to obtain.
/// @param recursive Whether to recurse and obtain all levels of children.
/// @return The populated root starting from the guid.
Future<BookmarkNode?> getTree(String guid, bool recursive) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoBookmarksApi.getTree$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[guid, recursive]);
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 (pigeonVar_replyList[0] as BookmarkNode?);
}
}
/// Obtains the details of a bookmark without children, if one exists with that guid. Otherwise, null.
///
/// @param guid The bookmark guid to obtain.
/// @return The bookmark node or null if it does not exist.
Future<BookmarkNode?> getBookmark(String guid) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoBookmarksApi.getBookmark$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[guid]);
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 (pigeonVar_replyList[0] as BookmarkNode?);
}
}
/// Produces a list of all bookmarks with the given URL.
///
/// @param url The URL string.
/// @return The list of bookmarks that match the URL
Future<List<BookmarkNode>> getBookmarksWithUrl(String url) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoBookmarksApi.getBookmarksWithUrl$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]);
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 if (pigeonVar_replyList[0] == null) {
throw PlatformException(
code: 'null-error',
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (pigeonVar_replyList[0] as List<Object?>?)!.cast<BookmarkNode>();
}
}
/// Produces a list of the most recently added bookmarks.
///
/// @param limit The maximum number of entries to return.
/// @param maxAge Optional parameter used to filter out entries older than this number of milliseconds.
/// @param currentTime Optional parameter for current time. Defaults toSystem.currentTimeMillis()
/// @return The list of bookmarks that have been recently added up to the limit number of items.
Future<List<BookmarkNode>> getRecentBookmarks(int limit, int? maxAge, int currentTime) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoBookmarksApi.getRecentBookmarks$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[limit, maxAge, currentTime]);
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 if (pigeonVar_replyList[0] == null) {
throw PlatformException(
code: 'null-error',
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (pigeonVar_replyList[0] as List<Object?>?)!.cast<BookmarkNode>();
}
}
/// Searches bookmarks with a query string.
///
/// @param query The query string to search.
/// @param limit The maximum number of entries to return.
/// @return The list of matching bookmark nodes up to the limit number of items.
Future<List<BookmarkNode>> searchBookmarks(String query, int limit) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoBookmarksApi.searchBookmarks$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[query, limit]);
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 if (pigeonVar_replyList[0] == null) {
throw PlatformException(
code: 'null-error',
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (pigeonVar_replyList[0] as List<Object?>?)!.cast<BookmarkNode>();
}
}
/// Adds a new bookmark item to a given node.
///
/// Sync behavior: will add new bookmark item to remote devices.
///
/// @param parentGuid The parent guid of the new node.
/// @param url The URL of the bookmark item to add.
/// @param title The title of the bookmark item to add.
/// @param position The optional position to add the new node or null to append.
/// @return The guid of the newly inserted bookmark item.
Future<String> addItem(String parentGuid, String url, String title, int? position) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoBookmarksApi.addItem$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[parentGuid, url, title, position]);
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 if (pigeonVar_replyList[0] == null) {
throw PlatformException(
code: 'null-error',
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (pigeonVar_replyList[0] as String?)!;
}
}
/// Adds a new bookmark folder to a given node.
///
/// Sync behavior: will add new separator to remote devices.
///
/// @param parentGuid The parent guid of the new node.
/// @param title The title of the bookmark folder to add.
/// @param position The optional position to add the new node or null to append.
/// @return The guid of the newly inserted bookmark item.
Future<String> addFolder(String parentGuid, String title, int? position) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoBookmarksApi.addFolder$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[parentGuid, title, position]);
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 if (pigeonVar_replyList[0] == null) {
throw PlatformException(
code: 'null-error',
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (pigeonVar_replyList[0] as String?)!;
}
}
/// Edits the properties of an existing bookmark item and/or moves an existing one underneath a new parent guid.
///
/// Sync behavior: will alter bookmark item on remote devices.
///
/// @param guid The guid of the item to update.
/// @param info The info to change in the bookmark.
Future<void> updateNode(String guid, BookmarkInfo info) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoBookmarksApi.updateNode$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[guid, info]);
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;
}
}
/// Deletes a bookmark node and all of its children, if any.
///
/// Sync behavior: will remove bookmark from remote devices.
///
/// @return Whether the bookmark existed or not.
Future<bool> deleteNode(String guid) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoBookmarksApi.deleteNode$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[guid]);
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 if (pigeonVar_replyList[0] == null) {
throw PlatformException(
code: 'null-error',
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (pigeonVar_replyList[0] as bool?)!;
}
}
}
@@ -1426,3 +1426,130 @@ abstract class GeckoFetchApi {
@async @async
GeckoFetchResponse fetch(GeckoFetchRequest request); GeckoFetchResponse fetch(GeckoFetchRequest request);
} }
enum BookmarkNodeType { item, folder, separator }
class BookmarkNode {
final BookmarkNodeType type;
final String guid;
final String? parentGuid;
final int? position;
final String? title;
final String? url;
final int dateAdded;
final int lastModified;
final List<BookmarkNode>? children;
BookmarkNode({
required this.type,
required this.guid,
required this.parentGuid,
required this.position,
required this.title,
required this.url,
required this.dateAdded,
required this.lastModified,
required this.children,
});
}
/// Class for making alterations to any bookmark node
class BookmarkInfo {
final String? parentGuid;
final int? position;
final String? title;
final String? url;
BookmarkInfo({
required this.parentGuid,
required this.position,
required this.title,
required this.url,
});
}
@HostApi()
abstract class GeckoBookmarksApi {
/// Produces a bookmarks tree for the given guid string.
///
/// @param guid The bookmark guid to obtain.
/// @param recursive Whether to recurse and obtain all levels of children.
/// @return The populated root starting from the guid.
@async
BookmarkNode? getTree(String guid, bool recursive);
/// Obtains the details of a bookmark without children, if one exists with that guid. Otherwise, null.
///
/// @param guid The bookmark guid to obtain.
/// @return The bookmark node or null if it does not exist.
@async
BookmarkNode? getBookmark(String guid);
/// Produces a list of all bookmarks with the given URL.
///
/// @param url The URL string.
/// @return The list of bookmarks that match the URL
@async
List<BookmarkNode> getBookmarksWithUrl(String url);
/// Produces a list of the most recently added bookmarks.
///
/// @param limit The maximum number of entries to return.
/// @param maxAge Optional parameter used to filter out entries older than this number of milliseconds.
/// @param currentTime Optional parameter for current time. Defaults toSystem.currentTimeMillis()
/// @return The list of bookmarks that have been recently added up to the limit number of items.
@async
List<BookmarkNode> getRecentBookmarks(
int limit,
int? maxAge,
int currentTime,
);
/// Searches bookmarks with a query string.
///
/// @param query The query string to search.
/// @param limit The maximum number of entries to return.
/// @return The list of matching bookmark nodes up to the limit number of items.
@async
List<BookmarkNode> searchBookmarks(String query, int limit);
/// Adds a new bookmark item to a given node.
///
/// Sync behavior: will add new bookmark item to remote devices.
///
/// @param parentGuid The parent guid of the new node.
/// @param url The URL of the bookmark item to add.
/// @param title The title of the bookmark item to add.
/// @param position The optional position to add the new node or null to append.
/// @return The guid of the newly inserted bookmark item.
@async
String addItem(String parentGuid, String url, String title, int? position);
/// Adds a new bookmark folder to a given node.
///
/// Sync behavior: will add new separator to remote devices.
///
/// @param parentGuid The parent guid of the new node.
/// @param title The title of the bookmark folder to add.
/// @param position The optional position to add the new node or null to append.
/// @return The guid of the newly inserted bookmark item.
@async
String addFolder(String parentGuid, String title, int? position);
/// Edits the properties of an existing bookmark item and/or moves an existing one underneath a new parent guid.
///
/// Sync behavior: will alter bookmark item on remote devices.
///
/// @param guid The guid of the item to update.
/// @param info The info to change in the bookmark.
@async
void updateNode(String guid, BookmarkInfo info);
/// Deletes a bookmark node and all of its children, if any.
///
/// Sync behavior: will remove bookmark from remote devices.
///
/// @return Whether the bookmark existed or not.
@async
bool deleteNode(String guid);
}