Added shared bookmarks screen and archived bookmarks screen

This commit is contained in:
Juan Gilsanz Polo
2024-02-28 01:13:55 +01:00
parent 4633023b4f
commit 06df3c2e0a
18 changed files with 663 additions and 236 deletions

View File

@@ -5,3 +5,5 @@ enum ConnectionMethod { http, https }
enum SelectedTheme { system, light, dark } enum SelectedTheme { system, light, dark }
enum AuthResult { success, invalidToken, other } enum AuthResult { success, invalidToken, other }
enum FilteredBookmarksMode { tag, shared, archived }

View File

@@ -7,6 +7,6 @@ class ScaffoldMessengerKeys {
static final search = GlobalKey<ScaffoldMessengerState>(); static final search = GlobalKey<ScaffoldMessengerState>();
static final tags = GlobalKey<ScaffoldMessengerState>(); static final tags = GlobalKey<ScaffoldMessengerState>();
static final addBookmark = GlobalKey<ScaffoldMessengerState>(); static final addBookmark = GlobalKey<ScaffoldMessengerState>();
static final tagBookmarks = GlobalKey<ScaffoldMessengerState>(); static final filteredBookmarks = GlobalKey<ScaffoldMessengerState>();
static final webview = GlobalKey<ScaffoldMessengerState>(); static final webview = GlobalKey<ScaffoldMessengerState>();
} }

View File

