Hide Empty Root Folders by default
This commit is contained in:
@@ -27,6 +27,13 @@ import 'package:weblibre/features/geckoview/features/bookmarks/domain/repositori
|
|||||||
|
|
||||||
part 'bookmarks.g.dart';
|
part 'bookmarks.g.dart';
|
||||||
|
|
||||||
|
/// Check if a root folder is effectively empty (has no non-root children)
|
||||||
|
bool _isEmptyRootFolder(BookmarkFolder folder) {
|
||||||
|
if (folder.children == null) return true;
|
||||||
|
// A root folder is empty if it has no children, or only contains other root folders
|
||||||
|
return folder.children!.every((child) => bookmarkRootIds.contains(child.guid));
|
||||||
|
}
|
||||||
|
|
||||||
T? _selectChildRecursive<T extends BookmarkItem>(
|
T? _selectChildRecursive<T extends BookmarkItem>(
|
||||||
List<BookmarkItem> children,
|
List<BookmarkItem> children,
|
||||||
String guid,
|
String guid,
|
||||||
@@ -117,7 +124,11 @@ class BookmarksSearch extends _$BookmarksSearch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Riverpod()
|
@Riverpod()
|
||||||
AsyncValue<T?> bookmarks<T extends BookmarkItem>(Ref ref, String entryGuid) {
|
AsyncValue<T?> bookmarks<T extends BookmarkItem>(
|
||||||
|
Ref ref,
|
||||||
|
String entryGuid, {
|
||||||
|
bool hideEmptyRoots = false,
|
||||||
|
}) {
|
||||||
final bookmarksAsync = ref.watch(bookmarksRepositoryProvider);
|
final bookmarksAsync = ref.watch(bookmarksRepositoryProvider);
|
||||||
|
|
||||||
return bookmarksAsync.whenData((bookmarkNode) {
|
return bookmarksAsync.whenData((bookmarkNode) {
|
||||||
@@ -134,7 +145,22 @@ AsyncValue<T?> bookmarks<T extends BookmarkItem>(Ref ref, String entryGuid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (selectedNode != null) {
|
if (selectedNode != null) {
|
||||||
return _cloneAndFilterChildrenType<T>(selectedNode);
|
var result = _cloneAndFilterChildrenType<T>(selectedNode);
|
||||||
|
|
||||||
|
// Filter empty root folders when viewing root level (excluding WebLibre root)
|
||||||
|
if (hideEmptyRoots &&
|
||||||
|
entryGuid == BookmarkRoot.root.id &&
|
||||||
|
result is BookmarkFolder) {
|
||||||
|
final filteredChildren = result.children
|
||||||
|
?.where((child) =>
|
||||||
|
child is! BookmarkFolder ||
|
||||||
|
child.guid == BookmarkRoot.mobile.id ||
|
||||||
|
!_isEmptyRootFolder(child))
|
||||||
|
.toList();
|
||||||
|
result = result.copyWith.children(filteredChildren) as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -161,8 +187,8 @@ class SeamlessBookmarks extends _$SeamlessBookmarks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
AsyncValue<BookmarkItem?> build(String entryGuid) {
|
AsyncValue<BookmarkItem?> build(String entryGuid, {bool hideEmptyRoots = false}) {
|
||||||
final bookmarks = ref.watch(bookmarksProvider<BookmarkItem>(entryGuid));
|
final bookmarks = ref.watch(bookmarksProvider<BookmarkItem>(entryGuid, hideEmptyRoots: hideEmptyRoots));
|
||||||
|
|
||||||
if (_hasSearch) {
|
if (_hasSearch) {
|
||||||
final filterGuids = ref.watch(bookmarksSearchProvider);
|
final filterGuids = ref.watch(bookmarksSearchProvider);
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ final class BookmarksProvider<T extends BookmarkItem>
|
|||||||
with $Provider<AsyncValue<T?>> {
|
with $Provider<AsyncValue<T?>> {
|
||||||
BookmarksProvider._({
|
BookmarksProvider._({
|
||||||
required BookmarksFamily super.from,
|
required BookmarksFamily super.from,
|
||||||
required String super.argument,
|
required (String, {bool hideEmptyRoots}) super.argument,
|
||||||
}) : super(
|
}) : super(
|
||||||
retry: null,
|
retry: null,
|
||||||
name: r'bookmarksProvider',
|
name: r'bookmarksProvider',
|
||||||
@@ -77,7 +77,7 @@ final class BookmarksProvider<T extends BookmarkItem>
|
|||||||
String toString() {
|
String toString() {
|
||||||
return r'bookmarksProvider'
|
return r'bookmarksProvider'
|
||||||
'<${T}>'
|
'<${T}>'
|
||||||
'($argument)';
|
'$argument';
|
||||||
}
|
}
|
||||||
|
|
||||||
@$internal
|
@$internal
|
||||||
@@ -87,8 +87,12 @@ final class BookmarksProvider<T extends BookmarkItem>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
AsyncValue<T?> create(Ref ref) {
|
AsyncValue<T?> create(Ref ref) {
|
||||||
final argument = this.argument as String;
|
final argument = this.argument as (String, {bool hideEmptyRoots});
|
||||||
return bookmarks<T>(ref, argument);
|
return bookmarks<T>(
|
||||||
|
ref,
|
||||||
|
argument.$1,
|
||||||
|
hideEmptyRoots: argument.hideEmptyRoots,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$R _captureGenerics<$R>($R Function<T extends BookmarkItem>() cb) {
|
$R _captureGenerics<$R>($R Function<T extends BookmarkItem>() cb) {
|
||||||
@@ -116,7 +120,7 @@ final class BookmarksProvider<T extends BookmarkItem>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$bookmarksHash() => r'dfa19aea04f352b8a6cefe35a0b283c9fddb214f';
|
String _$bookmarksHash() => r'72b54c4ff18cfdb60824a57607628be6b61d25d4';
|
||||||
|
|
||||||
final class BookmarksFamily extends $Family {
|
final class BookmarksFamily extends $Family {
|
||||||
BookmarksFamily._()
|
BookmarksFamily._()
|
||||||
@@ -128,15 +132,23 @@ final class BookmarksFamily extends $Family {
|
|||||||
isAutoDispose: true,
|
isAutoDispose: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
BookmarksProvider<T> call<T extends BookmarkItem>(String entryGuid) =>
|
BookmarksProvider<T> call<T extends BookmarkItem>(
|
||||||
BookmarksProvider<T>._(argument: entryGuid, from: this);
|
String entryGuid, {
|
||||||
|
bool hideEmptyRoots = false,
|
||||||
|
}) => BookmarksProvider<T>._(
|
||||||
|
argument: (entryGuid, hideEmptyRoots: hideEmptyRoots),
|
||||||
|
from: this,
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => r'bookmarksProvider';
|
String toString() => r'bookmarksProvider';
|
||||||
|
|
||||||
/// {@macro riverpod.override_with}
|
/// {@macro riverpod.override_with}
|
||||||
Override overrideWith(
|
Override overrideWith(
|
||||||
AsyncValue<T?> Function<T extends BookmarkItem>(Ref ref, String args)
|
AsyncValue<T?> Function<T extends BookmarkItem>(
|
||||||
|
Ref ref,
|
||||||
|
(String, {bool hideEmptyRoots}) args,
|
||||||
|
)
|
||||||
create,
|
create,
|
||||||
) => $FamilyOverride(
|
) => $FamilyOverride(
|
||||||
from: this,
|
from: this,
|
||||||
@@ -144,7 +156,7 @@ final class BookmarksFamily extends $Family {
|
|||||||
final provider = pointer.origin as BookmarksProvider;
|
final provider = pointer.origin as BookmarksProvider;
|
||||||
return provider._captureGenerics(<T extends BookmarkItem>() {
|
return provider._captureGenerics(<T extends BookmarkItem>() {
|
||||||
provider as BookmarksProvider<T>;
|
provider as BookmarksProvider<T>;
|
||||||
final argument = provider.argument as String;
|
final argument = provider.argument as (String, {bool hideEmptyRoots});
|
||||||
return provider
|
return provider
|
||||||
.$view(create: (ref) => create(ref, argument))
|
.$view(create: (ref) => create(ref, argument))
|
||||||
.$createElement(pointer);
|
.$createElement(pointer);
|
||||||
@@ -160,7 +172,7 @@ final class SeamlessBookmarksProvider
|
|||||||
extends $NotifierProvider<SeamlessBookmarks, AsyncValue<BookmarkItem?>> {
|
extends $NotifierProvider<SeamlessBookmarks, AsyncValue<BookmarkItem?>> {
|
||||||
SeamlessBookmarksProvider._({
|
SeamlessBookmarksProvider._({
|
||||||
required SeamlessBookmarksFamily super.from,
|
required SeamlessBookmarksFamily super.from,
|
||||||
required String super.argument,
|
required (String, {bool hideEmptyRoots}) super.argument,
|
||||||
}) : super(
|
}) : super(
|
||||||
retry: null,
|
retry: null,
|
||||||
name: r'seamlessBookmarksProvider',
|
name: r'seamlessBookmarksProvider',
|
||||||
@@ -176,7 +188,7 @@ final class SeamlessBookmarksProvider
|
|||||||
String toString() {
|
String toString() {
|
||||||
return r'seamlessBookmarksProvider'
|
return r'seamlessBookmarksProvider'
|
||||||
''
|
''
|
||||||
'($argument)';
|
'$argument';
|
||||||
}
|
}
|
||||||
|
|
||||||
@$internal
|
@$internal
|
||||||
@@ -202,7 +214,7 @@ final class SeamlessBookmarksProvider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$seamlessBookmarksHash() => r'ddbc51ba22141a2c81bd9cf25adc610e5cdd72d9';
|
String _$seamlessBookmarksHash() => r'240b213fa8fe595781ccc608c5d551be54c31992';
|
||||||
|
|
||||||
final class SeamlessBookmarksFamily extends $Family
|
final class SeamlessBookmarksFamily extends $Family
|
||||||
with
|
with
|
||||||
@@ -211,7 +223,7 @@ final class SeamlessBookmarksFamily extends $Family
|
|||||||
AsyncValue<BookmarkItem?>,
|
AsyncValue<BookmarkItem?>,
|
||||||
AsyncValue<BookmarkItem?>,
|
AsyncValue<BookmarkItem?>,
|
||||||
AsyncValue<BookmarkItem?>,
|
AsyncValue<BookmarkItem?>,
|
||||||
String
|
(String, {bool hideEmptyRoots})
|
||||||
> {
|
> {
|
||||||
SeamlessBookmarksFamily._()
|
SeamlessBookmarksFamily._()
|
||||||
: super(
|
: super(
|
||||||
@@ -222,8 +234,13 @@ final class SeamlessBookmarksFamily extends $Family
|
|||||||
isAutoDispose: true,
|
isAutoDispose: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
SeamlessBookmarksProvider call(String entryGuid) =>
|
SeamlessBookmarksProvider call(
|
||||||
SeamlessBookmarksProvider._(argument: entryGuid, from: this);
|
String entryGuid, {
|
||||||
|
bool hideEmptyRoots = false,
|
||||||
|
}) => SeamlessBookmarksProvider._(
|
||||||
|
argument: (entryGuid, hideEmptyRoots: hideEmptyRoots),
|
||||||
|
from: this,
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => r'seamlessBookmarksProvider';
|
String toString() => r'seamlessBookmarksProvider';
|
||||||
@@ -231,10 +248,14 @@ final class SeamlessBookmarksFamily extends $Family
|
|||||||
|
|
||||||
abstract class _$SeamlessBookmarks
|
abstract class _$SeamlessBookmarks
|
||||||
extends $Notifier<AsyncValue<BookmarkItem?>> {
|
extends $Notifier<AsyncValue<BookmarkItem?>> {
|
||||||
late final _$args = ref.$arg as String;
|
late final _$args = ref.$arg as (String, {bool hideEmptyRoots});
|
||||||
String get entryGuid => _$args;
|
String get entryGuid => _$args.$1;
|
||||||
|
bool get hideEmptyRoots => _$args.hideEmptyRoots;
|
||||||
|
|
||||||
AsyncValue<BookmarkItem?> build(String entryGuid);
|
AsyncValue<BookmarkItem?> build(
|
||||||
|
String entryGuid, {
|
||||||
|
bool hideEmptyRoots = false,
|
||||||
|
});
|
||||||
@$mustCallSuper
|
@$mustCallSuper
|
||||||
@override
|
@override
|
||||||
void runBuild() {
|
void runBuild() {
|
||||||
@@ -248,6 +269,9 @@ abstract class _$SeamlessBookmarks
|
|||||||
Object?,
|
Object?,
|
||||||
Object?
|
Object?
|
||||||
>;
|
>;
|
||||||
element.handleCreate(ref, () => build(_$args));
|
element.handleCreate(
|
||||||
|
ref,
|
||||||
|
() => build(_$args.$1, hideEmptyRoots: _$args.hideEmptyRoots),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-3
@@ -48,15 +48,16 @@ class BookmarkListScreen extends HookConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final treeKey = useMemoized(() => GlobalKey<TreeViewState>());
|
final treeKey = useMemoized(() => GlobalKey<TreeViewState>());
|
||||||
final bookmarkList = ref.watch(seamlessBookmarksProvider(entryGuid));
|
final hideEmptyRoots = useState(true);
|
||||||
|
final bookmarkList = ref.watch(seamlessBookmarksProvider(entryGuid, hideEmptyRoots: hideEmptyRoots.value));
|
||||||
|
|
||||||
final textFilterEnabled = useState(false);
|
final textFilterEnabled = useState(false);
|
||||||
final textFilterController = useTextEditingController();
|
final textFilterController = useTextEditingController();
|
||||||
|
|
||||||
useOnListenableChange(textFilterController, () {
|
useOnListenableChange(textFilterController, () {
|
||||||
if (ref.exists(seamlessBookmarksProvider(entryGuid))) {
|
if (ref.exists(seamlessBookmarksProvider(entryGuid, hideEmptyRoots: hideEmptyRoots.value))) {
|
||||||
ref
|
ref
|
||||||
.read(seamlessBookmarksProvider(entryGuid).notifier)
|
.read(seamlessBookmarksProvider(entryGuid, hideEmptyRoots: hideEmptyRoots.value).notifier)
|
||||||
.search(textFilterController.text);
|
.search(textFilterController.text);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -106,6 +107,18 @@ class BookmarkListScreen extends HookConsumerWidget {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
if (entryGuid == BookmarkRoot.root.id)
|
||||||
|
MenuItemButton(
|
||||||
|
leadingIcon: Icon(hideEmptyRoots.value
|
||||||
|
? MdiIcons.eyeOff
|
||||||
|
: MdiIcons.eye),
|
||||||
|
child: Text(hideEmptyRoots.value
|
||||||
|
? 'Show Empty Root Folders'
|
||||||
|
: 'Hide Empty Root Folders'),
|
||||||
|
onPressed: () {
|
||||||
|
hideEmptyRoots.value = !hideEmptyRoots.value;
|
||||||
|
},
|
||||||
|
),
|
||||||
SubmenuButton(
|
SubmenuButton(
|
||||||
leadingIcon: const Icon(MdiIcons.import),
|
leadingIcon: const Icon(MdiIcons.import),
|
||||||
menuChildren: [
|
menuChildren: [
|
||||||
|
|||||||
Reference in New Issue
Block a user