diff --git a/lib/config/app_screens.dart b/lib/config/app_screens.dart index 6d75296..6040ef4 100644 --- a/lib/config/app_screens.dart +++ b/lib/config/app_screens.dart @@ -7,14 +7,9 @@ import 'package:linkdy/router/paths.dart'; final appScreens = [ AppRoute( icon: Icons.bookmarks_rounded, - route: RoutesPaths.links, + route: RoutesPaths.bookmarks, name: t.bookmarks.bookmarks, ), - AppRoute( - icon: Icons.search_rounded, - route: RoutesPaths.search, - name: t.search.search, - ), AppRoute( icon: Icons.settings_rounded, route: RoutesPaths.settings, diff --git a/lib/i18n/strings.g.dart b/lib/i18n/strings.g.dart index 968c888..d3f59e8 100644 --- a/lib/i18n/strings.g.dart +++ b/lib/i18n/strings.g.dart @@ -4,9 +4,9 @@ /// To regenerate, run: `dart run slang` /// /// Locales: 2 -/// Strings: 212 (106 per locale) +/// Strings: 222 (111 per locale) /// -/// Built on 2024-02-24 at 00:56 UTC +/// Built on 2024-02-24 at 11:55 UTC // coverage:ignore-file // ignore_for_file: type=lint @@ -216,6 +216,7 @@ class _StringsBookmarksEn { String get cannotLoadBookmarks => 'Cannot load the bookmarks'; late final _StringsBookmarksDatesEn dates = _StringsBookmarksDatesEn._(_root); late final _StringsBookmarksAddBookmarkEn addBookmark = _StringsBookmarksAddBookmarkEn._(_root); + late final _StringsBookmarksSearchEn search = _StringsBookmarksSearchEn._(_root); } // Path: search @@ -333,6 +334,20 @@ class _StringsBookmarksAddBookmarkEn { String get leaveEmptyUseWebsiteDescription => 'Leave empty to use the website\'s description.'; } +// Path: bookmarks.search +class _StringsBookmarksSearchEn { + _StringsBookmarksSearchEn._(this._root); + + final Translations _root; // ignore: unused_field + + // Translations + String get searchBookmarks => 'Search bookmarks'; + String get clearSearch => 'Clear search'; + String get inputSearchTerm => 'Input a search term'; + String get cannotSearchError => 'Cannot search content due to an error'; + String get inputtedSearchTermNoResults => 'The inputted search term doesn\'t have any results'; +} + // Path: settings.customization class _StringsSettingsCustomizationEn { _StringsSettingsCustomizationEn._(this._root); @@ -468,6 +483,7 @@ class _StringsBookmarksEs implements _StringsBookmarksEn { @override String get cannotLoadBookmarks => 'No se pueden cargar los marcadores'; @override late final _StringsBookmarksDatesEs dates = _StringsBookmarksDatesEs._(_root); @override late final _StringsBookmarksAddBookmarkEs addBookmark = _StringsBookmarksAddBookmarkEs._(_root); + @override late final _StringsBookmarksSearchEs search = _StringsBookmarksSearchEs._(_root); } // Path: search @@ -585,6 +601,20 @@ class _StringsBookmarksAddBookmarkEs implements _StringsBookmarksAddBookmarkEn { @override String get leaveEmptyUseWebsiteDescription => 'Dejar vacío para usar la descripción del sitio web.'; } +// Path: bookmarks.search +class _StringsBookmarksSearchEs implements _StringsBookmarksSearchEn { + _StringsBookmarksSearchEs._(this._root); + + @override final _StringsEs _root; // ignore: unused_field + + // Translations + @override String get searchBookmarks => 'Buscar marcadores'; + @override String get clearSearch => 'Limpiar búsqueda'; + @override String get inputSearchTerm => 'Introduce un término de búsqueda'; + @override String get cannotSearchError => 'No se puede buscar el contenido por un error'; + @override String get inputtedSearchTermNoResults => 'El término de búsqueda introducido no tiene resultados'; +} + // Path: settings.customization class _StringsSettingsCustomizationEs implements _StringsSettingsCustomizationEn { _StringsSettingsCustomizationEs._(this._root); @@ -692,6 +722,11 @@ extension on Translations { case 'bookmarks.addBookmark.errorSavingBookmark': return 'An error occured when saving the new bookmark.'; case 'bookmarks.addBookmark.leaveEmptyUseWebsiteTitle': return 'Leave empty to use the website\'s title.'; case 'bookmarks.addBookmark.leaveEmptyUseWebsiteDescription': return 'Leave empty to use the website\'s description.'; + case 'bookmarks.search.searchBookmarks': return 'Search bookmarks'; + case 'bookmarks.search.clearSearch': return 'Clear search'; + case 'bookmarks.search.inputSearchTerm': return 'Input a search term'; + case 'bookmarks.search.cannotSearchError': return 'Cannot search content due to an error'; + case 'bookmarks.search.inputtedSearchTermNoResults': return 'The inputted search term doesn\'t have any results'; case 'search.search': return 'Search'; case 'search.bookmarks': return 'Bookmarks'; case 'search.tags': return 'Tags'; @@ -806,6 +841,11 @@ extension on _StringsEs { case 'bookmarks.addBookmark.errorSavingBookmark': return 'Ocurrió un error al guardar el nuevo marcador.'; case 'bookmarks.addBookmark.leaveEmptyUseWebsiteTitle': return 'Dejar vacío para usar el título del sitio web.'; case 'bookmarks.addBookmark.leaveEmptyUseWebsiteDescription': return 'Dejar vacío para usar la descripción del sitio web.'; + case 'bookmarks.search.searchBookmarks': return 'Buscar marcadores'; + case 'bookmarks.search.clearSearch': return 'Limpiar búsqueda'; + case 'bookmarks.search.inputSearchTerm': return 'Introduce un término de búsqueda'; + case 'bookmarks.search.cannotSearchError': return 'No se puede buscar el contenido por un error'; + case 'bookmarks.search.inputtedSearchTermNoResults': return 'El término de búsqueda introducido no tiene resultados'; case 'search.search': return 'Buscar'; case 'search.bookmarks': return 'Marcadores'; case 'search.tags': return 'Etiquetas'; diff --git a/lib/i18n/strings_en.i18n.json b/lib/i18n/strings_en.i18n.json index af9f0ae..61422fe 100644 --- a/lib/i18n/strings_en.i18n.json +++ b/lib/i18n/strings_en.i18n.json @@ -65,6 +65,13 @@ "errorSavingBookmark": "An error occured when saving the new bookmark.", "leaveEmptyUseWebsiteTitle": "Leave empty to use the website's title.", "leaveEmptyUseWebsiteDescription": "Leave empty to use the website's description." + }, + "search": { + "searchBookmarks": "Search bookmarks", + "clearSearch": "Clear search", + "inputSearchTerm": "Input a search term", + "cannotSearchError": "Cannot search content due to an error", + "inputtedSearchTermNoResults": "The inputted search term doesn't have any results" } }, "search": { diff --git a/lib/i18n/strings_es.i18n.json b/lib/i18n/strings_es.i18n.json index e42f0ff..b722cd0 100644 --- a/lib/i18n/strings_es.i18n.json +++ b/lib/i18n/strings_es.i18n.json @@ -65,6 +65,13 @@ "errorSavingBookmark": "Ocurrió un error al guardar el nuevo marcador.", "leaveEmptyUseWebsiteTitle": "Dejar vacío para usar el título del sitio web.", "leaveEmptyUseWebsiteDescription": "Dejar vacío para usar la descripción del sitio web." + }, + "search": { + "searchBookmarks": "Buscar marcadores", + "clearSearch": "Limpiar búsqueda", + "inputSearchTerm": "Introduce un término de búsqueda", + "cannotSearchError": "No se puede buscar el contenido por un error", + "inputtedSearchTermNoResults": "El término de búsqueda introducido no tiene resultados" } }, "search": { diff --git a/lib/providers/router_provider.dart b/lib/providers/router_provider.dart index bd2e249..58c09b7 100644 --- a/lib/providers/router_provider.dart +++ b/lib/providers/router_provider.dart @@ -16,7 +16,7 @@ GoRouter router(RouterRef ref) { } return null; }, - initialLocation: RoutesPaths.links, + initialLocation: RoutesPaths.bookmarks, routes: appRoutes, navigatorKey: rootNavigatorKey, ); diff --git a/lib/providers/router_provider.g.dart b/lib/providers/router_provider.g.dart index 55ffa49..d4f6303 100644 --- a/lib/providers/router_provider.g.dart +++ b/lib/providers/router_provider.g.dart @@ -6,7 +6,7 @@ part of 'router_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$routerHash() => r'c7bebdd3bf825fbce96530b63367f1c30b0f7c0b'; +String _$routerHash() => r'63ab878a4f0eadb075a6d23d1e32a8d233fcf74e'; /// See also [router]. @ProviderFor(router) diff --git a/lib/router/paths.dart b/lib/router/paths.dart index c9b2f9b..8f1b158 100644 --- a/lib/router/paths.dart +++ b/lib/router/paths.dart @@ -1,6 +1,7 @@ class RoutesPaths { static const onboarding = "/onboarding"; - static const links = "/links"; + static const bookmarks = "/bookmarks"; + static const bookmarksSearch = "/bookmarks/search"; static const webview = "/webview"; static const search = "/search"; static const settings = "/settings"; diff --git a/lib/router/routes.dart b/lib/router/routes.dart index 315d219..1cb68c0 100644 --- a/lib/router/routes.dart +++ b/lib/router/routes.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:linkdy/screens/bookmarks/ui/bookmarks.dart'; +import 'package:linkdy/screens/bookmarks/ui/search_bookmarks.dart'; import 'package:linkdy/screens/onboarding/ui/onboarding.dart'; -import 'package:linkdy/screens/search/ui/search.dart'; import 'package:linkdy/screens/settings/ui/customization/customization.dart'; import 'package:linkdy/screens/settings/ui/general_settings/general_settings.dart'; import 'package:linkdy/screens/settings/ui/settings.dart'; @@ -34,19 +34,19 @@ final List appRoutes = [ ), routes: [ GoRoute( - path: RoutesPaths.links, + path: RoutesPaths.bookmarks, builder: (context, state) => const BookmarksScreen(), ), - GoRoute( - path: RoutesPaths.search, - builder: (context, state) => const Search(), - ), GoRoute( path: RoutesPaths.settings, - builder: (context, state) => const Settings(), + builder: (context, state) => const SettingsScreen(), ), ], ), + GoRoute( + path: RoutesPaths.bookmarksSearch, + builder: (context, state) => const SearchBookmarksScreen(), + ), GoRoute( path: RoutesPaths.customization, builder: (context, state) => const Customization(), diff --git a/lib/screens/search/model/search.model.dart b/lib/screens/bookmarks/model/search_bookmarks.model.dart similarity index 77% rename from lib/screens/search/model/search.model.dart rename to lib/screens/bookmarks/model/search_bookmarks.model.dart index d7ae231..66252bd 100644 --- a/lib/screens/search/model/search.model.dart +++ b/lib/screens/bookmarks/model/search_bookmarks.model.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; -class SearchModel { +class SearchBookmarksModel { final TextEditingController searchController; String? searchTerm; - SearchModel({ + SearchBookmarksModel({ required this.searchController, this.searchTerm = "", }); diff --git a/lib/screens/search/provider/search.provider.dart b/lib/screens/bookmarks/provider/search_bookmarks.provider.dart similarity index 51% rename from lib/screens/search/provider/search.provider.dart rename to lib/screens/bookmarks/provider/search_bookmarks.provider.dart index 74c3b44..63574dc 100644 --- a/lib/screens/search/provider/search.provider.dart +++ b/lib/screens/bookmarks/provider/search_bookmarks.provider.dart @@ -1,34 +1,26 @@ import 'package:flutter/material.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; -import 'package:linkdy/screens/search/model/search.model.dart'; +import 'package:linkdy/screens/bookmarks/model/search_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/providers/api_client_provider.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; -part 'search.provider.g.dart'; +part 'search_bookmarks.provider.g.dart'; @riverpod -FutureOr?> searchBookmarks(SearchBookmarksRef ref) async { - if (ref.watch(searchProvider).searchTerm == "") return null; - final result = await ref.read(apiClientProvider)!.fetchBookmarks(q: ref.watch(searchProvider).searchTerm); +FutureOr?> fetchSearchBookmarks(FetchSearchBookmarksRef ref) async { + if (ref.watch(searchBookmarksProvider).searchTerm == "") return null; + final result = await ref.read(apiClientProvider)!.fetchBookmarks(q: ref.watch(searchBookmarksProvider).searchTerm); return result; } @riverpod -FutureOr?> searchTags(SearchTagsRef ref) async { - if (ref.watch(searchProvider).searchTerm == "") return null; - final result = await ref.read(apiClientProvider)!.fetchTags(q: ref.watch(searchProvider).searchTerm); - return result; -} - -@riverpod -class Search extends _$Search { +class SearchBookmarks extends _$SearchBookmarks { @override - SearchModel build() { - return SearchModel( + SearchBookmarksModel build() { + return SearchBookmarksModel( searchController: TextEditingController(), ); } diff --git a/lib/screens/bookmarks/provider/search_bookmarks.provider.g.dart b/lib/screens/bookmarks/provider/search_bookmarks.provider.g.dart new file mode 100644 index 0000000..e22ab86 --- /dev/null +++ b/lib/screens/bookmarks/provider/search_bookmarks.provider.g.dart @@ -0,0 +1,44 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'search_bookmarks.provider.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$fetchSearchBookmarksHash() => + r'2b871e9d1f75fb7f7b969f819cfdd9442593c4cb'; + +/// See also [fetchSearchBookmarks]. +@ProviderFor(fetchSearchBookmarks) +final fetchSearchBookmarksProvider = + AutoDisposeFutureProvider?>.internal( + fetchSearchBookmarks, + name: r'fetchSearchBookmarksProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$fetchSearchBookmarksHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef FetchSearchBookmarksRef + = AutoDisposeFutureProviderRef?>; +String _$searchBookmarksHash() => r'56efd123e03f6a080f4a61cc91794aab2e563199'; + +/// See also [SearchBookmarks]. +@ProviderFor(SearchBookmarks) +final searchBookmarksProvider = + AutoDisposeNotifierProvider.internal( + SearchBookmarks.new, + name: r'searchBookmarksProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$searchBookmarksHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$SearchBookmarks = AutoDisposeNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/screens/bookmarks/ui/bookmark_item.dart b/lib/screens/bookmarks/ui/bookmark_item.dart index a41072f..c12760c 100644 --- a/lib/screens/bookmarks/ui/bookmark_item.dart +++ b/lib/screens/bookmarks/ui/bookmark_item.dart @@ -8,10 +8,10 @@ import 'package:linkdy/router/paths.dart'; import 'package:linkdy/utils/date_to_string.dart'; import 'package:linkdy/utils/open_url.dart'; -class LinkItem extends ConsumerWidget { +class BookmarkItem extends ConsumerWidget { final Bookmark bookmark; - const LinkItem({ + const BookmarkItem({ Key? key, required this.bookmark, }) : super(key: key); diff --git a/lib/screens/bookmarks/ui/bookmarks.dart b/lib/screens/bookmarks/ui/bookmarks.dart index f5ce5dd..722e518 100644 --- a/lib/screens/bookmarks/ui/bookmarks.dart +++ b/lib/screens/bookmarks/ui/bookmarks.dart @@ -10,6 +10,8 @@ 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/providers/router_provider.dart'; +import 'package:linkdy/router/paths.dart'; class BookmarksScreen extends ConsumerWidget { const BookmarksScreen({Key? key}) : super(key: key); @@ -47,6 +49,14 @@ class BookmarksScreen extends ConsumerWidget { centerTitle: false, forceElevated: innerBoxIsScrolled, title: Text(t.bookmarks.bookmarks), + actions: [ + IconButton( + onPressed: () => ref.watch(routerProvider).push(RoutesPaths.bookmarksSearch), + icon: const Icon(Icons.search_rounded), + tooltip: t.bookmarks.search.searchBookmarks, + ), + const SizedBox(width: 8), + ], ), ), ], @@ -94,7 +104,7 @@ class BookmarksScreen extends ConsumerWidget { final link = bookmarks.value?.content?.results?[index]; return Column( children: [ - LinkItem(bookmark: link!), + BookmarkItem(bookmark: link!), if (index < bookmarks.value!.content!.results!.length - 1) Padding( padding: const EdgeInsets.symmetric(horizontal: 16), diff --git a/lib/screens/bookmarks/ui/search_bookmarks.dart b/lib/screens/bookmarks/ui/search_bookmarks.dart new file mode 100644 index 0000000..5e2668d --- /dev/null +++ b/lib/screens/bookmarks/ui/search_bookmarks.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import 'package:linkdy/screens/bookmarks/provider/search_bookmarks.provider.dart'; +import 'package:linkdy/screens/bookmarks/ui/bookmark_item.dart'; +import 'package:linkdy/widgets/error_screen.dart'; +import 'package:linkdy/widgets/no_data_screen.dart'; + +import 'package:linkdy/providers/router_provider.dart'; + +import 'package:linkdy/i18n/strings.g.dart'; + +class SearchBookmarksScreen extends ConsumerWidget { + const SearchBookmarksScreen({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Scaffold( + appBar: AppBar( + toolbarHeight: 68, + leading: Padding( + padding: const EdgeInsets.only(bottom: 8), + child: BackButton( + onPressed: () => ref.watch(routerProvider).pop(), + ), + ), + title: Padding( + padding: const EdgeInsets.only(bottom: 8), + child: ClipRRect( + borderRadius: BorderRadius.circular(50), + child: TextFormField( + controller: ref.watch(searchBookmarksProvider).searchController, + onChanged: ref.read(searchBookmarksProvider.notifier).setSearchTerm, + decoration: InputDecoration( + hintText: t.search.search, + prefixIcon: const Icon(Icons.search_rounded), + border: InputBorder.none, + filled: true, + fillColor: Colors.grey.withOpacity(0.2), + suffixIcon: ref.watch(searchBookmarksProvider).searchController.text != "" + ? IconButton( + onPressed: ref.read(searchBookmarksProvider.notifier).clearSearch, + icon: const Icon( + Icons.close_rounded, + size: 20, + ), + tooltip: t.search.clearSearch, + ) + : null, + ), + textInputAction: TextInputAction.search, + ), + ), + ), + ), + body: Builder( + builder: (context) { + if (ref.watch(searchBookmarksProvider).searchTerm == "") { + return NoDataScreen(message: t.bookmarks.search.inputSearchTerm); + } + + return ref.watch(fetchSearchBookmarksProvider).when( + data: (data) { + if (data == null || data.successful == false) { + return ErrorScreen( + error: t.bookmarks.search.cannotSearchError, + ); + } else { + return ListView.builder( + itemCount: data.content!.results!.length, + itemBuilder: (context, index) => BookmarkItem( + bookmark: data.content!.results![index], + ), + ); + } + }, + error: (error, stackTrace) => ErrorScreen( + error: t.bookmarks.search.cannotSearchError, + ), + loading: () => const Center( + child: CircularProgressIndicator(), + ), + ); + }, + ), + ); + } +} diff --git a/lib/screens/search/provider/search.provider.g.dart b/lib/screens/search/provider/search.provider.g.dart deleted file mode 100644 index 48f334e..0000000 --- a/lib/screens/search/provider/search.provider.g.dart +++ /dev/null @@ -1,58 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'search.provider.dart'; - -// ************************************************************************** -// RiverpodGenerator -// ************************************************************************** - -String _$searchBookmarksHash() => r'301f5c0355044978b6ddcfa13478b8729773f290'; - -/// See also [searchBookmarks]. -@ProviderFor(searchBookmarks) -final searchBookmarksProvider = - AutoDisposeFutureProvider?>.internal( - searchBookmarks, - name: r'searchBookmarksProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$searchBookmarksHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef SearchBookmarksRef - = AutoDisposeFutureProviderRef?>; -String _$searchTagsHash() => r'1fc8cf44f512490fcf369b843bf3ca137e867bb6'; - -/// See also [searchTags]. -@ProviderFor(searchTags) -final searchTagsProvider = - AutoDisposeFutureProvider?>.internal( - searchTags, - name: r'searchTagsProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$searchTagsHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef SearchTagsRef - = AutoDisposeFutureProviderRef?>; -String _$searchHash() => r'bdb992f3343d912d9422fa5da14ad0d5704f18e7'; - -/// See also [Search]. -@ProviderFor(Search) -final searchProvider = - AutoDisposeNotifierProvider.internal( - Search.new, - name: r'searchProvider', - debugGetCreateSourceHash: - const bool.fromEnvironment('dart.vm.product') ? null : _$searchHash, - dependencies: null, - allTransitiveDependencies: null, -); - -typedef _$Search = AutoDisposeNotifier; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/screens/search/ui/bookmarks_search_list.dart b/lib/screens/search/ui/bookmarks_search_list.dart deleted file mode 100644 index 8f6d30c..0000000 --- a/lib/screens/search/ui/bookmarks_search_list.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:linkdy/i18n/strings.g.dart'; - -import 'package:linkdy/screens/search/ui/input_search_term.dart'; -import 'package:linkdy/screens/bookmarks/ui/bookmark_item.dart'; -import 'package:linkdy/screens/search/provider/search.provider.dart'; -import 'package:linkdy/widgets/error_screen.dart'; -import 'package:linkdy/widgets/no_data_screen.dart'; -import 'package:linkdy/widgets/sliver_tab_body.dart'; - -class BookmarksSearchList extends ConsumerWidget { - const BookmarksSearchList({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final bookmarksSearch = ref.watch(searchBookmarksProvider); - - if (ref.watch(searchProvider).searchTerm == "") { - return const SliverTabBody( - child: SliverFillRemaining( - child: InputSearchTerm(), - ), - ); - } - - return bookmarksSearch.when( - data: (data) { - if (data!.successful == false) { - return SliverTabBody( - child: SliverFillRemaining( - child: ErrorScreen( - error: t.search.cannotSearchError, - ), - ), - ); - } else if (data.content!.results!.isEmpty) { - return SliverTabBody( - child: SliverFillRemaining( - child: NoDataScreen( - message: t.search.inputtedSearchTermNoResults, - ), - ), - ); - } else { - return SliverTabBodyRefresh( - onRefresh: () => ref.refresh(searchBookmarksProvider.future), - child: SliverList.builder( - itemCount: data.content!.results!.length, - itemBuilder: (context, index) => LinkItem( - bookmark: data.content!.results![index], - ), - ), - ); - } - }, - error: (error, stackTrace) => SliverTabBody( - child: SliverFillRemaining( - child: ErrorScreen( - error: t.search.cannotSearchError, - ), - ), - ), - loading: () => const SliverTabBody( - child: SliverFillRemaining( - child: Center( - child: CircularProgressIndicator(), - ), - ), - ), - ); - } -} diff --git a/lib/screens/search/ui/input_search_term.dart b/lib/screens/search/ui/input_search_term.dart deleted file mode 100644 index b427c16..0000000 --- a/lib/screens/search/ui/input_search_term.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:linkdy/i18n/strings.g.dart'; - -class InputSearchTerm extends StatelessWidget { - const InputSearchTerm({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(16), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.search_rounded, - size: 50, - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - const SizedBox(height: 30), - Text( - t.search.inputSearchTerm, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 24, - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - ), - ], - ), - ); - } -} diff --git a/lib/screens/search/ui/search.dart b/lib/screens/search/ui/search.dart deleted file mode 100644 index cb23575..0000000 --- a/lib/screens/search/ui/search.dart +++ /dev/null @@ -1,106 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import 'package:linkdy/screens/search/provider/search.provider.dart'; - -import 'package:linkdy/i18n/strings.g.dart'; -import 'package:linkdy/screens/search/ui/bookmarks_search_list.dart'; -import 'package:linkdy/screens/search/ui/tags_search_list.dart'; - -class Search extends ConsumerStatefulWidget { - const Search({Key? key}) : super(key: key); - - @override - SearchState createState() => SearchState(); -} - -class SearchState extends ConsumerState with TickerProviderStateMixin { - late TabController _tabController; - - @override - void initState() { - _tabController = TabController(length: 2, vsync: this); - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - body: DefaultTabController( - length: 2, - child: NestedScrollView( - headerSliverBuilder: (context, innerBoxIsScrolled) => [ - SliverOverlapAbsorber( - handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), - sliver: SliverAppBar( - pinned: true, - floating: true, - centerTitle: false, - forceElevated: innerBoxIsScrolled, - toolbarHeight: 70, - title: ClipRRect( - borderRadius: BorderRadius.circular(50), - child: TextFormField( - controller: ref.watch(searchProvider).searchController, - onChanged: (_) => ref.read(searchProvider.notifier).notifyOnChange(), - onFieldSubmitted: ref.read(searchProvider.notifier).setSearchTerm, - decoration: InputDecoration( - hintText: t.search.search, - prefixIcon: const Icon(Icons.search_rounded), - border: InputBorder.none, - filled: true, - fillColor: Colors.grey.withOpacity(0.2), - suffixIcon: ref.watch(searchProvider).searchController.text != "" - ? IconButton( - onPressed: ref.read(searchProvider.notifier).clearSearch, - icon: const Icon( - Icons.close_rounded, - size: 20, - ), - tooltip: t.search.clearSearch, - ) - : null, - ), - textInputAction: TextInputAction.search, - ), - ), - bottom: TabBar( - controller: _tabController, - tabs: [ - Tab( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon(Icons.bookmarks_rounded), - const SizedBox(width: 8), - Text(t.search.bookmarks), - ], - ), - ), - Tab( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon(Icons.label_rounded), - const SizedBox(width: 8), - Text(t.search.tags), - ], - ), - ), - ], - ), - ), - ), - ], - body: TabBarView( - controller: _tabController, - children: const [ - BookmarksSearchList(), - TagsSearchList(), - ], - ), - ), - ), - ); - } -} diff --git a/lib/screens/search/ui/tags_search_list.dart b/lib/screens/search/ui/tags_search_list.dart deleted file mode 100644 index ed15d7c..0000000 --- a/lib/screens/search/ui/tags_search_list.dart +++ /dev/null @@ -1,94 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import 'package:linkdy/screens/search/provider/search.provider.dart'; -import 'package:linkdy/screens/search/ui/input_search_term.dart'; -import 'package:linkdy/widgets/error_screen.dart'; -import 'package:linkdy/widgets/no_data_screen.dart'; -import 'package:linkdy/widgets/sliver_tab_body.dart'; - -import 'package:linkdy/i18n/strings.g.dart'; -import 'package:linkdy/utils/date_to_string.dart'; - -class TagsSearchList extends ConsumerWidget { - const TagsSearchList({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final bookmarksSearch = ref.watch(searchTagsProvider); - - if (ref.watch(searchProvider).searchTerm == "") { - return const SliverTabBody( - child: SliverFillRemaining( - child: InputSearchTerm(), - ), - ); - } - - return bookmarksSearch.when( - data: (data) { - if (data!.successful == false) { - return SliverTabBody( - child: SliverFillRemaining( - child: SliverTabBody( - child: SliverFillRemaining( - child: ErrorScreen( - error: t.search.cannotSearchError, - ), - ), - ), - ), - ); - } else if (data.content!.results!.isEmpty) { - return SliverTabBody( - child: SliverFillRemaining( - child: NoDataScreen( - message: t.search.inputtedSearchTermNoResults, - ), - ), - ); - } else { - return SliverTabBodyRefresh( - onRefresh: () => ref.refresh(searchTagsProvider.future), - child: SliverList.builder( - itemCount: data.content!.results!.length, - itemBuilder: (context, index) => ListTile( - title: Text( - data.content!.results![index].name!, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Theme.of(context).colorScheme.onSurface, - ), - ), - subtitle: Text( - t.search.created( - created: dateToString(data.content!.results![index].dateAdded!), - ), - ), - ), - ), - ); - } - }, - error: (error, stackTrace) => SliverTabBody( - child: SliverFillRemaining( - child: SliverTabBody( - child: SliverFillRemaining( - child: ErrorScreen( - error: t.search.cannotSearchError, - ), - ), - ), - ), - ), - loading: () => const SliverTabBody( - child: SliverFillRemaining( - child: Center( - child: CircularProgressIndicator(), - ), - ), - ), - ); - } -} diff --git a/lib/screens/settings/ui/settings.dart b/lib/screens/settings/ui/settings.dart index e41b341..33ca9e8 100644 --- a/lib/screens/settings/ui/settings.dart +++ b/lib/screens/settings/ui/settings.dart @@ -13,8 +13,8 @@ import 'package:linkdy/constants/strings.dart'; import 'package:linkdy/constants/urls.dart'; import 'package:linkdy/i18n/strings.g.dart'; -class Settings extends ConsumerWidget { - const Settings({Key? key}) : super(key: key); +class SettingsScreen extends ConsumerWidget { + const SettingsScreen({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/lib/widgets/no_data_screen.dart b/lib/widgets/no_data_screen.dart index 9c2001d..2bd2985 100644 --- a/lib/widgets/no_data_screen.dart +++ b/lib/widgets/no_data_screen.dart @@ -12,22 +12,27 @@ class NoDataScreen extends StatelessWidget { Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(16), - child: Column( + child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon( - Icons.clear_rounded, - size: 50, - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), - const SizedBox(height: 30), - Text( - message, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 24, - color: Theme.of(context).colorScheme.onSurfaceVariant, - ), + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.clear_rounded, + size: 50, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + const SizedBox(height: 30), + Text( + message, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 24, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ], ), ], ),