@@ -4,9 +4,9 @@
/// To regenerate, run: `dart run slang` /// To regenerate, run: `dart run slang`
/// ///
/// Locales: 2 /// Locales: 2
/// Strings: 322 (161 per locale) /// Strings: 332 (166 per locale)
/// ///
/// Built on 2024-02-27 at 17:13 UTC /// Built on 2024-02-28 at 00:11 UTC
// coverage:ignore-file // coverage:ignore-file
// ignore_for_file: type=lint // ignore_for_file: type=lint
@@ -224,6 +224,8 @@ class _StringsBookmarksEn {
String get noBookmarksAdded => 'No bookmarks added'; String get noBookmarksAdded => 'No bookmarks added';
String get cannotLoadBookmarks => 'Cannot load the bookmarks'; String get cannotLoadBookmarks => 'Cannot load the bookmarks';
late final _StringsBookmarksDatesEn dates = _StringsBookmarksDatesEn._(_root); late final _StringsBookmarksDatesEn dates = _StringsBookmarksDatesEn._(_root);
String get shared => 'Shared';
String get archived => 'Archived';
late final _StringsBookmarksAddBookmarkEn addBookmark = _StringsBookmarksAddBookmarkEn._(_root); late final _StringsBookmarksAddBookmarkEn addBookmark = _StringsBookmarksAddBookmarkEn._(_root);
late final _StringsBookmarksSearchEn search = _StringsBookmarksSearchEn._(_root); late final _StringsBookmarksSearchEn search = _StringsBookmarksSearchEn._(_root);
late final _StringsBookmarksBookmarkOptionsEn bookmarkOptions = _StringsBookmarksBookmarkOptionsEn._(_root); late final _StringsBookmarksBookmarkOptionsEn bookmarkOptions = _StringsBookmarksBookmarkOptionsEn._(_root);
@@ -240,7 +242,7 @@ class _StringsTagsEn {
String get tags => 'Tags'; String get tags => 'Tags';
String created({required Object created}) => 'Created: ${created}'; String created({required Object created}) => 'Created: ${created}';
late final _StringsTagsCreateTagEn createTag = _StringsTagsCreateTagEn._(_root); late final _StringsTagsCreateTagEn createTag = _StringsTagsCreateTagEn._(_root);
late final _StringsTagsTagBookmarksEn tagBookmarks = _StringsTagsTagBookmarksEn._(_root); late final _StringsTagsFilteredBookmarksEn filteredBookmarks = _StringsTagsFilteredBookmarksEn._(_root);
} }
// Path: settings // Path: settings
@@ -368,9 +370,10 @@ class _StringsBookmarksBookmarkOptionsEn {
String get delete => 'Delete'; String get delete => 'Delete';
String get unread => 'Unread'; String get unread => 'Unread';
String get read => 'Read'; String get read => 'Read';
String get share => 'Share'; String get shareOptions => 'Share options';
String get shared => 'Shared'; String get shared => 'Shared';
String get archive => 'Archive'; String get archive => 'Archive';
String get unarchive => 'Unarchive';
String get deletingBookmark => 'Deleting bookmark...'; String get deletingBookmark => 'Deleting bookmark...';
String get bookmarkDeleted => 'Bookmark deleted successfully'; String get bookmarkDeleted => 'Bookmark deleted successfully';
String get bookmarkNotDeleted => 'The bookmark couldn\'t be deleted due to an error.'; String get bookmarkNotDeleted => 'The bookmark couldn\'t be deleted due to an error.';
@@ -425,14 +428,16 @@ class _StringsTagsCreateTagEn {
String get errorCreatingTag => 'An error occured when creating the tag.'; String get errorCreatingTag => 'An error occured when creating the tag.';
} }
// Path: tags.tagBookmarks // Path: tags.filteredBookmarks
class _StringsTagsTagBookmarksEn { class _StringsTagsFilteredBookmarksEn {
_StringsTagsTagBookmarksEn._(this._root); _StringsTagsFilteredBookmarksEn._(this._root);
final Translations _root; // ignore: unused_field final Translations _root; // ignore: unused_field
// Translations // Translations
String get noBookmarksWithThisTag => 'There are no bookmarks with this tag'; String get noBookmarksWithThisTag => 'There are no bookmarks with this tag';
String get noArchivedBookmarks => 'There are no archived bookmarks';
String get noSharedBookmarks => 'There are no shared bookmarks';
} }
// Path: settings.customization // Path: settings.customization
@@ -581,6 +586,8 @@ class _StringsBookmarksEs implements _StringsBookmarksEn {
@override String get noBookmarksAdded => 'No hay marcadores añadidos'; @override String get noBookmarksAdded => 'No hay marcadores añadidos';
@override String get cannotLoadBookmarks => 'No se pueden cargar los marcadores'; @override String get cannotLoadBookmarks => 'No se pueden cargar los marcadores';
@override late final _StringsBookmarksDatesEs dates = _StringsBookmarksDatesEs._(_root); @override late final _StringsBookmarksDatesEs dates = _StringsBookmarksDatesEs._(_root);
@override String get shared => 'Compartidos';
@override String get archived => 'Archivados';
@override late final _StringsBookmarksAddBookmarkEs addBookmark = _StringsBookmarksAddBookmarkEs._(_root); @override late final _StringsBookmarksAddBookmarkEs addBookmark = _StringsBookmarksAddBookmarkEs._(_root);
@override late final _StringsBookmarksSearchEs search = _StringsBookmarksSearchEs._(_root); @override late final _StringsBookmarksSearchEs search = _StringsBookmarksSearchEs._(_root);
@override late final _StringsBookmarksBookmarkOptionsEs bookmarkOptions = _StringsBookmarksBookmarkOptionsEs._(_root); @override late final _StringsBookmarksBookmarkOptionsEs bookmarkOptions = _StringsBookmarksBookmarkOptionsEs._(_root);
@@ -597,7 +604,7 @@ class _StringsTagsEs implements _StringsTagsEn {
@override String get tags => 'Etiquetas'; @override String get tags => 'Etiquetas';
@override String created({required Object created}) => 'Creado: ${created}'; @override String created({required Object created}) => 'Creado: ${created}';
@override late final _StringsTagsCreateTagEs createTag = _StringsTagsCreateTagEs._(_root); @override late final _StringsTagsCreateTagEs createTag = _StringsTagsCreateTagEs._(_root);
@override late final _StringsTagsTagBookmarksEs tagBookmarks = _StringsTagsTagBookmarksEs._(_root); @override late final _StringsTagsFilteredBookmarksEs filteredBookmarks = _StringsTagsFilteredBookmarksEs._(_root);
} }
// Path: settings // Path: settings
@@ -725,9 +732,10 @@ class _StringsBookmarksBookmarkOptionsEs implements _StringsBookmarksBookmarkOpt
@override String get delete => 'Eliminar'; @override String get delete => 'Eliminar';
@override String get unread => 'No leído'; @override String get unread => 'No leído';
@override String get read => 'Leído'; @override String get read => 'Leído';
@override String get share => 'Compartir'; @override String get shareOptions => 'Opciones de compartir';
@override String get shared => 'Compartido'; @override String get shared => 'Compartido';
@override String get archive => 'Archivar'; @override String get archive => 'Archivar';
@override String get unarchive => 'Desarchivar';
@override String get deletingBookmark => 'Eliminando marcador...'; @override String get deletingBookmark => 'Eliminando marcador...';
@override String get bookmarkDeleted => 'Marcador eliminado correctamente'; @override String get bookmarkDeleted => 'Marcador eliminado correctamente';
@override String get bookmarkNotDeleted => 'El marcador no se ha podido eliminar debido a un error.'; @override String get bookmarkNotDeleted => 'El marcador no se ha podido eliminar debido a un error.';
@@ -782,14 +790,16 @@ class _StringsTagsCreateTagEs implements _StringsTagsCreateTagEn {
@override String get errorCreatingTag => 'Ocurrió un error al crear la etiqueta.'; @override String get errorCreatingTag => 'Ocurrió un error al crear la etiqueta.';
} }
// Path: tags.tagBookmarks // Path: tags.filteredBookmarks
class _StringsTagsTagBookmarksEs implements _StringsTagsTagBookmarksEn { class _StringsTagsFilteredBookmarksEs implements _StringsTagsFilteredBookmarksEn {
_StringsTagsTagBookmarksEs._(this._root); _StringsTagsFilteredBookmarksEs._(this._root);
@override final _StringsEs _root; // ignore: unused_field @override final _StringsEs _root; // ignore: unused_field
// Translations // Translations
@override String get noBookmarksWithThisTag => 'No hay marcadores con esta etiqueta'; @override String get noBookmarksWithThisTag => 'No hay marcadores con esta etiqueta';
@override String get noArchivedBookmarks => 'No hay marcadores archivados';
@override String get noSharedBookmarks => 'No hay marcadores compartidos';
} }
// Path: settings.customization // Path: settings.customization
@@ -887,6 +897,8 @@ extension on Translations {
case 'bookmarks.cannotLoadBookmarks': return 'Cannot load the bookmarks'; case 'bookmarks.cannotLoadBookmarks': return 'Cannot load the bookmarks';
case 'bookmarks.dates.todayAt': return ({required Object time}) => 'Today, ${time}'; case 'bookmarks.dates.todayAt': return ({required Object time}) => 'Today, ${time}';
case 'bookmarks.dates.yesterdayAt': return ({required Object time}) => 'Yesterday, ${time}'; case 'bookmarks.dates.yesterdayAt': return ({required Object time}) => 'Yesterday, ${time}';
case 'bookmarks.shared': return 'Shared';
case 'bookmarks.archived': return 'Archived';
case 'bookmarks.addBookmark.addBookmark': return 'Add new bookmark'; case 'bookmarks.addBookmark.addBookmark': return 'Add new bookmark';
case 'bookmarks.addBookmark.editBookmark': return 'Edit bookmark'; case 'bookmarks.addBookmark.editBookmark': return 'Edit bookmark';
case 'bookmarks.addBookmark.bookmarkUrl': return 'Bookmark URL'; case 'bookmarks.addBookmark.bookmarkUrl': return 'Bookmark URL';
@@ -922,9 +934,10 @@ extension on Translations {
case 'bookmarks.bookmarkOptions.delete': return 'Delete'; case 'bookmarks.bookmarkOptions.delete': return 'Delete';
case 'bookmarks.bookmarkOptions.unread': return 'Unread'; case 'bookmarks.bookmarkOptions.unread': return 'Unread';
case 'bookmarks.bookmarkOptions.read': return 'Read'; case 'bookmarks.bookmarkOptions.read': return 'Read';
case 'bookmarks.bookmarkOptions.share': return 'Share'; case 'bookmarks.bookmarkOptions.shareOptions': return 'Share options';
case 'bookmarks.bookmarkOptions.shared': return 'Shared'; case 'bookmarks.bookmarkOptions.shared': return 'Shared';
case 'bookmarks.bookmarkOptions.archive': return 'Archive'; case 'bookmarks.bookmarkOptions.archive': return 'Archive';
case 'bookmarks.bookmarkOptions.unarchive': return 'Unarchive';
case 'bookmarks.bookmarkOptions.deletingBookmark': return 'Deleting bookmark...'; case 'bookmarks.bookmarkOptions.deletingBookmark': return 'Deleting bookmark...';
case 'bookmarks.bookmarkOptions.bookmarkDeleted': return 'Bookmark deleted successfully'; case 'bookmarks.bookmarkOptions.bookmarkDeleted': return 'Bookmark deleted successfully';
case 'bookmarks.bookmarkOptions.bookmarkNotDeleted': return 'The bookmark couldn\'t be deleted due to an error.'; case 'bookmarks.bookmarkOptions.bookmarkNotDeleted': return 'The bookmark couldn\'t be deleted due to an error.';
@@ -961,7 +974,9 @@ extension on Translations {
case 'tags.createTag.name': return 'Name'; case 'tags.createTag.name': return 'Name';
case 'tags.createTag.creatingTag': return 'Creating tag...'; case 'tags.createTag.creatingTag': return 'Creating tag...';
case 'tags.createTag.errorCreatingTag': return 'An error occured when creating the tag.'; case 'tags.createTag.errorCreatingTag': return 'An error occured when creating the tag.';
case 'tags.tagBookmarks.noBookmarksWithThisTag': return 'There are no bookmarks with this tag'; case 'tags.filteredBookmarks.noBookmarksWithThisTag': return 'There are no bookmarks with this tag';
case 'tags.filteredBookmarks.noArchivedBookmarks': return 'There are no archived bookmarks';
case 'tags.filteredBookmarks.noSharedBookmarks': return 'There are no shared bookmarks';
case 'settings.settings': return 'Settings'; case 'settings.settings': return 'Settings';
case 'settings.appSettings': return 'App settings'; case 'settings.appSettings': return 'App settings';
case 'settings.aboutApp': return 'About the app'; case 'settings.aboutApp': return 'About the app';
@@ -1056,6 +1071,8 @@ extension on _StringsEs {
case 'bookmarks.cannotLoadBookmarks': return 'No se pueden cargar los marcadores'; case 'bookmarks.cannotLoadBookmarks': return 'No se pueden cargar los marcadores';
case 'bookmarks.dates.todayAt': return ({required Object time}) => 'Hoy, ${time}'; case 'bookmarks.dates.todayAt': return ({required Object time}) => 'Hoy, ${time}';
case 'bookmarks.dates.yesterdayAt': return ({required Object time}) => 'Ayer, ${time}'; case 'bookmarks.dates.yesterdayAt': return ({required Object time}) => 'Ayer, ${time}';
case 'bookmarks.shared': return 'Compartidos';
case 'bookmarks.archived': return 'Archivados';
case 'bookmarks.addBookmark.addBookmark': return 'Añadir nuevo marcador'; case 'bookmarks.addBookmark.addBookmark': return 'Añadir nuevo marcador';
case 'bookmarks.addBookmark.editBookmark': return 'Editar marcador'; case 'bookmarks.addBookmark.editBookmark': return 'Editar marcador';
case 'bookmarks.addBookmark.url': return 'URL'; case 'bookmarks.addBookmark.url': return 'URL';
@@ -1091,9 +1108,10 @@ extension on _StringsEs {
case 'bookmarks.bookmarkOptions.delete': return 'Eliminar'; case 'bookmarks.bookmarkOptions.delete': return 'Eliminar';
case 'bookmarks.bookmarkOptions.unread': return 'No leído'; case 'bookmarks.bookmarkOptions.unread': return 'No leído';
case 'bookmarks.bookmarkOptions.read': return 'Leído'; case 'bookmarks.bookmarkOptions.read': return 'Leído';
case 'bookmarks.bookmarkOptions.share': return 'Compartir'; case 'bookmarks.bookmarkOptions.shareOptions': return 'Opciones de compartir';
case 'bookmarks.bookmarkOptions.shared': return 'Compartido'; case 'bookmarks.bookmarkOptions.shared': return 'Compartido';
case 'bookmarks.bookmarkOptions.archive': return 'Archivar'; case 'bookmarks.bookmarkOptions.archive': return 'Archivar';
case 'bookmarks.bookmarkOptions.unarchive': return 'Desarchivar';
case 'bookmarks.bookmarkOptions.deletingBookmark': return 'Eliminando marcador...'; case 'bookmarks.bookmarkOptions.deletingBookmark': return 'Eliminando marcador...';
case 'bookmarks.bookmarkOptions.bookmarkDeleted': return 'Marcador eliminado correctamente'; case 'bookmarks.bookmarkOptions.bookmarkDeleted': return 'Marcador eliminado correctamente';
case 'bookmarks.bookmarkOptions.bookmarkNotDeleted': return 'El marcador no se ha podido eliminar debido a un error.'; case 'bookmarks.bookmarkOptions.bookmarkNotDeleted': return 'El marcador no se ha podido eliminar debido a un error.';
@@ -1130,7 +1148,9 @@ extension on _StringsEs {
case 'tags.createTag.name': return 'Nombre'; case 'tags.createTag.name': return 'Nombre';
case 'tags.createTag.creatingTag': return 'Creando etiqueta...'; case 'tags.createTag.creatingTag': return 'Creando etiqueta...';
case 'tags.createTag.errorCreatingTag': return 'Ocurrió un error al crear la etiqueta.'; case 'tags.createTag.errorCreatingTag': return 'Ocurrió un error al crear la etiqueta.';
case 'tags.tagBookmarks.noBookmarksWithThisTag': return 'No hay marcadores con esta etiqueta'; case 'tags.filteredBookmarks.noBookmarksWithThisTag': return 'No hay marcadores con esta etiqueta';
case 'tags.filteredBookmarks.noArchivedBookmarks': return 'No hay marcadores archivados';
case 'tags.filteredBookmarks.noSharedBookmarks': return 'No hay marcadores compartidos';
case 'settings.settings': return 'Ajustes'; case 'settings.settings': return 'Ajustes';
case 'settings.appSettings': return 'Ajustes de la aplicación'; case 'settings.appSettings': return 'Ajustes de la aplicación';
case 'settings.aboutApp': return 'Sobre la aplicación'; case 'settings.aboutApp': return 'Sobre la aplicación';

View File

@@ -49,6 +49,8 @@
"todayAt": "Today, $time", "todayAt": "Today, $time",
"yesterdayAt": "Yesterday, $time" "yesterdayAt": "Yesterday, $time"
}, },
"shared": "Shared",
"archived": "Archived",
"addBookmark": { "addBookmark": {
"addBookmark": "Add new bookmark", "addBookmark": "Add new bookmark",
"editBookmark": "Edit bookmark", "editBookmark": "Edit bookmark",
@@ -89,9 +91,10 @@
"delete": "Delete", "delete": "Delete",
"unread": "Unread", "unread": "Unread",
"read": "Read", "read": "Read",
"share": "Share", "shareOptions": "Share options",
"shared": "Shared", "shared": "Shared",
"archive": "Archive", "archive": "Archive",
"unarchive": "Unarchive",
"deletingBookmark": "Deleting bookmark...", "deletingBookmark": "Deleting bookmark...",
"bookmarkDeleted": "Bookmark deleted successfully", "bookmarkDeleted": "Bookmark deleted successfully",
"bookmarkNotDeleted": "The bookmark couldn't be deleted due to an error.", "bookmarkNotDeleted": "The bookmark couldn't be deleted due to an error.",
@@ -135,8 +138,10 @@
"creatingTag": "Creating tag...", "creatingTag": "Creating tag...",
"errorCreatingTag": "An error occured when creating the tag." "errorCreatingTag": "An error occured when creating the tag."
}, },
"tagBookmarks": { "filteredBookmarks": {
"noBookmarksWithThisTag": "There are no bookmarks with this tag" "noBookmarksWithThisTag": "There are no bookmarks with this tag",
"noArchivedBookmarks": "There are no archived bookmarks",
"noSharedBookmarks": "There are no shared bookmarks"
} }
}, },
"settings": { "settings": {

View File

@@ -49,6 +49,8 @@
"todayAt": "Hoy, $time", "todayAt": "Hoy, $time",
"yesterdayAt": "Ayer, $time" "yesterdayAt": "Ayer, $time"
}, },
"shared": "Compartidos",
"archived": "Archivados",
"addBookmark": { "addBookmark": {
"addBookmark": "Añadir nuevo marcador", "addBookmark": "Añadir nuevo marcador",
"editBookmark": "Editar marcador", "editBookmark": "Editar marcador",
@@ -89,9 +91,10 @@
"delete": "Eliminar", "delete": "Eliminar",
"unread": "No leído", "unread": "No leído",
"read": "Leído", "read": "Leído",
"share": "Compartir", "shareOptions": "Opciones de compartir",
"shared": "Compartido", "shared": "Compartido",
"archive": "Archivar", "archive": "Archivar",
"unarchive": "Desarchivar",
"deletingBookmark": "Eliminando marcador...", "deletingBookmark": "Eliminando marcador...",
"bookmarkDeleted": "Marcador eliminado correctamente", "bookmarkDeleted": "Marcador eliminado correctamente",
"bookmarkNotDeleted": "El marcador no se ha podido eliminar debido a un error.", "bookmarkNotDeleted": "El marcador no se ha podido eliminar debido a un error.",
@@ -135,8 +138,10 @@
"creatingTag": "Creando etiqueta...", "creatingTag": "Creando etiqueta...",
"errorCreatingTag": "Ocurrió un error al crear la etiqueta." "errorCreatingTag": "Ocurrió un error al crear la etiqueta."
}, },
"tagBookmarks": { "filteredBookmarks": {
"noBookmarksWithThisTag": "No hay marcadores con esta etiqueta" "noBookmarksWithThisTag": "No hay marcadores con esta etiqueta",
"noArchivedBookmarks": "No hay marcadores archivados",
"noSharedBookmarks": "No hay marcadores compartidos"
} }
}, },
"settings": { "settings": {

View File

@@ -88,4 +88,35 @@ class Bookmark {
"date_added": dateAdded?.toIso8601String(), "date_added": dateAdded?.toIso8601String(),
"date_modified": dateModified?.toIso8601String(), "date_modified": dateModified?.toIso8601String(),
}; };
Bookmark copyWith({
int? id,
String? url,
String? title,
String? description,
String? notes,
String? websiteTitle,
String? websiteDescription,
bool? isArchived,
bool? unread,
bool? shared,
List<String>? tagNames,
DateTime? dateAdded,
DateTime? dateModified,
}) =>
Bookmark(
id: id ?? this.id,
url: url ?? this.url,
title: title ?? this.title,
description: description ?? this.description,
notes: notes ?? this.notes,
websiteTitle: websiteTitle ?? this.websiteTitle,
websiteDescription: websiteDescription ?? this.websiteDescription,
isArchived: isArchived ?? this.isArchived,
unread: unread ?? this.unread,
shared: shared ?? this.shared,
tagNames: tagNames ?? this.tagNames,
dateAdded: dateAdded ?? this.dateAdded,
dateModified: dateModified ?? this.dateModified,
);
} }

