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';
|
||||
|
||||
/// 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>(
|
||||
List<BookmarkItem> children,
|
||||
String guid,
|
||||
@@ -117,7 +124,11 @@ class BookmarksSearch extends _$BookmarksSearch {
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
return bookmarksAsync.whenData((bookmarkNode) {
|
||||
@@ -134,7 +145,22 @@ AsyncValue<T?> bookmarks<T extends BookmarkItem>(Ref ref, String entryGuid) {
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -161,8 +187,8 @@ class SeamlessBookmarks extends _$SeamlessBookmarks {
|
||||
}
|
||||
|
||||
@override
|
||||
AsyncValue<BookmarkItem?> build(String entryGuid) {
|
||||
final bookmarks = ref.watch(bookmarksProvider<BookmarkItem>(entryGuid));
|
||||
AsyncValue<BookmarkItem?> build(String entryGuid, {bool hideEmptyRoots = false}) {
|
||||
final bookmarks = ref.watch(bookmarksProvider<BookmarkItem>(entryGuid, hideEmptyRoots: hideEmptyRoots));
|
||||
|
||||
if (_hasSearch) {
|
||||
final filterGuids = ref.watch(bookmarksSearchProvider);
|
||||
|
||||
@@ -61,7 +61,7 @@ final class BookmarksProvider<T extends BookmarkItem>
|
||||
with $Provider<AsyncValue<T?>> {
|
||||
BookmarksProvider._({
|
||||
required BookmarksFamily super.from,
|
||||
required String super.argument,
|
||||
required (String, {bool hideEmptyRoots}) super.argument,
|
||||
}) : super(
|
||||
retry: null,
|
||||
name: r'bookmarksProvider',
|
||||
@@ -77,7 +77,7 @@ final class BookmarksProvider<T extends BookmarkItem>
|
||||
String toString() {
|
||||
return r'bookmarksProvider'
|
||||
'<${T}>'
|
||||
'($argument)';
|
||||
'$argument';
|
||||
}
|
||||
|
||||
@$internal
|
||||
@@ -87,8 +87,12 @@ final class BookmarksProvider<T extends BookmarkItem>
|
||||
|
||||
@override
|
||||
AsyncValue<T?> create(Ref ref) {
|
||||
final argument = this.argument as String;
|
||||
return bookmarks<T>(ref, argument);
|
||||
final argument = this.argument as (String, {bool hideEmptyRoots});
|
||||
return bookmarks<T>(
|
||||
ref,
|
||||
argument.$1,
|
||||
hideEmptyRoots: argument.hideEmptyRoots,
|
||||
);
|
||||
}
|
||||
|
||||
$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 {
|
||||
BookmarksFamily._()
|
||||
@@ -128,15 +132,23 @@ final class BookmarksFamily extends $Family {
|
||||
isAutoDispose: true,
|
||||
);
|
||||
|
||||
BookmarksProvider<T> call<T extends BookmarkItem>(String entryGuid) =>
|
||||
BookmarksProvider<T>._(argument: entryGuid, from: this);
|
||||
BookmarksProvider<T> call<T extends BookmarkItem>(
|
||||
String entryGuid, {
|
||||
bool hideEmptyRoots = false,
|
||||
}) => BookmarksProvider<T>._(
|
||||
argument: (entryGuid, hideEmptyRoots: hideEmptyRoots),
|
||||
from: this,
|
||||
);
|
||||
|
||||
@override
|
||||
String toString() => r'bookmarksProvider';
|
||||
|
||||
/// {@macro riverpod.override_with}
|
||||
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,
|
||||
) => $FamilyOverride(
|
||||
from: this,
|
||||
@@ -144,7 +156,7 @@ final class BookmarksFamily extends $Family {
|
||||
final provider = pointer.origin as BookmarksProvider;
|
||||
return provider._captureGenerics(<T extends BookmarkItem>() {
|
||||
provider as BookmarksProvider<T>;
|
||||
final argument = provider.argument as String;
|
||||
final argument = provider.argument as (String, {bool hideEmptyRoots});
|
||||
return provider
|
||||
.$view(create: (ref) => create(ref, argument))
|
||||
.$createElement(pointer);
|
||||
@@ -160,7 +172,7 @@ final class SeamlessBookmarksProvider
|
||||
extends $NotifierProvider<SeamlessBookmarks, AsyncValue<BookmarkItem?>> {
|
||||
SeamlessBookmarksProvider._({
|
||||
required SeamlessBookmarksFamily super.from,
|
||||
required String super.argument,
|
||||
required (String, {bool hideEmptyRoots}) super.argument,
|
||||
}) : super(
|
||||
retry: null,
|
||||
name: r'seamlessBookmarksProvider',
|
||||
@@ -176,7 +188,7 @@ final class SeamlessBookmarksProvider
|
||||
String toString() {
|
||||
return r'seamlessBookmarksProvider'
|
||||
''
|
||||
'($argument)';
|
||||
'$argument';
|
||||
}
|
||||
|
||||
@$internal
|
||||
@@ -202,7 +214,7 @@ final class SeamlessBookmarksProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$seamlessBookmarksHash() => r'ddbc51ba22141a2c81bd9cf25adc610e5cdd72d9';
|
||||
String _$seamlessBookmarksHash() => r'240b213fa8fe595781ccc608c5d551be54c31992';
|
||||
|
||||
final class SeamlessBookmarksFamily extends $Family
|
||||
with
|
||||
@@ -211,7 +223,7 @@ final class SeamlessBookmarksFamily extends $Family
|
||||
AsyncValue<BookmarkItem?>,
|
||||
AsyncValue<BookmarkItem?>,
|
||||
AsyncValue<BookmarkItem?>,
|
||||
String
|
||||
(String, {bool hideEmptyRoots})
|
||||
> {
|
||||
SeamlessBookmarksFamily._()
|
||||
: super(
|
||||
@@ -222,8 +234,13 @@ final class SeamlessBookmarksFamily extends $Family
|
||||
isAutoDispose: true,
|
||||
);
|
||||
|
||||
SeamlessBookmarksProvider call(String entryGuid) =>
|
||||
SeamlessBookmarksProvider._(argument: entryGuid, from: this);
|
||||
SeamlessBookmarksProvider call(
|
||||
String entryGuid, {
|
||||
bool hideEmptyRoots = false,
|
||||
}) => SeamlessBookmarksProvider._(
|
||||
argument: (entryGuid, hideEmptyRoots: hideEmptyRoots),
|
||||
from: this,
|
||||
);
|
||||
|
||||
@override
|
||||
String toString() => r'seamlessBookmarksProvider';
|
||||
@@ -231,10 +248,14 @@ final class SeamlessBookmarksFamily extends $Family
|
||||
|
||||
abstract class _$SeamlessBookmarks
|
||||
extends $Notifier<AsyncValue<BookmarkItem?>> {
|
||||
late final _$args = ref.$arg as String;
|
||||
String get entryGuid => _$args;
|
||||
late final _$args = ref.$arg as (String, {bool hideEmptyRoots});
|
||||
String get entryGuid => _$args.$1;
|
||||
bool get hideEmptyRoots => _$args.hideEmptyRoots;
|
||||
|
||||
AsyncValue<BookmarkItem?> build(String entryGuid);
|
||||
AsyncValue<BookmarkItem?> build(
|
||||
String entryGuid, {
|
||||
bool hideEmptyRoots = false,
|
||||
});
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
@@ -248,6 +269,9 @@ abstract class _$SeamlessBookmarks
|
||||
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
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
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 textFilterController = useTextEditingController();
|
||||
|
||||
useOnListenableChange(textFilterController, () {
|
||||
if (ref.exists(seamlessBookmarksProvider(entryGuid))) {
|
||||
if (ref.exists(seamlessBookmarksProvider(entryGuid, hideEmptyRoots: hideEmptyRoots.value))) {
|
||||
ref
|
||||
.read(seamlessBookmarksProvider(entryGuid).notifier)
|
||||
.read(seamlessBookmarksProvider(entryGuid, hideEmptyRoots: hideEmptyRoots.value).notifier)
|
||||
.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(
|
||||
leadingIcon: const Icon(MdiIcons.import),
|
||||
menuChildren: [
|
||||
|
||||
Reference in New Issue
Block a user