diff --git a/lib/i18n/strings.g.dart b/lib/i18n/strings.g.dart index e031953..3300263 100644 --- a/lib/i18n/strings.g.dart +++ b/lib/i18n/strings.g.dart @@ -6,7 +6,7 @@ /// Locales: 2 /// Strings: 226 (113 per locale) /// -/// Built on 2024-02-24 at 17:36 UTC +/// Built on 2024-02-24 at 22:54 UTC // coverage:ignore-file // ignore_for_file: type=lint diff --git a/lib/models/data/tag_bookmarks.dart b/lib/models/data/tag_bookmarks.dart new file mode 100644 index 0000000..0b8fc0e --- /dev/null +++ b/lib/models/data/tag_bookmarks.dart @@ -0,0 +1,12 @@ +import 'package:linkdy/models/data/bookmarks.dart'; +import 'package:linkdy/models/data/tags.dart'; + +class TagBookmarksResponse { + final BookmarksResponse? bookmarksResponse; + final Tag? tag; + + const TagBookmarksResponse({ + required this.bookmarksResponse, + required this.tag, + }); +} diff --git a/lib/router/routes.dart b/lib/router/routes.dart index 6523ae6..0c20c5a 100644 --- a/lib/router/routes.dart +++ b/lib/router/routes.dart @@ -55,7 +55,7 @@ final List appRoutes = [ GoRoute( path: RoutesPaths.tagBookmarks, name: RoutesNames.tagBookmarks, - builder: (context, state) => TagBookmarksScreen(tag: state.extra as Tag), + builder: (context, state) => TagBookmarksScreen(tagId: state.pathParameters['id'], tag: state.extra as Tag?), ), GoRoute( path: RoutesPaths.customization, diff --git a/lib/screens/tag_bookmarks/provider/tag_bookmarks.provider.dart b/lib/screens/tag_bookmarks/provider/tag_bookmarks.provider.dart index c61f2d5..2bff6cd 100644 --- a/lib/screens/tag_bookmarks/provider/tag_bookmarks.provider.dart +++ b/lib/screens/tag_bookmarks/provider/tag_bookmarks.provider.dart @@ -2,16 +2,32 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:linkdy/screens/tag_bookmarks/model/tag_bookmarks.model.dart'; -import 'package:linkdy/models/api_response.dart'; +import 'package:linkdy/models/data/tags.dart'; import 'package:linkdy/models/data/bookmarks.dart'; +import 'package:linkdy/models/api_response.dart'; +import 'package:linkdy/models/data/tag_bookmarks.dart'; import 'package:linkdy/providers/api_client_provider.dart'; part 'tag_bookmarks.provider.g.dart'; @riverpod -FutureOr> tagBookmarksRequest(TagBookmarksRequestRef ref, String tagName) async { - final result = await ref.watch(apiClientProvider)!.fetchBookmarks(q: tagName); - return result; +FutureOr> tagIdBookmarksRequest(TagIdBookmarksRequestRef ref, String tagId) async { + final tagResult = await ref.watch(apiClientProvider)!.fetchTagById(tagId); + if (tagResult.content == null) return const ApiResponse(successful: false); + final bookmarksResult = await ref.watch(apiClientProvider)!.fetchBookmarks(q: tagResult.content!.name); + return ApiResponse( + successful: bookmarksResult.successful, + content: TagBookmarksResponse( + bookmarksResponse: bookmarksResult.content, + tag: tagResult.content, + ), + ); +} + +@riverpod +FutureOr> tagBookmarksRequest(TagBookmarksRequestRef ref, Tag tag) async { + final bookmarksResult = await ref.watch(apiClientProvider)!.fetchBookmarks(q: tag.name); + return bookmarksResult; } @riverpod diff --git a/lib/screens/tag_bookmarks/provider/tag_bookmarks.provider.g.dart b/lib/screens/tag_bookmarks/provider/tag_bookmarks.provider.g.dart index 52a7e80..7f014cc 100644 --- a/lib/screens/tag_bookmarks/provider/tag_bookmarks.provider.g.dart +++ b/lib/screens/tag_bookmarks/provider/tag_bookmarks.provider.g.dart @@ -6,8 +6,8 @@ part of 'tag_bookmarks.provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$tagBookmarksRequestHash() => - r'0625c18022480eb1356396d9e46f677f21106797'; +String _$tagIdBookmarksRequestHash() => + r'2deadbe50293503ac94cd5d8656d6055c6bdc190'; /// Copied from Dart SDK class _SystemHash { @@ -30,6 +30,142 @@ class _SystemHash { } } +/// See also [tagIdBookmarksRequest]. +@ProviderFor(tagIdBookmarksRequest) +const tagIdBookmarksRequestProvider = TagIdBookmarksRequestFamily(); + +/// See also [tagIdBookmarksRequest]. +class TagIdBookmarksRequestFamily + extends Family>> { + /// See also [tagIdBookmarksRequest]. + const TagIdBookmarksRequestFamily(); + + /// See also [tagIdBookmarksRequest]. + TagIdBookmarksRequestProvider call( + String tagId, + ) { + return TagIdBookmarksRequestProvider( + tagId, + ); + } + + @override + TagIdBookmarksRequestProvider getProviderOverride( + covariant TagIdBookmarksRequestProvider provider, + ) { + return call( + provider.tagId, + ); + } + + static const Iterable? _dependencies = null; + + @override + Iterable? get dependencies => _dependencies; + + static const Iterable? _allTransitiveDependencies = null; + + @override + Iterable? get allTransitiveDependencies => + _allTransitiveDependencies; + + @override + String? get name => r'tagIdBookmarksRequestProvider'; +} + +/// See also [tagIdBookmarksRequest]. +class TagIdBookmarksRequestProvider + extends AutoDisposeFutureProvider> { + /// See also [tagIdBookmarksRequest]. + TagIdBookmarksRequestProvider( + String tagId, + ) : this._internal( + (ref) => tagIdBookmarksRequest( + ref as TagIdBookmarksRequestRef, + tagId, + ), + from: tagIdBookmarksRequestProvider, + name: r'tagIdBookmarksRequestProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') + ? null + : _$tagIdBookmarksRequestHash, + dependencies: TagIdBookmarksRequestFamily._dependencies, + allTransitiveDependencies: + TagIdBookmarksRequestFamily._allTransitiveDependencies, + tagId: tagId, + ); + + TagIdBookmarksRequestProvider._internal( + super._createNotifier, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.debugGetCreateSourceHash, + required super.from, + required this.tagId, + }) : super.internal(); + + final String tagId; + + @override + Override overrideWith( + FutureOr> Function( + TagIdBookmarksRequestRef provider) + create, + ) { + return ProviderOverride( + origin: this, + override: TagIdBookmarksRequestProvider._internal( + (ref) => create(ref as TagIdBookmarksRequestRef), + from: from, + name: null, + dependencies: null, + allTransitiveDependencies: null, + debugGetCreateSourceHash: null, + tagId: tagId, + ), + ); + } + + @override + AutoDisposeFutureProviderElement> + createElement() { + return _TagIdBookmarksRequestProviderElement(this); + } + + @override + bool operator ==(Object other) { + return other is TagIdBookmarksRequestProvider && other.tagId == tagId; + } + + @override + int get hashCode { + var hash = _SystemHash.combine(0, runtimeType.hashCode); + hash = _SystemHash.combine(hash, tagId.hashCode); + + return _SystemHash.finish(hash); + } +} + +mixin TagIdBookmarksRequestRef + on AutoDisposeFutureProviderRef> { + /// The parameter `tagId` of this provider. + String get tagId; +} + +class _TagIdBookmarksRequestProviderElement + extends AutoDisposeFutureProviderElement> + with TagIdBookmarksRequestRef { + _TagIdBookmarksRequestProviderElement(super.provider); + + @override + String get tagId => (origin as TagIdBookmarksRequestProvider).tagId; +} + +String _$tagBookmarksRequestHash() => + r'd198a94eafd0a78ef19de4064da4ddfba26ec1d3'; + /// See also [tagBookmarksRequest]. @ProviderFor(tagBookmarksRequest) const tagBookmarksRequestProvider = TagBookmarksRequestFamily(); @@ -42,10 +178,10 @@ class TagBookmarksRequestFamily /// See also [tagBookmarksRequest]. TagBookmarksRequestProvider call( - String tagName, + Tag tag, ) { return TagBookmarksRequestProvider( - tagName, + tag, ); } @@ -54,7 +190,7 @@ class TagBookmarksRequestFamily covariant TagBookmarksRequestProvider provider, ) { return call( - provider.tagName, + provider.tag, ); } @@ -78,11 +214,11 @@ class TagBookmarksRequestProvider extends AutoDisposeFutureProvider> { /// See also [tagBookmarksRequest]. TagBookmarksRequestProvider( - String tagName, + Tag tag, ) : this._internal( (ref) => tagBookmarksRequest( ref as TagBookmarksRequestRef, - tagName, + tag, ), from: tagBookmarksRequestProvider, name: r'tagBookmarksRequestProvider', @@ -93,7 +229,7 @@ class TagBookmarksRequestProvider dependencies: TagBookmarksRequestFamily._dependencies, allTransitiveDependencies: TagBookmarksRequestFamily._allTransitiveDependencies, - tagName: tagName, + tag: tag, ); TagBookmarksRequestProvider._internal( @@ -103,10 +239,10 @@ class TagBookmarksRequestProvider required super.allTransitiveDependencies, required super.debugGetCreateSourceHash, required super.from, - required this.tagName, + required this.tag, }) : super.internal(); - final String tagName; + final Tag tag; @override Override overrideWith( @@ -123,7 +259,7 @@ class TagBookmarksRequestProvider dependencies: null, allTransitiveDependencies: null, debugGetCreateSourceHash: null, - tagName: tagName, + tag: tag, ), ); } @@ -136,13 +272,13 @@ class TagBookmarksRequestProvider @override bool operator ==(Object other) { - return other is TagBookmarksRequestProvider && other.tagName == tagName; + return other is TagBookmarksRequestProvider && other.tag == tag; } @override int get hashCode { var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, tagName.hashCode); + hash = _SystemHash.combine(hash, tag.hashCode); return _SystemHash.finish(hash); } @@ -150,8 +286,8 @@ class TagBookmarksRequestProvider mixin TagBookmarksRequestRef on AutoDisposeFutureProviderRef> { - /// The parameter `tagName` of this provider. - String get tagName; + /// The parameter `tag` of this provider. + Tag get tag; } class _TagBookmarksRequestProviderElement @@ -160,7 +296,7 @@ class _TagBookmarksRequestProviderElement _TagBookmarksRequestProviderElement(super.provider); @override - String get tagName => (origin as TagBookmarksRequestProvider).tagName; + Tag get tag => (origin as TagBookmarksRequestProvider).tag; } String _$tagBookmarksHash() => r'bcfcc986b8d58ccff54ed6dec3ab2a1a8c33a4f7'; diff --git a/lib/screens/tag_bookmarks/ui/tag_bookmarks.dart b/lib/screens/tag_bookmarks/ui/tag_bookmarks.dart index 838e724..220d966 100644 --- a/lib/screens/tag_bookmarks/ui/tag_bookmarks.dart +++ b/lib/screens/tag_bookmarks/ui/tag_bookmarks.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:linkdy/screens/bookmarks/ui/bookmark_item.dart'; @@ -6,20 +7,49 @@ import 'package:linkdy/screens/tag_bookmarks/provider/tag_bookmarks.provider.dar import 'package:linkdy/widgets/error_screen.dart'; import 'package:linkdy/widgets/no_data_screen.dart'; -import 'package:linkdy/i18n/strings.g.dart'; +import 'package:linkdy/models/api_response.dart'; +import 'package:linkdy/models/data/bookmarks.dart'; +import 'package:linkdy/models/data/tag_bookmarks.dart'; import 'package:linkdy/models/data/tags.dart'; +import 'package:linkdy/providers/router_provider.dart'; +import 'package:linkdy/router/paths.dart'; +import 'package:linkdy/i18n/strings.g.dart'; class TagBookmarksScreen extends ConsumerWidget { - final Tag tag; + final String? tagId; + final Tag? tag; const TagBookmarksScreen({ Key? key, - required this.tag, + required this.tagId, + this.tag, }) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { - final bookmarks = ref.watch(tagBookmarksRequestProvider(tag.name!)); + // Redirect to initial screen if no tagId or tag + SchedulerBinding.instance.addPostFrameCallback((_) { + if (tag == null && tagId == null) { + final router = ref.watch(routerProvider); + while (router.canPop() == true) { + router.pop(); + } + router.pushReplacement(RoutesPaths.bookmarks); + } + }); + + if (tag == null && tagId == null) return const Material(); + + final bookmarksResponse = + tag != null ? ref.watch(tagBookmarksRequestProvider(tag!)) : ref.watch(tagIdBookmarksRequestProvider(tagId!)); + + final bookmarks = tag != null + ? (bookmarksResponse.value as ApiResponse?)?.content + : (bookmarksResponse.value as ApiResponse?)?.content?.bookmarksResponse; + + final title = tag != null + ? tag!.name! + : ((bookmarksResponse.value as ApiResponse?)?.content?.tag?.name ?? ''); return Scaffold( body: NestedScrollView( @@ -31,7 +61,7 @@ class TagBookmarksScreen extends ConsumerWidget { floating: true, centerTitle: false, forceElevated: innerBoxIsScrolled, - title: Text(tag.name!), + title: Text(title != "" ? "#$title" : ""), ), ), ], @@ -41,37 +71,39 @@ class TagBookmarksScreen extends ConsumerWidget { child: Builder( builder: (context) => RefreshIndicator( displacement: 120, - onRefresh: () => ref.refresh(tagBookmarksRequestProvider(tag.name!).future), + onRefresh: () => tag != null + ? ref.watch(tagBookmarksRequestProvider(tag!).future) + : ref.watch(tagIdBookmarksRequestProvider(tagId!).future), child: CustomScrollView( slivers: [ SliverOverlapInjector( handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), ), - if (bookmarks.isLoading && !bookmarks.isRefreshing) + if (bookmarksResponse.isLoading && !bookmarksResponse.isRefreshing) const SliverFillRemaining( child: Center(child: CircularProgressIndicator()), ), - if (bookmarks.value != null && bookmarks.value!.successful == false) + if (bookmarksResponse.value != null && bookmarksResponse.value!.successful == false) SliverFillRemaining( child: ErrorScreen( error: t.bookmarks.cannotLoadBookmarks, ), ), - if (bookmarks.value != null && - bookmarks.value!.successful == true && - bookmarks.value!.content!.results!.isEmpty) + if (bookmarksResponse.value != null && + bookmarksResponse.value!.successful == true && + bookmarks!.results!.isEmpty) SliverFillRemaining( child: NoDataScreen( message: t.tags.tagBookmarks.noBookmarksWithThisTag, ), ), - if (bookmarks.value != null && - bookmarks.value!.successful == true && - bookmarks.value!.content!.results!.isNotEmpty) + if (bookmarksResponse.value != null && + bookmarksResponse.value!.successful == true && + bookmarks!.results!.isNotEmpty) SliverList.builder( - itemCount: bookmarks.value!.content!.results!.length, + itemCount: bookmarks.results!.length, itemBuilder: (context, index) => BookmarkItem( - bookmark: bookmarks.value!.content!.results![index], + bookmark: bookmarks.results![index], ), ), ], diff --git a/lib/services/api_client.dart b/lib/services/api_client.dart index 6c3c640..b16f715 100644 --- a/lib/services/api_client.dart +++ b/lib/services/api_client.dart @@ -121,4 +121,17 @@ class ApiClientService { return const ApiResponse(successful: false); } } + + Future> fetchTagById(String tagId) async { + try { + final response = await dioInstance.get("/tags/$tagId/"); + return ApiResponse( + successful: true, + content: Tag.fromJson(response.data), + ); + } catch (e, stackTrace) { + Sentry.captureException(e, stackTrace: stackTrace); + return const ApiResponse(successful: false); + } + } }