Added edit bookmark

This commit is contained in:
Juan Gilsanz Polo
2024-02-27 15:44:07 +01:00
parent 8a968be6e0
commit 2c59310f94
14 changed files with 161 additions and 72 deletions

View File

@@ -4,9 +4,9 @@
/// To regenerate, run: `dart run slang` /// To regenerate, run: `dart run slang`
/// ///
/// Locales: 2 /// Locales: 2
/// Strings: 290 (145 per locale) /// Strings: 294 (147 per locale)
/// ///
/// Built on 2024-02-27 at 00:35 UTC /// Built on 2024-02-27 at 14:35 UTC
// coverage:ignore-file // coverage:ignore-file
// ignore_for_file: type=lint // ignore_for_file: type=lint
@@ -315,6 +315,7 @@ class _StringsBookmarksAddBookmarkEn {
// Translations // Translations
String get addBookmark => 'Add new bookmark'; String get addBookmark => 'Add new bookmark';
String get editBookmark => 'Edit bookmark';
String get bookmarkUrl => 'Bookmark URL'; String get bookmarkUrl => 'Bookmark URL';
String get bookmarkDetails => 'Bookmark details'; String get bookmarkDetails => 'Bookmark details';
String get validateUrl => 'Validate URL'; String get validateUrl => 'Validate URL';
@@ -335,6 +336,7 @@ class _StringsBookmarksAddBookmarkEn {
String get addTag => 'Add tag'; String get addTag => 'Add tag';
String get others => 'Others'; String get others => 'Others';
String get savingBookmark => 'Saving bookmark...'; String get savingBookmark => 'Saving bookmark...';
String get bookmarkSavedSuccessfully => 'Bookmark saved successfully.';
String get errorSavingBookmark => 'An error occured when saving the new bookmark.'; String get errorSavingBookmark => 'An error occured when saving the new bookmark.';
String get leaveEmptyUseWebsiteTitle => 'Leave empty to use the website\'s title.'; String get leaveEmptyUseWebsiteTitle => 'Leave empty to use the website\'s title.';
String get leaveEmptyUseWebsiteDescription => 'Leave empty to use the website\'s description.'; String get leaveEmptyUseWebsiteDescription => 'Leave empty to use the website\'s description.';
@@ -646,6 +648,7 @@ class _StringsBookmarksAddBookmarkEs implements _StringsBookmarksAddBookmarkEn {
// Translations // Translations
@override String get addBookmark => 'Añadir nuevo marcador'; @override String get addBookmark => 'Añadir nuevo marcador';
@override String get editBookmark => 'Editar marcador';
@override String get url => 'URL'; @override String get url => 'URL';
@override String get bookmarkUrl => 'URL del marcador'; @override String get bookmarkUrl => 'URL del marcador';
@override String get bookmarkDetails => 'Detalles del marcador'; @override String get bookmarkDetails => 'Detalles del marcador';
@@ -666,6 +669,7 @@ class _StringsBookmarksAddBookmarkEs implements _StringsBookmarksAddBookmarkEn {
@override String get addTag => 'Añadir etiqueta'; @override String get addTag => 'Añadir etiqueta';
@override String get others => 'Otros'; @override String get others => 'Otros';
@override String get savingBookmark => 'Guardando marcador...'; @override String get savingBookmark => 'Guardando marcador...';
@override String get bookmarkSavedSuccessfully => 'Marcador guardado correctamente';
@override String get errorSavingBookmark => 'Ocurrió un error al guardar el nuevo marcador.'; @override String get errorSavingBookmark => 'Ocurrió un error al guardar el nuevo marcador.';
@override String get leaveEmptyUseWebsiteTitle => 'Dejar vacío para usar el título del sitio web.'; @override String get leaveEmptyUseWebsiteTitle => 'Dejar vacío para usar el título del sitio web.';
@override String get leaveEmptyUseWebsiteDescription => 'Dejar vacío para usar la descripción del sitio web.'; @override String get leaveEmptyUseWebsiteDescription => 'Dejar vacío para usar la descripción del sitio web.';
@@ -836,6 +840,7 @@ extension on Translations {
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.addBookmark.addBookmark': return 'Add new bookmark'; case 'bookmarks.addBookmark.addBookmark': return 'Add new bookmark';
case 'bookmarks.addBookmark.editBookmark': return 'Edit bookmark';
case 'bookmarks.addBookmark.bookmarkUrl': return 'Bookmark URL'; case 'bookmarks.addBookmark.bookmarkUrl': return 'Bookmark URL';
case 'bookmarks.addBookmark.bookmarkDetails': return 'Bookmark details'; case 'bookmarks.addBookmark.bookmarkDetails': return 'Bookmark details';
case 'bookmarks.addBookmark.validateUrl': return 'Validate URL'; case 'bookmarks.addBookmark.validateUrl': return 'Validate URL';
@@ -856,6 +861,7 @@ extension on Translations {
case 'bookmarks.addBookmark.addTag': return 'Add tag'; case 'bookmarks.addBookmark.addTag': return 'Add tag';
case 'bookmarks.addBookmark.others': return 'Others'; case 'bookmarks.addBookmark.others': return 'Others';
case 'bookmarks.addBookmark.savingBookmark': return 'Saving bookmark...'; case 'bookmarks.addBookmark.savingBookmark': return 'Saving bookmark...';
case 'bookmarks.addBookmark.bookmarkSavedSuccessfully': return 'Bookmark saved successfully.';
case 'bookmarks.addBookmark.errorSavingBookmark': return 'An error occured when saving the new bookmark.'; 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.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.addBookmark.leaveEmptyUseWebsiteDescription': return 'Leave empty to use the website\'s description.';
@@ -989,6 +995,7 @@ extension on _StringsEs {
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.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.url': return 'URL'; case 'bookmarks.addBookmark.url': return 'URL';
case 'bookmarks.addBookmark.bookmarkUrl': return 'URL del marcador'; case 'bookmarks.addBookmark.bookmarkUrl': return 'URL del marcador';
case 'bookmarks.addBookmark.bookmarkDetails': return 'Detalles del marcador'; case 'bookmarks.addBookmark.bookmarkDetails': return 'Detalles del marcador';
@@ -1009,6 +1016,7 @@ extension on _StringsEs {
case 'bookmarks.addBookmark.addTag': return 'Añadir etiqueta'; case 'bookmarks.addBookmark.addTag': return 'Añadir etiqueta';
case 'bookmarks.addBookmark.others': return 'Otros'; case 'bookmarks.addBookmark.others': return 'Otros';
case 'bookmarks.addBookmark.savingBookmark': return 'Guardando marcador...'; case 'bookmarks.addBookmark.savingBookmark': return 'Guardando marcador...';
case 'bookmarks.addBookmark.bookmarkSavedSuccessfully': return 'Marcador guardado correctamente';
case 'bookmarks.addBookmark.errorSavingBookmark': return 'Ocurrió un error al guardar el nuevo marcador.'; 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.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.addBookmark.leaveEmptyUseWebsiteDescription': return 'Dejar vacío para usar la descripción del sitio web.';

View File

@@ -51,6 +51,7 @@
}, },
"addBookmark": { "addBookmark": {
"addBookmark": "Add new bookmark", "addBookmark": "Add new bookmark",
"editBookmark": "Edit bookmark",
"bookmarkUrl": "Bookmark URL", "bookmarkUrl": "Bookmark URL",
"bookmarkDetails": "Bookmark details", "bookmarkDetails": "Bookmark details",
"validateUrl": "Validate URL", "validateUrl": "Validate URL",
@@ -71,6 +72,7 @@
"addTag": "Add tag", "addTag": "Add tag",
"others": "Others", "others": "Others",
"savingBookmark": "Saving bookmark...", "savingBookmark": "Saving bookmark...",
"bookmarkSavedSuccessfully": "Bookmark saved successfully.",
"errorSavingBookmark": "An error occured when saving the new bookmark.", "errorSavingBookmark": "An error occured when saving the new bookmark.",
"leaveEmptyUseWebsiteTitle": "Leave empty to use the website's title.", "leaveEmptyUseWebsiteTitle": "Leave empty to use the website's title.",
"leaveEmptyUseWebsiteDescription": "Leave empty to use the website's description." "leaveEmptyUseWebsiteDescription": "Leave empty to use the website's description."

View File

@@ -51,6 +51,7 @@
}, },
"addBookmark": { "addBookmark": {
"addBookmark": "Añadir nuevo marcador", "addBookmark": "Añadir nuevo marcador",
"editBookmark": "Editar marcador",
"url": "URL", "url": "URL",
"bookmarkUrl": "URL del marcador", "bookmarkUrl": "URL del marcador",
"bookmarkDetails": "Detalles del marcador", "bookmarkDetails": "Detalles del marcador",
@@ -71,6 +72,7 @@
"addTag": "Añadir etiqueta", "addTag": "Añadir etiqueta",
"others": "Otros", "others": "Otros",
"savingBookmark": "Guardando marcador...", "savingBookmark": "Guardando marcador...",
"bookmarkSavedSuccessfully": "Marcador guardado correctamente",
"errorSavingBookmark": "Ocurrió un error al guardar el nuevo marcador.", "errorSavingBookmark": "Ocurrió un error al guardar el nuevo marcador.",
"leaveEmptyUseWebsiteTitle": "Dejar vacío para usar el título del sitio web.", "leaveEmptyUseWebsiteTitle": "Dejar vacío para usar el título del sitio web.",
"leaveEmptyUseWebsiteDescription": "Dejar vacío para usar la descripción del sitio web." "leaveEmptyUseWebsiteDescription": "Dejar vacío para usar la descripción del sitio web."

View File

@@ -1,5 +1,7 @@
import 'package:linkdy/models/data/bookmarks.dart';
class CheckBookmark { class CheckBookmark {
final dynamic bookmark; final Bookmark? bookmark;
final Metadata? metadata; final Metadata? metadata;
CheckBookmark({ CheckBookmark({
@@ -8,12 +10,12 @@ class CheckBookmark {
}); });
factory CheckBookmark.fromJson(Map<String, dynamic> json) => CheckBookmark( factory CheckBookmark.fromJson(Map<String, dynamic> json) => CheckBookmark(
bookmark: json["bookmark"], bookmark: json["bookmark"] != null ? Bookmark.fromJson(json["bookmark"]) : null,
metadata: json["metadata"] == null ? null : Metadata.fromJson(json["metadata"]), metadata: json["metadata"] == null ? null : Metadata.fromJson(json["metadata"]),
); );
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
"bookmark": bookmark, "bookmark": bookmark?.toJson(),
"metadata": metadata?.toJson(), "metadata": metadata?.toJson(),
}; };
} }

View File

@@ -6,6 +6,7 @@ class SetBookmarkData {
final bool unread; final bool unread;
final bool shared; final bool shared;
final String tagNames; final String tagNames;
final String notes;
const SetBookmarkData({ const SetBookmarkData({
required this.url, required this.url,
@@ -15,6 +16,7 @@ class SetBookmarkData {
required this.unread, required this.unread,
required this.shared, required this.shared,
required this.tagNames, required this.tagNames,
required this.notes,
}); });
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
@@ -25,6 +27,7 @@ class SetBookmarkData {
"is_archived": isArchived, "is_archived": isArchived,
"unread": unread, "unread": unread,
"shared": shared, "shared": shared,
"notes": notes,
}; };
if (tagNames != "") { if (tagNames != "") {
ret = { ret = {

View File

@@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:linkdy/constants/enums.dart'; import 'package:linkdy/constants/enums.dart';
import 'package:linkdy/models/data/check_bookmark.dart'; import 'package:linkdy/models/data/check_bookmark.dart';
class AddBookmarkModel { class BookmarkFormModel {
final TextEditingController urlController; final TextEditingController urlController;
String? urlError; String? urlError;
CheckBookmark? checkBookmark; CheckBookmark? checkBookmark;
@@ -15,8 +15,9 @@ class AddBookmarkModel {
String? tagsError; String? tagsError;
List<String> tags; List<String> tags;
final TextEditingController notesController; final TextEditingController notesController;
int? editBookmarkId;
AddBookmarkModel({ BookmarkFormModel({
required this.urlController, required this.urlController,
this.urlError, this.urlError,
this.checkBookmark, this.checkBookmark,
@@ -28,5 +29,6 @@ class AddBookmarkModel {
this.tagsError, this.tagsError,
required this.tags, required this.tags,
required this.notesController, required this.notesController,
this.editBookmarkId,
}); });
} }

View File

@@ -1,10 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:linkdy/constants/global_keys.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:linkdy/screens/bookmarks/provider/bookmarks.provider.dart'; import 'package:linkdy/screens/bookmarks/provider/bookmarks.provider.dart';
import 'package:linkdy/screens/bookmarks/model/add_link.model.dart'; import 'package:linkdy/screens/bookmarks/model/bookmark_form.model.dart';
import 'package:linkdy/constants/global_keys.dart';
import 'package:linkdy/models/data/bookmarks.dart';
import 'package:linkdy/utils/snackbar.dart'; import 'package:linkdy/utils/snackbar.dart';
import 'package:linkdy/models/data/tags.dart'; import 'package:linkdy/models/data/tags.dart';
import 'package:linkdy/i18n/strings.g.dart'; import 'package:linkdy/i18n/strings.g.dart';
@@ -17,7 +18,7 @@ import 'package:linkdy/models/api_response.dart';
import 'package:linkdy/models/data/check_bookmark.dart'; import 'package:linkdy/models/data/check_bookmark.dart';
import 'package:linkdy/providers/api_client.provider.dart'; import 'package:linkdy/providers/api_client.provider.dart';
part 'add_bookmark.provider.g.dart'; part 'bookmark_form.provider.g.dart';
@riverpod @riverpod
Future<ApiResponse<CheckBookmark>> checkBookmark(CheckBookmarkRef ref, String url) async { Future<ApiResponse<CheckBookmark>> checkBookmark(CheckBookmarkRef ref, String url) async {
@@ -32,10 +33,10 @@ FutureOr<ApiResponse<TagsResponse>> getTags(GetTagsRef ref) async {
} }
@riverpod @riverpod
class AddBookmark extends _$AddBookmark { class BookmarkForm extends _$BookmarkForm {
@override @override
AddBookmarkModel build() { BookmarkFormModel build() {
return AddBookmarkModel( return BookmarkFormModel(
urlController: TextEditingController(), urlController: TextEditingController(),
titleController: TextEditingController(), titleController: TextEditingController(),
descriptionController: TextEditingController(), descriptionController: TextEditingController(),
@@ -45,6 +46,25 @@ class AddBookmark extends _$AddBookmark {
); );
} }
void initializeProvider(Bookmark bookmark) {
state.editBookmarkId = bookmark.id;
state.checkBookmark = CheckBookmark(
bookmark: bookmark,
metadata: Metadata(
url: bookmark.url,
description: bookmark.websiteDescription,
title: bookmark.websiteTitle,
),
);
state.checkBookmarkLoadStatus = LoadStatus.loaded;
state.urlController.text = bookmark.url ?? '';
state.titleController.text = bookmark.title ?? '';
state.descriptionController.text = bookmark.description ?? '';
state.tags = bookmark.tagNames ?? [];
state.notesController.text = bookmark.notes ?? '';
state.markAsUnread = bookmark.unread ?? false;
}
void validateUrl(String value) { void validateUrl(String value) {
state.checkBookmark = null; state.checkBookmark = null;
state.checkBookmarkLoadStatus = null; state.checkBookmarkLoadStatus = null;
@@ -78,7 +98,7 @@ class AddBookmark extends _$AddBookmark {
ref.notifyListeners(); ref.notifyListeners();
} }
void addBookmark() async { void saveBookmark() async {
final newBookmark = SetBookmarkData( final newBookmark = SetBookmarkData(
url: state.urlController.text, url: state.urlController.text,
title: state.titleController.text != "" ? state.titleController.text : state.checkBookmark?.metadata?.title ?? '', title: state.titleController.text != "" ? state.titleController.text : state.checkBookmark?.metadata?.title ?? '',
@@ -89,18 +109,26 @@ class AddBookmark extends _$AddBookmark {
unread: state.markAsUnread, unread: state.markAsUnread,
shared: false, shared: false,
tagNames: state.tags.join(","), tagNames: state.tags.join(","),
notes: state.notesController.text,
); );
final processModal = ProcessModal(); final processModal = ProcessModal();
processModal.open(t.bookmarks.addBookmark.savingBookmark); processModal.open(t.bookmarks.addBookmark.savingBookmark);
final result = await ref.watch(apiClientProvider)!.postBookmark(newBookmark); final result = state.editBookmarkId != null
? await ref.watch(apiClientProvider)!.putUpdateBookmark(state.editBookmarkId!, newBookmark)
: await ref.watch(apiClientProvider)!.postBookmark(newBookmark);
processModal.close(); processModal.close();
if (result.successful == true) { if (result.successful == true) {
ref.read(bookmarksProvider.notifier).refresh(); ref.read(bookmarksProvider.notifier).refresh();
ref.watch(routerProvider).pop(); ref.watch(routerProvider).pop();
showSnackbar(
key: ScaffoldMessengerKeys.bookmarks,
label: t.bookmarks.addBookmark.bookmarkSavedSuccessfully,
color: Colors.green,
);
} else { } else {
showSnackbar( showSnackbar(
key: ScaffoldMessengerKeys.addBookmark, key: ScaffoldMessengerKeys.addBookmark,

View File

@@ -1,6 +1,6 @@
// GENERATED CODE - DO NOT MODIFY BY HAND // GENERATED CODE - DO NOT MODIFY BY HAND
part of 'add_bookmark.provider.dart'; part of 'bookmark_form.provider.dart';
// ************************************************************************** // **************************************************************************
// RiverpodGenerator // RiverpodGenerator
@@ -175,20 +175,20 @@ final getTagsProvider =
); );
typedef GetTagsRef = AutoDisposeFutureProviderRef<ApiResponse<TagsResponse>>; typedef GetTagsRef = AutoDisposeFutureProviderRef<ApiResponse<TagsResponse>>;
String _$addBookmarkHash() => r'a350eee7aeed2fabae90966afadc609ee5eef895'; String _$bookmarkFormHash() => r'e0a6612cb2a409f488614e3e91619f41055df61b';
/// See also [AddBookmark]. /// See also [BookmarkForm].
@ProviderFor(AddBookmark) @ProviderFor(BookmarkForm)
final addBookmarkProvider = final bookmarkFormProvider =
AutoDisposeNotifierProvider<AddBookmark, AddBookmarkModel>.internal( AutoDisposeNotifierProvider<BookmarkForm, BookmarkFormModel>.internal(
AddBookmark.new, BookmarkForm.new,
name: r'addBookmarkProvider', name: r'bookmarkFormProvider',
debugGetCreateSourceHash: debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$addBookmarkHash, const bool.fromEnvironment('dart.vm.product') ? null : _$bookmarkFormHash,
dependencies: null, dependencies: null,
allTransitiveDependencies: null, allTransitiveDependencies: null,
); );
typedef _$AddBookmark = AutoDisposeNotifier<AddBookmarkModel>; typedef _$BookmarkForm = AutoDisposeNotifier<BookmarkFormModel>;
// 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

@@ -58,6 +58,7 @@ class BookmarkCommonFunctions {
unread: bookmark.unread == true ? false : true, unread: bookmark.unread == true ? false : true,
tagNames: bookmark.tagNames?.join(",") ?? '', tagNames: bookmark.tagNames?.join(",") ?? '',
title: bookmark.title ?? '', title: bookmark.title ?? '',
notes: bookmark.notes ?? '',
), ),
); );

View File

@@ -1,24 +1,42 @@
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:easy_autocomplete/easy_autocomplete.dart'; import 'package:easy_autocomplete/easy_autocomplete.dart';
import 'package:linkdy/screens/bookmarks/provider/add_bookmark.provider.dart'; import 'package:linkdy/screens/bookmarks/provider/bookmark_form.provider.dart';
import 'package:linkdy/widgets/section_label.dart'; import 'package:linkdy/widgets/section_label.dart';
import 'package:linkdy/models/data/bookmarks.dart';
import 'package:linkdy/i18n/strings.g.dart'; import 'package:linkdy/i18n/strings.g.dart';
import 'package:linkdy/constants/global_keys.dart'; import 'package:linkdy/constants/global_keys.dart';
import 'package:linkdy/constants/enums.dart'; import 'package:linkdy/constants/enums.dart';
class AddBookmarkModal extends ConsumerWidget { class BookmarkFormModal extends ConsumerStatefulWidget {
final bool fullscreen; final bool fullscreen;
final Bookmark? bookmark;
const AddBookmarkModal({ const BookmarkFormModal({
super.key, super.key,
required this.fullscreen, required this.fullscreen,
this.bookmark,
}); });
@override @override
Widget build(BuildContext context, WidgetRef ref) { BookmarkFormModalState createState() => BookmarkFormModalState();
}
class BookmarkFormModalState extends ConsumerState<BookmarkFormModal> {
@override
void initState() {
if (widget.bookmark != null) {
ref.read(bookmarkFormProvider.notifier).initializeProvider(widget.bookmark!);
}
super.initState();
}
@override
Widget build(BuildContext context) {
return ScaffoldMessenger( return ScaffoldMessenger(
key: ScaffoldMessengerKeys.addBookmark, key: ScaffoldMessengerKeys.addBookmark,
child: Dialog.fullscreen( child: Dialog.fullscreen(
@@ -35,11 +53,15 @@ class AddBookmarkModal extends ConsumerWidget {
leading: CloseButton( leading: CloseButton(
onPressed: () => Navigator.pop(context), onPressed: () => Navigator.pop(context),
), ),
title: Text(t.bookmarks.addBookmark.addBookmark), title: Text(
widget.bookmark != null
? t.bookmarks.addBookmark.editBookmark
: t.bookmarks.addBookmark.addBookmark,
),
actions: [ actions: [
IconButton( IconButton(
onPressed: ref.watch(addBookmarkProvider).checkBookmark != null onPressed: ref.watch(bookmarkFormProvider).checkBookmark != null
? () => ref.read(addBookmarkProvider.notifier).addBookmark() ? () => ref.read(bookmarkFormProvider.notifier).saveBookmark()
: null, : null,
icon: const Icon(Icons.save_rounded), icon: const Icon(Icons.save_rounded),
tooltip: t.generic.save, tooltip: t.generic.save,
@@ -59,9 +81,7 @@ class AddBookmarkModal extends ConsumerWidget {
handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
), ),
SliverList.list( SliverList.list(
children: const [ children: const [_ModalContent()],
_ModalContent(),
],
), ),
], ],
), ),
@@ -79,10 +99,12 @@ class _ModalContent extends ConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final provider = ref.watch(addBookmarkProvider); final provider = ref.watch(bookmarkFormProvider);
final tags = ref.watch(getTagsProvider); final tags = ref.watch(getTagsProvider);
final enabledFields = provider.checkBookmark != null;
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@@ -99,7 +121,7 @@ class _ModalContent extends ConsumerWidget {
padding: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 16),
child: TextFormField( child: TextFormField(
controller: provider.urlController, controller: provider.urlController,
onChanged: ref.read(addBookmarkProvider.notifier).validateUrl, onChanged: ref.read(bookmarkFormProvider.notifier).validateUrl,
enabled: provider.checkBookmarkLoadStatus != LoadStatus.loading, enabled: provider.checkBookmarkLoadStatus != LoadStatus.loading,
autocorrect: false, autocorrect: false,
decoration: InputDecoration( decoration: InputDecoration(
@@ -122,7 +144,7 @@ class _ModalContent extends ConsumerWidget {
onPressed: provider.checkBookmarkLoadStatus == null && onPressed: provider.checkBookmarkLoadStatus == null &&
provider.urlError == null && provider.urlError == null &&
provider.urlController.text != "" provider.urlController.text != ""
? () => ref.read(addBookmarkProvider.notifier).checkUrlDetails() ? () => ref.read(bookmarkFormProvider.notifier).checkUrlDetails()
: null, : null,
style: ButtonStyle( style: ButtonStyle(
foregroundColor: provider.checkBookmarkLoadStatus == LoadStatus.loaded foregroundColor: provider.checkBookmarkLoadStatus == LoadStatus.loaded
@@ -189,7 +211,7 @@ class _ModalContent extends ConsumerWidget {
floatingLabelBehavior: floatingLabelBehavior:
provider.checkBookmark != null ? FloatingLabelBehavior.always : FloatingLabelBehavior.auto, provider.checkBookmark != null ? FloatingLabelBehavior.always : FloatingLabelBehavior.auto,
helperText: t.bookmarks.addBookmark.leaveEmptyUseWebsiteTitle, helperText: t.bookmarks.addBookmark.leaveEmptyUseWebsiteTitle,
enabled: provider.checkBookmark != null, enabled: enabledFields,
), ),
), ),
), ),
@@ -210,7 +232,7 @@ class _ModalContent extends ConsumerWidget {
floatingLabelBehavior: floatingLabelBehavior:
provider.checkBookmark != null ? FloatingLabelBehavior.always : FloatingLabelBehavior.auto, provider.checkBookmark != null ? FloatingLabelBehavior.always : FloatingLabelBehavior.auto,
helperText: t.bookmarks.addBookmark.leaveEmptyUseWebsiteDescription, helperText: t.bookmarks.addBookmark.leaveEmptyUseWebsiteDescription,
enabled: provider.checkBookmark != null, enabled: enabledFields,
), ),
), ),
), ),
@@ -226,13 +248,13 @@ class _ModalContent extends ConsumerWidget {
padding: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column( child: Column(
children: [ children: [
if (provider.checkBookmark != null) if (enabledFields == true)
Row( Row(
children: [ children: [
Expanded( Expanded(
child: EasyAutocomplete( child: EasyAutocomplete(
controller: provider.tagsController, controller: provider.tagsController,
onChanged: ref.read(addBookmarkProvider.notifier).validateTagInput, onChanged: ref.read(bookmarkFormProvider.notifier).validateTagInput,
suggestions: tags.value?.content?.results?.map((t) => t.name!).toList() ?? [], suggestions: tags.value?.content?.results?.map((t) => t.name!).toList() ?? [],
decoration: InputDecoration( decoration: InputDecoration(
border: const OutlineInputBorder( border: const OutlineInputBorder(
@@ -252,8 +274,8 @@ class _ModalContent extends ConsumerWidget {
), ),
onSubmitted: provider.tagsController.text != "" && provider.tagsError == null onSubmitted: provider.tagsController.text != "" && provider.tagsError == null
? (v) { ? (v) {
ref.read(addBookmarkProvider.notifier).setTags([...provider.tags, v]); ref.read(bookmarkFormProvider.notifier).setTags([...provider.tags, v]);
ref.read(addBookmarkProvider.notifier).clearTagsController(); ref.read(bookmarkFormProvider.notifier).clearTagsController();
} }
: null, : null,
), ),
@@ -263,9 +285,9 @@ class _ModalContent extends ConsumerWidget {
onPressed: provider.tagsController.text != "" && provider.tagsError == null onPressed: provider.tagsController.text != "" && provider.tagsError == null
? () { ? () {
ref ref
.read(addBookmarkProvider.notifier) .read(bookmarkFormProvider.notifier)
.setTags([...provider.tags, provider.tagsController.text]); .setTags([...provider.tags, provider.tagsController.text]);
ref.read(addBookmarkProvider.notifier).clearTagsController(); ref.read(bookmarkFormProvider.notifier).clearTagsController();
} }
: null, : null,
icon: const Icon(Icons.check_rounded), icon: const Icon(Icons.check_rounded),
@@ -273,7 +295,7 @@ class _ModalContent extends ConsumerWidget {
), ),
], ],
), ),
if (provider.checkBookmark != null) const SizedBox(height: 16), if (enabledFields == true) const SizedBox(height: 16),
SizedBox( SizedBox(
height: 40, height: 40,
child: provider.tags.isNotEmpty child: provider.tags.isNotEmpty
@@ -284,7 +306,7 @@ class _ModalContent extends ConsumerWidget {
itemBuilder: (context, index) => InputChip( itemBuilder: (context, index) => InputChip(
label: Text(provider.tags[index]), label: Text(provider.tags[index]),
onDeleted: () => ref onDeleted: () => ref
.read(addBookmarkProvider.notifier) .read(bookmarkFormProvider.notifier)
.setTags(provider.tags.where((tag) => tag != provider.tags[index]).toList()), .setTags(provider.tags.where((tag) => tag != provider.tags[index]).toList()),
), ),
) )
@@ -329,7 +351,7 @@ class _ModalContent extends ConsumerWidget {
minLines: null, minLines: null,
maxLines: null, maxLines: null,
textAlignVertical: TextAlignVertical.top, textAlignVertical: TextAlignVertical.top,
enabled: provider.checkBookmark != null, enabled: enabledFields,
), ),
), ),
), ),
@@ -339,7 +361,7 @@ class _ModalContent extends ConsumerWidget {
title: Text(t.bookmarks.addBookmark.markAsUnread), title: Text(t.bookmarks.addBookmark.markAsUnread),
value: provider.markAsUnread, value: provider.markAsUnread,
onChanged: provider.checkBookmark != null onChanged: provider.checkBookmark != null
? (v) => ref.read(addBookmarkProvider.notifier).updateMarkAsUnread(v) ? (v) => ref.read(bookmarkFormProvider.notifier).updateMarkAsUnread(v)
: null, : null,
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
@@ -347,3 +369,26 @@ class _ModalContent extends ConsumerWidget {
); );
} }
} }
void openBookmarkFormModal({
required BuildContext context,
required double width,
Bookmark? bookmark,
}) {
showGeneralDialog(
context: context,
barrierColor: !(width > 700 || !(Platform.isAndroid || Platform.isIOS)) ? Colors.transparent : Colors.black54,
transitionBuilder: (context, anim1, anim2, child) {
return SlideTransition(
position: Tween(begin: const Offset(0, 1), end: const Offset(0, 0)).animate(
CurvedAnimation(parent: anim1, curve: Curves.easeInOutCubicEmphasized),
),
child: child,
);
},
pageBuilder: (context, animation, secondaryAnimation) => BookmarkFormModal(
fullscreen: width <= 700,
bookmark: bookmark,
),
);
}

View File

@@ -21,14 +21,16 @@ class BookmarkItem extends ConsumerWidget {
final void Function(Bookmark bookmark) onDelete; final void Function(Bookmark bookmark) onDelete;
final void Function(Bookmark bookmark) onReadUnread; final void Function(Bookmark bookmark) onReadUnread;
final void Function(Bookmark bookmark) onArchiveUnarchive; final void Function(Bookmark bookmark) onArchiveUnarchive;
final void Function(Bookmark bookmark) onEdit;
const BookmarkItem({ const BookmarkItem({
Key? key, super.key,
required this.bookmark, required this.bookmark,
required this.onDelete, required this.onDelete,
required this.onReadUnread, required this.onReadUnread,
required this.onArchiveUnarchive, required this.onArchiveUnarchive,
}) : super(key: key); required this.onEdit,
});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
@@ -77,7 +79,7 @@ class BookmarkItem extends ConsumerWidget {
extentRatio: 0.5, extentRatio: 0.5,
children: [ children: [
SlidableAction( SlidableAction(
onPressed: (ctx) => {}, onPressed: (ctx) => onEdit(bookmark),
backgroundColor: Colors.green, backgroundColor: Colors.green,
label: t.bookmarks.bookmarkOptions.edit, label: t.bookmarks.bookmarkOptions.edit,
icon: Icons.edit_rounded, icon: Icons.edit_rounded,

View File

@@ -6,12 +6,13 @@ import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:linkdy/screens/bookmarks/provider/bookmarks.provider.dart'; import 'package:linkdy/screens/bookmarks/provider/bookmarks.provider.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/add_bookmark_modal.dart'; import 'package:linkdy/screens/bookmarks/ui/bookmark_form_modal.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/bookmarks/ui/search_bookmarks.dart'; import 'package:linkdy/screens/bookmarks/ui/search_bookmarks.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';
import 'package:linkdy/models/data/bookmarks.dart';
import 'package:linkdy/constants/enums.dart'; import 'package:linkdy/constants/enums.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';
@@ -25,22 +26,6 @@ class BookmarksScreen extends ConsumerWidget {
final width = MediaQuery.of(context).size.width; final width = MediaQuery.of(context).size.width;
void openAddModal() {
showGeneralDialog(
context: context,
barrierColor: !(width > 700 || !(Platform.isAndroid || Platform.isIOS)) ? Colors.transparent : Colors.black54,
transitionBuilder: (context, anim1, anim2, child) {
return SlideTransition(
position: Tween(begin: const Offset(0, 1), end: const Offset(0, 0)).animate(
CurvedAnimation(parent: anim1, curve: Curves.easeInOutCubicEmphasized),
),
child: child,
);
},
pageBuilder: (context, animation, secondaryAnimation) => AddBookmarkModal(fullscreen: width <= 700),
);
}
void openSearchModal() { void openSearchModal() {
showGeneralDialog( showGeneralDialog(
context: context, context: context,
@@ -150,6 +135,7 @@ class BookmarksScreen extends ConsumerWidget {
), ),
), ),
onArchiveUnarchive: ref.read(bookmarksProvider.notifier).archiveUnarchive, onArchiveUnarchive: ref.read(bookmarksProvider.notifier).archiveUnarchive,
onEdit: (b) => openBookmarkFormModal(context: context, width: width, bookmark: b),
); );
}, },
), ),
@@ -162,7 +148,7 @@ class BookmarksScreen extends ConsumerWidget {
), ),
), ),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
onPressed: openAddModal, onPressed: () => openBookmarkFormModal(context: context, width: width),
child: const Icon(Icons.add_rounded), child: const Icon(Icons.add_rounded),
), ),
), ),

View File

@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:linkdy/screens/bookmarks/provider/search_bookmarks.provider.dart'; import 'package:linkdy/screens/bookmarks/provider/search_bookmarks.provider.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/widgets/enter_search_term_screen.dart'; import 'package:linkdy/widgets/enter_search_term_screen.dart';
@@ -27,6 +28,8 @@ class SearchBookmarksModal extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final provider = ref.watch(searchBookmarksProvider); final provider = ref.watch(searchBookmarksProvider);
final width = MediaQuery.of(context).size.width;
bool scrollListener(ScrollUpdateNotification scrollNotification) { bool scrollListener(ScrollUpdateNotification scrollNotification) {
if (scrollNotification.metrics.extentAfter < 100 && if (scrollNotification.metrics.extentAfter < 100 &&
provider.loadingMore == false && provider.loadingMore == false &&
@@ -135,6 +138,7 @@ class SearchBookmarksModal extends ConsumerWidget {
), ),
), ),
onArchiveUnarchive: ref.read(searchBookmarksProvider.notifier).archiveUnarchive, onArchiveUnarchive: ref.read(searchBookmarksProvider.notifier).archiveUnarchive,
onEdit: (b) => openBookmarkFormModal(context: context, width: width, bookmark: b),
); );
}, },
), ),

View File

@@ -1,14 +1,15 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:linkdy/constants/global_keys.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/tag_bookmarks/provider/tag_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';
import 'package:linkdy/constants/global_keys.dart';
import 'package:linkdy/models/data/tags.dart'; import 'package:linkdy/models/data/tags.dart';
import 'package:linkdy/constants/enums.dart'; import 'package:linkdy/constants/enums.dart';
import 'package:linkdy/providers/router.provider.dart'; import 'package:linkdy/providers/router.provider.dart';
@@ -57,6 +58,8 @@ class TagBookmarksScreenState extends ConsumerState<TagBookmarksScreen> {
final provider = ref.watch(tagBookmarksProvider); final provider = ref.watch(tagBookmarksProvider);
final width = MediaQuery.of(context).size.width;
bool scrollListener(ScrollUpdateNotification scrollNotification) { bool scrollListener(ScrollUpdateNotification scrollNotification) {
if (scrollNotification.metrics.extentAfter < 100 && if (scrollNotification.metrics.extentAfter < 100 &&
provider.loadingMore == false && provider.loadingMore == false &&
@@ -144,6 +147,7 @@ class TagBookmarksScreenState extends ConsumerState<TagBookmarksScreen> {
), ),
), ),
onArchiveUnarchive: ref.read(tagBookmarksProvider.notifier).archiveUnarchive, onArchiveUnarchive: ref.read(tagBookmarksProvider.notifier).archiveUnarchive,
onEdit: (b) => openBookmarkFormModal(context: context, width: width, bookmark: b),
); );
}, },
), ),