View File

@@ -5,6 +5,8 @@ class RoutesPaths {
static const webview = "/webview"; static const webview = "/webview";
static const tags = "/tags"; static const tags = "/tags";
static const tagBookmarks = "/tags/:id"; static const tagBookmarks = "/tags/:id";
static const sharedBookmarks = "/bookmarks/shared";
static const archivedBookmarks = "/bookmarks/archived";
static const settings = "/settings"; static const settings = "/settings";
static const customization = "/settings/customization"; static const customization = "/settings/customization";
static const generalSettings = "/settings/general"; static const generalSettings = "/settings/general";

View File

@@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:linkdy/models/data/tags.dart';
import 'package:linkdy/router/names.dart'; import 'package:linkdy/router/names.dart';
import 'package:linkdy/screens/bookmarks/ui/bookmarks.dart'; import 'package:linkdy/screens/bookmarks/ui/bookmarks.dart';
@@ -8,12 +7,14 @@ import 'package:linkdy/screens/onboarding/ui/onboarding.dart';
import 'package:linkdy/screens/settings/ui/customization/customization.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/general_settings/general_settings.dart';
import 'package:linkdy/screens/settings/ui/settings.dart'; import 'package:linkdy/screens/settings/ui/settings.dart';
import 'package:linkdy/screens/tag_bookmarks/ui/tag_bookmarks.dart'; import 'package:linkdy/screens/filtered_bookmarks/ui/filtered_bookmarks.dart';
import 'package:linkdy/screens/tags/ui/tags.dart'; import 'package:linkdy/screens/tags/ui/tags.dart';
import 'package:linkdy/screens/webview/ui/webview.dart'; import 'package:linkdy/screens/webview/ui/webview.dart';
import 'package:linkdy/widgets/layout.dart'; import 'package:linkdy/widgets/layout.dart';
import 'package:linkdy/widgets/page_transition.dart'; import 'package:linkdy/widgets/page_transition.dart';
import 'package:linkdy/constants/enums.dart';
import 'package:linkdy/models/data/tags.dart';
import 'package:linkdy/models/data/bookmarks.dart'; import 'package:linkdy/models/data/bookmarks.dart';
import 'package:linkdy/router/paths.dart'; import 'package:linkdy/router/paths.dart';
@@ -50,7 +51,19 @@ final List<RouteBase> appRoutes = [
GoRoute( GoRoute(
path: RoutesPaths.tagBookmarks, path: RoutesPaths.tagBookmarks,
name: RoutesNames.tagBookmarks, name: RoutesNames.tagBookmarks,
builder: (context, state) => TagBookmarksScreen(tagId: state.pathParameters['id'], tag: state.extra as Tag?), builder: (context, state) => FilteredBookmarksScreen(
filteredBookmarksMode: FilteredBookmarksMode.tag,
tagId: state.pathParameters['id'],
tag: state.extra as Tag?,
),
),
GoRoute(
path: RoutesPaths.sharedBookmarks,
builder: (context, state) => const FilteredBookmarksScreen(filteredBookmarksMode: FilteredBookmarksMode.shared),
),
GoRoute(
path: RoutesPaths.archivedBookmarks,
builder: (context, state) => const FilteredBookmarksScreen(filteredBookmarksMode: FilteredBookmarksMode.archived),
), ),
GoRoute( GoRoute(
path: RoutesPaths.customization, path: RoutesPaths.customization,

View File

@@ -145,7 +145,7 @@ class BookmarkCommonFunctions {
if (result.successful == true) { if (result.successful == true) {
showSnackbar( showSnackbar(
key: scaffoldMessengerKey, key: scaffoldMessengerKey,
label: bookmark.isArchived == true label: bookmark.shared == true
? t.bookmarks.shareOptions.bookmarkUnsharedSuccessfully ? t.bookmarks.shareOptions.bookmarkUnsharedSuccessfully
: t.bookmarks.shareOptions.bookmarkSharedSuccessfully, : t.bookmarks.shareOptions.bookmarkSharedSuccessfully,
color: Colors.green, color: Colors.green,
@@ -154,7 +154,7 @@ class BookmarkCommonFunctions {
} else { } else {
showSnackbar( showSnackbar(
key: scaffoldMessengerKey, key: scaffoldMessengerKey,
label: bookmark.isArchived == true label: bookmark.shared == true
? t.bookmarks.shareOptions.bookmarkNotUnshared ? t.bookmarks.shareOptions.bookmarkNotUnshared
: t.bookmarks.shareOptions.bookmarkNotShared, : t.bookmarks.shareOptions.bookmarkNotShared,
color: Colors.red, color: Colors.red,

View File

@@ -63,8 +63,10 @@ class BookmarkItem extends ConsumerWidget {
SlidableAction( SlidableAction(
onPressed: (ctx) => onArchiveUnarchive(bookmark), onPressed: (ctx) => onArchiveUnarchive(bookmark),
backgroundColor: Colors.grey, backgroundColor: Colors.grey,
label: t.bookmarks.bookmarkOptions.archive, label: bookmark.isArchived == true
icon: Icons.archive_rounded, ? t.bookmarks.bookmarkOptions.unarchive
: t.bookmarks.bookmarkOptions.archive,
icon: bookmark.isArchived == true ? Icons.unarchive_rounded : Icons.archive_rounded,
padding: const EdgeInsets.all(4), padding: const EdgeInsets.all(4),
), ),
SlidableAction( SlidableAction(
@@ -73,7 +75,7 @@ class BookmarkItem extends ConsumerWidget {
builder: (ctx) => ShareOptionsModal(bookmark: bookmark, onShareInternally: onShareInternally), builder: (ctx) => ShareOptionsModal(bookmark: bookmark, onShareInternally: onShareInternally),
), ),
backgroundColor: Colors.orange, backgroundColor: Colors.orange,
label: t.bookmarks.bookmarkOptions.share, label: t.bookmarks.bookmarkOptions.shareOptions,
icon: Icons.share_rounded, icon: Icons.share_rounded,
padding: const EdgeInsets.all(4), padding: const EdgeInsets.all(4),
), ),

View File

@@ -12,6 +12,8 @@ import 'package:linkdy/widgets/error_screen.dart';
import 'package:linkdy/widgets/no_data_screen.dart'; import 'package:linkdy/widgets/no_data_screen.dart';
import 'package:linkdy/constants/enums.dart'; import 'package:linkdy/constants/enums.dart';
import 'package:linkdy/providers/router.provider.dart';
import 'package:linkdy/router/paths.dart';
import 'package:linkdy/constants/global_keys.dart'; import 'package:linkdy/constants/global_keys.dart';
import 'package:linkdy/i18n/strings.g.dart'; import 'package:linkdy/i18n/strings.g.dart';
@@ -68,6 +70,30 @@ class BookmarksScreen extends ConsumerWidget {
icon: const Icon(Icons.search_rounded), icon: const Icon(Icons.search_rounded),
tooltip: t.bookmarks.search.searchBookmarks, tooltip: t.bookmarks.search.searchBookmarks,
), ),
PopupMenuButton(
itemBuilder: (context) => [
PopupMenuItem(
onTap: () => ref.read(routerProvider).push(RoutesPaths.archivedBookmarks),
child: Row(
children: [
const Icon(Icons.archive_rounded),
const SizedBox(width: 16),
Text(t.bookmarks.archived),
],
),
),
PopupMenuItem(
onTap: () => ref.read(routerProvider).push(RoutesPaths.sharedBookmarks),
child: Row(
children: [
const Icon(Icons.share_rounded),
const SizedBox(width: 16),
Text(t.bookmarks.shared),
],
),
),
],
),
const SizedBox(width: 8), const SizedBox(width: 8),
], ],
), ),

View File

@@ -3,7 +3,8 @@ import 'package:linkdy/constants/enums.dart';
import 'package:linkdy/models/data/bookmarks.dart'; import 'package:linkdy/models/data/bookmarks.dart';
import 'package:linkdy/models/data/tags.dart'; import 'package:linkdy/models/data/tags.dart';
class TagBookmarksModel { class FilteredBookmarksModel {
FilteredBookmarksMode filteredBookmarksMode;
String? tagId; String? tagId;
Tag? tag; Tag? tag;
int currentPage; int currentPage;
@@ -13,7 +14,8 @@ class TagBookmarksModel {
int maxNumber; int maxNumber;
bool loadingMore; bool loadingMore;
TagBookmarksModel({ FilteredBookmarksModel({
this.filteredBookmarksMode = FilteredBookmarksMode.shared,
this.tagId, this.tagId,
this.tag, this.tag,
this.currentPage = 0, this.currentPage = 0,

View File

@@ -0,0 +1,220 @@
import 'package:linkdy/constants/global_keys.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:linkdy/screens/bookmarks/provider/favicon_loader.provider.dart';
import 'package:linkdy/screens/bookmarks/provider/common_functions.dart';
import 'package:linkdy/screens/filtered_bookmarks/model/filtered_bookmarks.model.dart';
import 'package:linkdy/models/data/bookmarks.dart';
import 'package:linkdy/constants/enums.dart';
import 'package:linkdy/models/data/tags.dart';
import 'package:linkdy/providers/api_client.provider.dart';
part 'filtered_bookmarks.provider.g.dart';
@riverpod
FutureOr<void> tagBookmarksRequest(TagBookmarksRequestRef ref, Tag? tag, String? tagId, int limit) async {
final tagResult = tag == null ? await ref.read(apiClientProvider)!.fetchTagById(tagId!) : null;
if (tagResult != null && tagResult.successful == false) {
ref.read(filteredBookmarksProvider.notifier).setInitialLoadStatus(LoadStatus.error);
return;
}
final bookmarksResult = await ref.read(apiClientProvider)!.fetchBookmarks(
q: tag != null ? tag.name : tagResult!.content!.name,
limit: limit,
offset: 0,
);
if (bookmarksResult.successful == true) {
ref.read(faviconStoreProvider.notifier).loadFavicons(bookmarksResult.content!.results!);
ref.read(filteredBookmarksProvider).bookmarks = bookmarksResult.content!.results!;
ref.read(filteredBookmarksProvider).maxNumber = bookmarksResult.content!.count!;
if (tag == null) ref.read(filteredBookmarksProvider).tag = tagResult!.content!;
ref.read(filteredBookmarksProvider).currentPage = 0;
ref.read(filteredBookmarksProvider).loadingMore = false;
ref.read(filteredBookmarksProvider).initialLoadStatus = LoadStatus.loaded;
} else {
ref.read(filteredBookmarksProvider).initialLoadStatus = LoadStatus.error;
}
ref.read(filteredBookmarksProvider.notifier).notifyListeners();
}
@riverpod
FutureOr<void> tagBookmarksRequestLoadMore(FilteredBookmarksRequestLoadMoreRef ref) async {
final provider = ref.read(filteredBookmarksProvider);
final newOffset = provider.limit * (provider.currentPage + 1);
final result = await ref.read(apiClientProvider)!.fetchBookmarks(
q: provider.tag!.name,
limit: provider.limit,
offset: newOffset,
);
if (result.successful == true) {
ref.read(faviconStoreProvider.notifier).loadFavicons(result.content!.results!);
provider.bookmarks = [...provider.bookmarks, ...result.content!.results!];
provider.maxNumber = result.content!.count!;
provider.currentPage = provider.currentPage + 1;
}
ref.read(filteredBookmarksProvider.notifier).setLoadingMore(false);
}
@riverpod
FutureOr<void> filteredBookmarksRequest(FilteredBookmarksRequestRef ref, FilteredBookmarksMode mode, int limit) async {
final bookmarksResult = mode == FilteredBookmarksMode.shared
? await ref.read(apiClientProvider)!.fetchSharedBookmarks(
limit: limit,
offset: 0,
)
: await ref.read(apiClientProvider)!.fetchArchivedBookmarks(
limit: limit,
offset: 0,
);
if (bookmarksResult.successful == true) {
ref.read(faviconStoreProvider.notifier).loadFavicons(bookmarksResult.content!.results!);
ref.read(filteredBookmarksProvider).bookmarks = bookmarksResult.content!.results!;
ref.read(filteredBookmarksProvider).maxNumber = bookmarksResult.content!.count!;
ref.read(filteredBookmarksProvider).currentPage = 0;
ref.read(filteredBookmarksProvider).loadingMore = false;
ref.read(filteredBookmarksProvider).initialLoadStatus = LoadStatus.loaded;
} else {
ref.read(filteredBookmarksProvider).initialLoadStatus = LoadStatus.error;
}
ref.read(filteredBookmarksProvider.notifier).notifyListeners();
}
@riverpod
FutureOr<void> filteredBookmarksRequestLoadMore(TagBookmarksRequestLoadMoreRef ref) async {
final provider = ref.read(filteredBookmarksProvider);
final newOffset = provider.limit * (provider.currentPage + 1);
final result = ref.read(filteredBookmarksProvider).filteredBookmarksMode == FilteredBookmarksMode.shared
? await ref.read(apiClientProvider)!.fetchSharedBookmarks(
q: provider.tag!.name,
limit: provider.limit,
offset: newOffset,
)
: await ref.read(apiClientProvider)!.fetchArchivedBookmarks(
q: provider.tag!.name,
limit: provider.limit,
offset: newOffset,
);
if (result.successful == true) {
ref.read(faviconStoreProvider.notifier).loadFavicons(result.content!.results!);
provider.bookmarks = [...provider.bookmarks, ...result.content!.results!];
provider.maxNumber = result.content!.count!;
provider.currentPage = provider.currentPage + 1;
}
ref.read(filteredBookmarksProvider.notifier).setLoadingMore(false);
}
@riverpod
class FilteredBookmarks extends _$FilteredBookmarks {
@override
FilteredBookmarksModel build() {
return FilteredBookmarksModel(
bookmarks: [],
);
}
void setMode(FilteredBookmarksMode mode) {
state.filteredBookmarksMode = mode;
}
void setCurrentPage(int page) {
state.currentPage = page;
}
void setLimit(int limit) {
state.limit = limit;
}
void setInitialLoadStatus(LoadStatus status) {
state.initialLoadStatus = status;
ref.notifyListeners();
}
void setLoadingMore(bool status) {
state.loadingMore = status;
ref.notifyListeners();
}
Future<void> refresh() async {
if (state.filteredBookmarksMode == FilteredBookmarksMode.tag) {
await ref.read(
tagBookmarksRequestProvider(state.tag, state.tagId, state.limit).future,
);
} else {
await ref.read(
filteredBookmarksRequestProvider(state.filteredBookmarksMode, state.limit).future,
);
}
}
void notifyListeners() {
ref.notifyListeners();
}
void deleteBookmark(Bookmark bookmark) async {
final result = await BookmarkCommonFunctions.deleteBookmark<FilteredBookmarksModel>(
scaffoldMessengerKey: ScaffoldMessengerKeys.filteredBookmarks,
ref: ref,
bookmark: bookmark,
apiClient: ref.read(apiClientProvider)!,
);
if (result == true) {
state.bookmarks = state.bookmarks.where((b) => b.id != bookmark.id).toList();
ref.notifyListeners();
}
}
void markAsReadUnread(Bookmark bookmark) async {
final result = await BookmarkCommonFunctions.markAsReadUnread<FilteredBookmarksModel>(
scaffoldMessengerKey: ScaffoldMessengerKeys.filteredBookmarks,
ref: ref,
bookmark: bookmark,
apiClient: ref.read(apiClientProvider)!,
);
if (result != null) {
state.bookmarks = state.bookmarks.map((b) => b.id == result.id ? result : b).toList();
ref.notifyListeners();
}
}
void archiveUnarchive(Bookmark bookmark) async {
final result = await BookmarkCommonFunctions.archiveUnarchive<FilteredBookmarksModel>(
scaffoldMessengerKey: ScaffoldMessengerKeys.filteredBookmarks,
ref: ref,
bookmark: bookmark,
apiClient: ref.read(apiClientProvider)!,
);
if (result == true) {
state.bookmarks = state.bookmarks.where((b) => b.id != bookmark.id).toList();
}
ref.notifyListeners();
}
void shareUnshare(Bookmark bookmark) async {
final result = await BookmarkCommonFunctions.shareUnshare<FilteredBookmarksModel>(
scaffoldMessengerKey: ScaffoldMessengerKeys.filteredBookmarks,
ref: ref,
bookmark: bookmark,
apiClient: ref.read(apiClientProvider)!,
);
if (result != null && state.filteredBookmarksMode == FilteredBookmarksMode.shared) {
state.bookmarks = state.bookmarks.where((b) => b.id != result.id).toList();
} else if (result != null && state.filteredBookmarksMode != FilteredBookmarksMode.shared) {
state.bookmarks = state.bookmarks.map((b) => b.id == result.id ? result : b).toList();
}
ref.notifyListeners();
}
}

View File

@@ -1,13 +1,13 @@
// GENERATED CODE - DO NOT MODIFY BY HAND // GENERATED CODE - DO NOT MODIFY BY HAND
part of 'tag_bookmarks.provider.dart'; part of 'filtered_bookmarks.provider.dart';
// ************************************************************************** // **************************************************************************
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$tagBookmarksRequestHash() => String _$tagBookmarksRequestHash() =>
r'9a4d4582b680da41069054429315f85976378524'; r'abcde0e4ef3aecf21708a24573ddd4279211e288';
/// Copied from Dart SDK /// Copied from Dart SDK
class _SystemHash { class _SystemHash {
@@ -190,7 +190,7 @@ class _TagBookmarksRequestProviderElement
} }
String _$tagBookmarksRequestLoadMoreHash() => String _$tagBookmarksRequestLoadMoreHash() =>
r'ce7c52c446c2d4b8593fd06e818b3499bdc36a39'; r'36205474b3c1d3f18f31af56cc97c700f328a4bc';
/// See also [tagBookmarksRequestLoadMore]. /// See also [tagBookmarksRequestLoadMore].
@ProviderFor(tagBookmarksRequestLoadMore) @ProviderFor(tagBookmarksRequestLoadMore)
@@ -206,20 +206,187 @@ final tagBookmarksRequestLoadMoreProvider =
); );
typedef TagBookmarksRequestLoadMoreRef = AutoDisposeFutureProviderRef<void>; typedef TagBookmarksRequestLoadMoreRef = AutoDisposeFutureProviderRef<void>;
String _$tagBookmarksHash() => r'35a9ed7751c2d7e9644907f969fb03950b7b9a04'; String _$filteredBookmarksRequestHash() =>
r'f5352c5076d8bf2ed0cf738bdc0c0cffc9d18a25';
/// See also [TagBookmarks]. /// See also [filteredBookmarksRequest].
@ProviderFor(TagBookmarks) @ProviderFor(filteredBookmarksRequest)
final tagBookmarksProvider = const filteredBookmarksRequestProvider = FilteredBookmarksRequestFamily();
AutoDisposeNotifierProvider<TagBookmarks, TagBookmarksModel>.internal(
TagBookmarks.new, /// See also [filteredBookmarksRequest].
name: r'tagBookmarksProvider', class FilteredBookmarksRequestFamily extends Family<AsyncValue<void>> {
/// See also [filteredBookmarksRequest].
const FilteredBookmarksRequestFamily();
/// See also [filteredBookmarksRequest].
FilteredBookmarksRequestProvider call(
FilteredBookmarksMode mode,
int limit,
) {
return FilteredBookmarksRequestProvider(
mode,
limit,
);
}
@override
FilteredBookmarksRequestProvider getProviderOverride(
covariant FilteredBookmarksRequestProvider provider,
) {
return call(
provider.mode,
provider.limit,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'filteredBookmarksRequestProvider';
}
/// See also [filteredBookmarksRequest].
class FilteredBookmarksRequestProvider extends AutoDisposeFutureProvider<void> {
/// See also [filteredBookmarksRequest].
FilteredBookmarksRequestProvider(
FilteredBookmarksMode mode,
int limit,
) : this._internal(
(ref) => filteredBookmarksRequest(
ref as FilteredBookmarksRequestRef,
mode,
limit,
),
from: filteredBookmarksRequestProvider,
name: r'filteredBookmarksRequestProvider',
debugGetCreateSourceHash: debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$tagBookmarksHash, const bool.fromEnvironment('dart.vm.product')
? null
: _$filteredBookmarksRequestHash,
dependencies: FilteredBookmarksRequestFamily._dependencies,
allTransitiveDependencies:
FilteredBookmarksRequestFamily._allTransitiveDependencies,
mode: mode,
limit: limit,
);
FilteredBookmarksRequestProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.mode,
required this.limit,
}) : super.internal();
final FilteredBookmarksMode mode;
final int limit;
@override
Override overrideWith(
FutureOr<void> Function(FilteredBookmarksRequestRef provider) create,
) {
return ProviderOverride(
origin: this,
override: FilteredBookmarksRequestProvider._internal(
(ref) => create(ref as FilteredBookmarksRequestRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
mode: mode,
limit: limit,
),
);
}
@override
AutoDisposeFutureProviderElement<void> createElement() {
return _FilteredBookmarksRequestProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is FilteredBookmarksRequestProvider &&
other.mode == mode &&
other.limit == limit;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, mode.hashCode);
hash = _SystemHash.combine(hash, limit.hashCode);
return _SystemHash.finish(hash);
}
}
mixin FilteredBookmarksRequestRef on AutoDisposeFutureProviderRef<void> {
/// The parameter `mode` of this provider.
FilteredBookmarksMode get mode;
/// The parameter `limit` of this provider.
int get limit;
}
class _FilteredBookmarksRequestProviderElement
extends AutoDisposeFutureProviderElement<void>
with FilteredBookmarksRequestRef {
_FilteredBookmarksRequestProviderElement(super.provider);
@override
FilteredBookmarksMode get mode =>
(origin as FilteredBookmarksRequestProvider).mode;
@override
int get limit => (origin as FilteredBookmarksRequestProvider).limit;
}
String _$filteredBookmarksRequestLoadMoreHash() =>
r'a40280a01fcfa3892c76287a5e67e5ab9cc00cd5';
/// See also [filteredBookmarksRequestLoadMore].
@ProviderFor(filteredBookmarksRequestLoadMore)
final filteredBookmarksRequestLoadMoreProvider =
AutoDisposeFutureProvider<void>.internal(
filteredBookmarksRequestLoadMore,
name: r'filteredBookmarksRequestLoadMoreProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$filteredBookmarksRequestLoadMoreHash,
dependencies: null, dependencies: null,
allTransitiveDependencies: null, allTransitiveDependencies: null,
); );
typedef _$TagBookmarks = AutoDisposeNotifier<TagBookmarksModel>; typedef FilteredBookmarksRequestLoadMoreRef
= AutoDisposeFutureProviderRef<void>;
String _$filteredBookmarksHash() => r'acb051c3b3e53c084c8c33d6ec1e80a1aa2a10f7';
/// See also [FilteredBookmarks].
@ProviderFor(FilteredBookmarks)
final filteredBookmarksProvider = AutoDisposeNotifierProvider<FilteredBookmarks,
FilteredBookmarksModel>.internal(
FilteredBookmarks.new,
name: r'filteredBookmarksProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$filteredBookmarksHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$FilteredBookmarks = AutoDisposeNotifier<FilteredBookmarksModel>;
// ignore_for_file: type=lint // ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View File

@@ -5,7 +5,7 @@ import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:linkdy/screens/bookmarks/ui/bookmark_form_modal.dart'; import 'package:linkdy/screens/bookmarks/ui/bookmark_form_modal.dart';
import 'package:linkdy/screens/bookmarks/ui/bookmark_item.dart'; import 'package:linkdy/screens/bookmarks/ui/bookmark_item.dart';
import 'package:linkdy/screens/bookmarks/ui/delete_bookmark_modal.dart'; import 'package:linkdy/screens/bookmarks/ui/delete_bookmark_modal.dart';
import 'package:linkdy/screens/tag_bookmarks/provider/tag_bookmarks.provider.dart'; import 'package:linkdy/screens/filtered_bookmarks/provider/filtered_bookmarks.provider.dart';
import 'package:linkdy/widgets/error_screen.dart'; import 'package:linkdy/widgets/error_screen.dart';
import 'package:linkdy/widgets/no_data_screen.dart'; import 'package:linkdy/widgets/no_data_screen.dart';
@@ -16,23 +16,27 @@ import 'package:linkdy/providers/router.provider.dart';
import 'package:linkdy/router/paths.dart'; import 'package:linkdy/router/paths.dart';
import 'package:linkdy/i18n/strings.g.dart'; import 'package:linkdy/i18n/strings.g.dart';
class TagBookmarksScreen extends ConsumerStatefulWidget { class FilteredBookmarksScreen extends ConsumerStatefulWidget {
final FilteredBookmarksMode filteredBookmarksMode;
final String? tagId; final String? tagId;
final Tag? tag; final Tag? tag;
const TagBookmarksScreen({ const FilteredBookmarksScreen({
super.key, super.key,
required this.tagId, required this.filteredBookmarksMode,
this.tagId,
this.tag, this.tag,
}); });
@override @override
TagBookmarksScreenState createState() => TagBookmarksScreenState(); FilteredBookmarksScreenState createState() => FilteredBookmarksScreenState();
} }
class TagBookmarksScreenState extends ConsumerState<TagBookmarksScreen> { class FilteredBookmarksScreenState extends ConsumerState<FilteredBookmarksScreen> {
@override @override
void initState() { void initState() {
ref.read(filteredBookmarksProvider.notifier).setMode(widget.filteredBookmarksMode);
if (widget.filteredBookmarksMode == FilteredBookmarksMode.tag) {
if (widget.tag == null && widget.tagId == null) { if (widget.tag == null && widget.tagId == null) {
final router = ref.read(routerProvider); final router = ref.read(routerProvider);
while (router.canPop() == true) { while (router.canPop() == true) {
@@ -42,21 +46,28 @@ class TagBookmarksScreenState extends ConsumerState<TagBookmarksScreen> {
return; return;
} }
if (widget.tag != null) { if (widget.tag != null) {
ref.read(tagBookmarksProvider).tag = widget.tag; ref.read(filteredBookmarksProvider).tag = widget.tag;
} }
if (widget.tagId != null) { if (widget.tagId != null) {
ref.read(tagBookmarksProvider).tagId = widget.tagId; ref.read(filteredBookmarksProvider).tagId = widget.tagId;
}
ref.read(tagBookmarksRequestProvider(widget.tag, widget.tagId, ref.read(filteredBookmarksProvider).limit));
} else {
ref.read(
filteredBookmarksRequestProvider(widget.filteredBookmarksMode, ref.read(filteredBookmarksProvider).limit),
);
} }
ref.read(tagBookmarksRequestProvider(widget.tag, widget.tagId, ref.read(tagBookmarksProvider).limit));
super.initState(); super.initState();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (widget.tag == null && widget.tagId == null) return const Material(); if (widget.filteredBookmarksMode == FilteredBookmarksMode.tag && widget.tag == null && widget.tagId == null) {
return const Material();
}
final provider = ref.watch(tagBookmarksProvider); final provider = ref.watch(filteredBookmarksProvider);
final width = MediaQuery.of(context).size.width; final width = MediaQuery.of(context).size.width;
@@ -64,14 +75,50 @@ class TagBookmarksScreenState extends ConsumerState<TagBookmarksScreen> {
if (scrollNotification.metrics.extentAfter < 100 && if (scrollNotification.metrics.extentAfter < 100 &&
provider.loadingMore == false && provider.loadingMore == false &&
provider.bookmarks.length < provider.maxNumber) { provider.bookmarks.length < provider.maxNumber) {
ref.read(tagBookmarksProvider.notifier).setLoadingMore(true); ref.read(filteredBookmarksProvider.notifier).setLoadingMore(true);
ref.read(tagBookmarksRequestLoadMoreProvider); ref.read(tagBookmarksRequestLoadMoreProvider);
} }
return false; return false;
} }
String title() {
switch (widget.filteredBookmarksMode) {
case FilteredBookmarksMode.tag:
return widget.tag != null
? "#${widget.tag!.name}"
: provider.tag != null
? "#${provider.tag!.name}"
: '';
case FilteredBookmarksMode.archived:
return t.bookmarks.archived;
case FilteredBookmarksMode.shared:
return t.bookmarks.shared;
default:
return "";
}
}
String noContent() {
switch (widget.filteredBookmarksMode) {
case FilteredBookmarksMode.tag:
return t.tags.filteredBookmarks.noBookmarksWithThisTag;
case FilteredBookmarksMode.archived:
return t.tags.filteredBookmarks.noArchivedBookmarks;
case FilteredBookmarksMode.shared:
return t.tags.filteredBookmarks.noSharedBookmarks;
default:
return "";
}
}
return ScaffoldMessenger( return ScaffoldMessenger(
key: ScaffoldMessengerKeys.tagBookmarks, key: ScaffoldMessengerKeys.filteredBookmarks,
child: Scaffold( child: Scaffold(
body: NestedScrollView( body: NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) => [ headerSliverBuilder: (context, innerBoxIsScrolled) => [
@@ -82,13 +129,7 @@ class TagBookmarksScreenState extends ConsumerState<TagBookmarksScreen> {
floating: true, floating: true,
centerTitle: false, centerTitle: false,
forceElevated: innerBoxIsScrolled, forceElevated: innerBoxIsScrolled,
title: Text( title: Text(title()),
widget.tag != null
? "#${widget.tag!.name}"
: provider.tag != null
? "#${provider.tag!.name}"
: '',
),
), ),
), ),
], ],
@@ -97,7 +138,7 @@ class TagBookmarksScreenState extends ConsumerState<TagBookmarksScreen> {
child: Builder( child: Builder(
builder: (context) => RefreshIndicator( builder: (context) => RefreshIndicator(
displacement: 120, displacement: 120,
onRefresh: () => ref.read(tagBookmarksProvider.notifier).refresh(), onRefresh: () => ref.read(filteredBookmarksProvider.notifier).refresh(),
child: NotificationListener( child: NotificationListener(
onNotification: scrollListener, onNotification: scrollListener,
child: CustomScrollView( child: CustomScrollView(
@@ -118,7 +159,7 @@ class TagBookmarksScreenState extends ConsumerState<TagBookmarksScreen> {
if (provider.bookmarks.isEmpty) if (provider.bookmarks.isEmpty)
SliverFillRemaining( SliverFillRemaining(
child: NoDataScreen( child: NoDataScreen(
message: t.tags.tagBookmarks.noBookmarksWithThisTag, message: noContent(),
), ),
), ),
if (provider.bookmarks.isNotEmpty) if (provider.bookmarks.isNotEmpty)
@@ -138,16 +179,16 @@ class TagBookmarksScreenState extends ConsumerState<TagBookmarksScreen> {
} }
return BookmarkItem( return BookmarkItem(
bookmark: provider.bookmarks[index], bookmark: provider.bookmarks[index],
onReadUnread: ref.read(tagBookmarksProvider.notifier).markAsReadUnread, onReadUnread: ref.read(filteredBookmarksProvider.notifier).markAsReadUnread,
onDelete: (bookmark) => showDialog( onDelete: (bookmark) => showDialog(
context: context, context: context,
builder: (context) => DeleteBookmarkModal( builder: (context) => DeleteBookmarkModal(
bookmark: bookmark, bookmark: bookmark,
onDelete: ref.read(tagBookmarksProvider.notifier).deleteBookmark, onDelete: ref.read(filteredBookmarksProvider.notifier).deleteBookmark,
), ),
), ),
onArchiveUnarchive: ref.read(tagBookmarksProvider.notifier).archiveUnarchive, onArchiveUnarchive: ref.read(filteredBookmarksProvider.notifier).archiveUnarchive,
onShareInternally: ref.read(tagBookmarksProvider.notifier).shareUnshare, onShareInternally: ref.read(filteredBookmarksProvider.notifier).shareUnshare,
onEdit: (b) => openBookmarkFormModal(context: context, width: width, bookmark: b), onEdit: (b) => openBookmarkFormModal(context: context, width: width, bookmark: b),
); );
}, },

View File

@@ -1,153 +0,0 @@
import 'package:linkdy/constants/global_keys.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:linkdy/screens/bookmarks/provider/favicon_loader.provider.dart';
import 'package:linkdy/screens/bookmarks/provider/common_functions.dart';
import 'package:linkdy/screens/tag_bookmarks/model/tag_bookmarks.model.dart';
import 'package:linkdy/models/data/bookmarks.dart';
import 'package:linkdy/constants/enums.dart';
import 'package:linkdy/models/data/tags.dart';
import 'package:linkdy/providers/api_client.provider.dart';
part 'tag_bookmarks.provider.g.dart';
@riverpod
FutureOr<void> tagBookmarksRequest(TagBookmarksRequestRef ref, Tag? tag, String? tagId, int limit) async {
final tagResult = tag == null ? await ref.read(apiClientProvider)!.fetchTagById(tagId!) : null;
if (tagResult != null && tagResult.successful == false) {
ref.read(tagBookmarksProvider.notifier).setInitialLoadStatus(LoadStatus.error);
return;
}
final bookmarksResult = await ref.read(apiClientProvider)!.fetchBookmarks(
q: tag != null ? tag.name : tagResult!.content!.name,
limit: limit,
offset: 0,
);
if (bookmarksResult.successful == true) {
ref.read(faviconStoreProvider.notifier).loadFavicons(bookmarksResult.content!.results!);
ref.read(tagBookmarksProvider).bookmarks = bookmarksResult.content!.results!;
ref.read(tagBookmarksProvider).maxNumber = bookmarksResult.content!.count!;
if (tag == null) ref.read(tagBookmarksProvider).tag = tagResult!.content!;
ref.read(tagBookmarksProvider).currentPage = 0;
ref.read(tagBookmarksProvider).loadingMore = false;
ref.read(tagBookmarksProvider).initialLoadStatus = LoadStatus.loaded;
} else {
ref.read(tagBookmarksProvider).initialLoadStatus = LoadStatus.error;
}
ref.read(tagBookmarksProvider.notifier).notifyListeners();
}
@riverpod
FutureOr<void> tagBookmarksRequestLoadMore(TagBookmarksRequestLoadMoreRef ref) async {
final provider = ref.read(tagBookmarksProvider);
final newOffset = provider.limit * (provider.currentPage + 1);
final result = await ref.read(apiClientProvider)!.fetchBookmarks(
q: provider.tag!.name,
limit: provider.limit,
offset: newOffset,
);
if (result.successful == true) {
ref.read(faviconStoreProvider.notifier).loadFavicons(result.content!.results!);
provider.bookmarks = [...provider.bookmarks, ...result.content!.results!];
provider.maxNumber = result.content!.count!;
provider.currentPage = provider.currentPage + 1;
}
ref.read(tagBookmarksProvider.notifier).setLoadingMore(false);
}
@riverpod
class TagBookmarks extends _$TagBookmarks {
@override
TagBookmarksModel build() {
return TagBookmarksModel(
bookmarks: [],
);
}
void setCurrentPage(int page) {
state.currentPage = page;
}
void setLimit(int limit) {
state.limit = limit;
}
void setInitialLoadStatus(LoadStatus status) {
state.initialLoadStatus = status;
ref.notifyListeners();
}
void setLoadingMore(bool status) {
state.loadingMore = status;
ref.notifyListeners();
}
Future<void> refresh() async {
await ref.read(tagBookmarksRequestProvider(state.tag, state.tagId, state.limit).future);
}
void notifyListeners() {
ref.notifyListeners();
}
void deleteBookmark(Bookmark bookmark) async {
final result = await BookmarkCommonFunctions.deleteBookmark<TagBookmarksModel>(
scaffoldMessengerKey: ScaffoldMessengerKeys.tagBookmarks,
ref: ref,
bookmark: bookmark,
apiClient: ref.read(apiClientProvider)!,
);
if (result == true) {
state.bookmarks = state.bookmarks.where((b) => b.id != bookmark.id).toList();
ref.notifyListeners();
}
}
void markAsReadUnread(Bookmark bookmark) async {
final result = await BookmarkCommonFunctions.markAsReadUnread<TagBookmarksModel>(
scaffoldMessengerKey: ScaffoldMessengerKeys.tagBookmarks,
ref: ref,
bookmark: bookmark,
apiClient: ref.read(apiClientProvider)!,
);
if (result != null) {
state.bookmarks = state.bookmarks.map((b) => b.id == result.id ? result : b).toList();
ref.notifyListeners();
}
}
void archiveUnarchive(Bookmark bookmark) async {
final result = await BookmarkCommonFunctions.archiveUnarchive<TagBookmarksModel>(
scaffoldMessengerKey: ScaffoldMessengerKeys.tagBookmarks,
ref: ref,
bookmark: bookmark,
apiClient: ref.read(apiClientProvider)!,
);
if (result == true) {
// On this case the bookmark always will pass from unarchived to archived
state.bookmarks = state.bookmarks.where((b) => b.id != bookmark.id).toList();
ref.notifyListeners();
}
}
void shareUnshare(Bookmark bookmark) async {
final result = await BookmarkCommonFunctions.shareUnshare<TagBookmarksModel>(
scaffoldMessengerKey: ScaffoldMessengerKeys.tagBookmarks,
ref: ref,
bookmark: bookmark,
apiClient: ref.read(apiClientProvider)!,
);
if (result != null) {
state.bookmarks = state.bookmarks.map((b) => b.id == result.id ? result : b).toList();
ref.notifyListeners();
}
}
}

View File

@@ -69,6 +69,50 @@ class ApiClientService {
} }
} }
Future<ApiResponse<BookmarksResponse>> fetchArchivedBookmarks({String? q, int? limit, int? offset}) async {
try {
final response = await dioInstance.get(
"/bookmarks/archived/",
queryParameters: {
"q": q,
"limit": limit,
"offset": offset,
},
);
return ApiResponse(
successful: true,
content: BookmarksResponse.fromJson(response.data),
);
} on DioException {
return const ApiResponse(successful: false);
} catch (e, stackTrace) {
Sentry.captureException(e, stackTrace: stackTrace);
return const ApiResponse(successful: false);
}
}
Future<ApiResponse<BookmarksResponse>> fetchSharedBookmarks({String? q, int? limit, int? offset}) async {
try {
final response = await dioInstance.get(
"/bookmarks/shared/",
queryParameters: {
"q": q,
"limit": limit,
"offset": offset,
},
);
return ApiResponse(
successful: true,
content: BookmarksResponse.fromJson(response.data),
);
} on DioException {
return const ApiResponse(successful: false);
} catch (e, stackTrace) {
Sentry.captureException(e, stackTrace: stackTrace);
return const ApiResponse(successful: false);
}
}
Future<ApiResponse<CheckBookmark>> fetchCheckAddBookmark({required String url}) async { Future<ApiResponse<CheckBookmark>> fetchCheckAddBookmark({required String url}) async {
try { try {
final response = await dioInstance.get( final response = await dioInstance.get(

View File

@@ -20,7 +20,7 @@ class NoDataScreen extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Icon( Icon(
Icons.clear_rounded, Icons.clear_all_rounded,
size: 50, size: 50,
color: Theme.of(context).colorScheme.onSurfaceVariant, color: Theme.of(context).colorScheme.onSurfaceVariant,
), ),