Added create tag
This commit is contained in:
@@ -4,9 +4,9 @@
|
||||
/// To regenerate, run: `dart run slang`
|
||||
///
|
||||
/// Locales: 2
|
||||
/// Strings: 208 (104 per locale)
|
||||
/// Strings: 218 (109 per locale)
|
||||
///
|
||||
/// Built on 2024-02-24 at 12:07 UTC
|
||||
/// Built on 2024-02-24 at 12:45 UTC
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint
|
||||
@@ -227,6 +227,8 @@ class _StringsTagsEn {
|
||||
|
||||
// Translations
|
||||
String get tags => 'Tags';
|
||||
String created({required Object created}) => 'Created: ${created}';
|
||||
late final _StringsTagsCreateTagEn createTag = _StringsTagsCreateTagEn._(_root);
|
||||
}
|
||||
|
||||
// Path: settings
|
||||
@@ -341,6 +343,19 @@ class _StringsBookmarksSearchEn {
|
||||
String get inputtedSearchTermNoResults => 'The inputted search term doesn\'t have any results';
|
||||
}
|
||||
|
||||
// Path: tags.createTag
|
||||
class _StringsTagsCreateTagEn {
|
||||
_StringsTagsCreateTagEn._(this._root);
|
||||
|
||||
final Translations _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
String get createTag => 'Create tag';
|
||||
String get name => 'Name';
|
||||
String get creatingTag => 'Creating tag...';
|
||||
String get errorCreatingTag => 'An error occured when creating the tag.';
|
||||
}
|
||||
|
||||
// Path: settings.customization
|
||||
class _StringsSettingsCustomizationEn {
|
||||
_StringsSettingsCustomizationEn._(this._root);
|
||||
@@ -487,6 +502,8 @@ class _StringsTagsEs implements _StringsTagsEn {
|
||||
|
||||
// Translations
|
||||
@override String get tags => 'Etiquetas';
|
||||
@override String created({required Object created}) => 'Creado: ${created}';
|
||||
@override late final _StringsTagsCreateTagEs createTag = _StringsTagsCreateTagEs._(_root);
|
||||
}
|
||||
|
||||
// Path: settings
|
||||
@@ -601,6 +618,19 @@ class _StringsBookmarksSearchEs implements _StringsBookmarksSearchEn {
|
||||
@override String get inputtedSearchTermNoResults => 'El término de búsqueda introducido no tiene resultados';
|
||||
}
|
||||
|
||||
// Path: tags.createTag
|
||||
class _StringsTagsCreateTagEs implements _StringsTagsCreateTagEn {
|
||||
_StringsTagsCreateTagEs._(this._root);
|
||||
|
||||
@override final _StringsEs _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
@override String get createTag => 'Crear etiqueta';
|
||||
@override String get name => 'Nombre';
|
||||
@override String get creatingTag => 'Creando etiqueta...';
|
||||
@override String get errorCreatingTag => 'Ocurrió un error al crear la etiqueta.';
|
||||
}
|
||||
|
||||
// Path: settings.customization
|
||||
class _StringsSettingsCustomizationEs implements _StringsSettingsCustomizationEn {
|
||||
_StringsSettingsCustomizationEs._(this._root);
|
||||
@@ -714,6 +744,11 @@ extension on Translations {
|
||||
case 'bookmarks.search.cannotSearchError': return 'Cannot search content due to an error';
|
||||
case 'bookmarks.search.inputtedSearchTermNoResults': return 'The inputted search term doesn\'t have any results';
|
||||
case 'tags.tags': return 'Tags';
|
||||
case 'tags.created': return ({required Object created}) => 'Created: ${created}';
|
||||
case 'tags.createTag.createTag': return 'Create tag';
|
||||
case 'tags.createTag.name': return 'Name';
|
||||
case 'tags.createTag.creatingTag': return 'Creating tag...';
|
||||
case 'tags.createTag.errorCreatingTag': return 'An error occured when creating the tag.';
|
||||
case 'settings.settings': return 'Settings';
|
||||
case 'settings.appSettings': return 'App settings';
|
||||
case 'settings.aboutApp': return 'About the app';
|
||||
@@ -826,6 +861,11 @@ extension on _StringsEs {
|
||||
case 'bookmarks.search.cannotSearchError': return 'No se puede buscar el contenido por un error';
|
||||
case 'bookmarks.search.inputtedSearchTermNoResults': return 'El término de búsqueda introducido no tiene resultados';
|
||||
case 'tags.tags': return 'Etiquetas';
|
||||
case 'tags.created': return ({required Object created}) => 'Creado: ${created}';
|
||||
case 'tags.createTag.createTag': return 'Crear etiqueta';
|
||||
case 'tags.createTag.name': return 'Nombre';
|
||||
case 'tags.createTag.creatingTag': return 'Creando etiqueta...';
|
||||
case 'tags.createTag.errorCreatingTag': return 'Ocurrió un error al crear la etiqueta.';
|
||||
case 'settings.settings': return 'Ajustes';
|
||||
case 'settings.appSettings': return 'Ajustes de la aplicación';
|
||||
case 'settings.aboutApp': return 'Sobre la aplicación';
|
||||
|
||||
@@ -75,7 +75,14 @@
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"tags": "Tags"
|
||||
"tags": "Tags",
|
||||
"created": "Created: $created",
|
||||
"createTag": {
|
||||
"createTag": "Create tag",
|
||||
"name": "Name",
|
||||
"creatingTag": "Creating tag...",
|
||||
"errorCreatingTag": "An error occured when creating the tag."
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"settings": "Settings",
|
||||
|
||||
@@ -75,7 +75,14 @@
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"tags": "Etiquetas"
|
||||
"tags": "Etiquetas",
|
||||
"created": "Creado: $created",
|
||||
"createTag": {
|
||||
"createTag": "Crear etiqueta",
|
||||
"name": "Nombre",
|
||||
"creatingTag": "Creando etiqueta...",
|
||||
"errorCreatingTag": "Ocurrió un error al crear la etiqueta."
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"settings": "Ajustes",
|
||||
|
||||
@@ -2,7 +2,7 @@ class TagsResponse {
|
||||
final int? count;
|
||||
final int? next;
|
||||
final int? previous;
|
||||
final List<Result>? results;
|
||||
final List<Tag>? results;
|
||||
|
||||
TagsResponse({
|
||||
this.count,
|
||||
@@ -15,7 +15,7 @@ class TagsResponse {
|
||||
count: json["count"],
|
||||
next: json["next"],
|
||||
previous: json["previous"],
|
||||
results: json["results"] == null ? [] : List<Result>.from(json["results"]!.map((x) => Result.fromJson(x))),
|
||||
results: json["results"] == null ? [] : List<Tag>.from(json["results"]!.map((x) => Tag.fromJson(x))),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
@@ -26,18 +26,18 @@ class TagsResponse {
|
||||
};
|
||||
}
|
||||
|
||||
class Result {
|
||||
class Tag {
|
||||
final int? id;
|
||||
final String? name;
|
||||
final DateTime? dateAdded;
|
||||
|
||||
Result({
|
||||
Tag({
|
||||
this.id,
|
||||
this.name,
|
||||
this.dateAdded,
|
||||
});
|
||||
|
||||
factory Result.fromJson(Map<String, dynamic> json) => Result(
|
||||
factory Tag.fromJson(Map<String, dynamic> json) => Tag(
|
||||
id: json["id"],
|
||||
name: json["name"],
|
||||
dateAdded: json["date_added"] == null ? null : DateTime.parse(json["date_added"]),
|
||||
|
||||
@@ -93,7 +93,7 @@ class AddBookmark extends _$AddBookmark {
|
||||
final processModal = ProcessModal();
|
||||
processModal.open(t.bookmarks.addBookmark.savingBookmark);
|
||||
|
||||
final result = await ref.watch(apiClientProvider)!.fetchPostBookmark(newBookmark);
|
||||
final result = await ref.watch(apiClientProvider)!.postBookmark(newBookmark);
|
||||
|
||||
processModal.close();
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ final getTagsProvider =
|
||||
);
|
||||
|
||||
typedef GetTagsRef = AutoDisposeFutureProviderRef<ApiResponse<TagsResponse>>;
|
||||
String _$addBookmarkHash() => r'2953a4223fe70809fbcf37af23b4f61a62f0de9f';
|
||||
String _$addBookmarkHash() => r'7a2a1c6c6597aa28fa478d2ef9dc3bc4d71c143d';
|
||||
|
||||
/// See also [AddBookmark].
|
||||
@ProviderFor(AddBookmark)
|
||||
|
||||
9
lib/screens/tags/models/add_tag.modal.dart
Normal file
9
lib/screens/tags/models/add_tag.modal.dart
Normal file
@@ -0,0 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AddTagModel {
|
||||
final TextEditingController nameController;
|
||||
|
||||
const AddTagModel({
|
||||
required this.nameController,
|
||||
});
|
||||
}
|
||||
47
lib/screens/tags/provider/add_tag.provider.dart
Normal file
47
lib/screens/tags/provider/add_tag.provider.dart
Normal file
@@ -0,0 +1,47 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
import 'package:linkdy/screens/tags/ui/add_tag_error_modal.dart';
|
||||
import 'package:linkdy/screens/tags/provider/tags.provider.dart';
|
||||
import 'package:linkdy/screens/tags/models/add_tag.modal.dart';
|
||||
|
||||
import 'package:linkdy/router/routes.dart';
|
||||
import 'package:linkdy/providers/api_client_provider.dart';
|
||||
import 'package:linkdy/providers/router_provider.dart';
|
||||
import 'package:linkdy/utils/process_modal.dart';
|
||||
import 'package:linkdy/i18n/strings.g.dart';
|
||||
|
||||
part 'add_tag.provider.g.dart';
|
||||
|
||||
@riverpod
|
||||
class AddTag extends _$AddTag {
|
||||
@override
|
||||
AddTagModel build() {
|
||||
return AddTagModel(
|
||||
nameController: TextEditingController(),
|
||||
);
|
||||
}
|
||||
|
||||
void notifyOnWrite() {
|
||||
ref.notifyListeners();
|
||||
}
|
||||
|
||||
void onAddTag() async {
|
||||
final processModal = ProcessModal();
|
||||
processModal.open(t.tags.createTag.creatingTag);
|
||||
|
||||
final result = await ref.watch(apiClientProvider)!.postTag(state.nameController.text);
|
||||
|
||||
processModal.close();
|
||||
if (result.successful == true) {
|
||||
ref.invalidate(tagsRequestProvider);
|
||||
ref.watch(routerProvider).pop();
|
||||
} else {
|
||||
if (rootNavigatorKey.currentContext == null) return;
|
||||
showDialog(
|
||||
context: rootNavigatorKey.currentContext!,
|
||||
builder: (context) => const AddTagErrorModal(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
lib/screens/tags/provider/add_tag.provider.g.dart
Normal file
25
lib/screens/tags/provider/add_tag.provider.g.dart
Normal file
@@ -0,0 +1,25 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'add_tag.provider.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$addTagHash() => r'8157bc7911ae64627adabbe1528f519a4b1cb913';
|
||||
|
||||
/// See also [AddTag].
|
||||
@ProviderFor(AddTag)
|
||||
final addTagProvider =
|
||||
AutoDisposeNotifierProvider<AddTag, AddTagModel>.internal(
|
||||
AddTag.new,
|
||||
name: r'addTagProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product') ? null : _$addTagHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$AddTag = AutoDisposeNotifier<AddTagModel>;
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
|
||||
24
lib/screens/tags/ui/add_tag_error_modal.dart
Normal file
24
lib/screens/tags/ui/add_tag_error_modal.dart
Normal file
@@ -0,0 +1,24 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:linkdy/i18n/strings.g.dart';
|
||||
|
||||
class AddTagErrorModal extends StatelessWidget {
|
||||
const AddTagErrorModal({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(t.generic.error),
|
||||
content: Text(t.tags.createTag.errorCreatingTag),
|
||||
actions: [
|
||||
Row(
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(t.generic.close),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
90
lib/screens/tags/ui/add_tag_modal.dart
Normal file
90
lib/screens/tags/ui/add_tag_modal.dart
Normal file
@@ -0,0 +1,90 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:linkdy/screens/tags/provider/add_tag.provider.dart';
|
||||
|
||||
import 'package:linkdy/i18n/strings.g.dart';
|
||||
|
||||
class AddTagModal extends ConsumerWidget {
|
||||
const AddTagModal({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final provider = ref.watch(addTagProvider);
|
||||
|
||||
return Padding(
|
||||
padding: MediaQuery.of(context).viewInsets,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(28),
|
||||
topRight: Radius.circular(28),
|
||||
),
|
||||
color: Theme.of(context).dialogBackgroundColor,
|
||||
),
|
||||
child: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.add_rounded,
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
size: 24,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
t.tags.createTag.createTag,
|
||||
style: const TextStyle(
|
||||
fontSize: 24,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
TextFormField(
|
||||
controller: provider.nameController,
|
||||
onChanged: (_) => ref.read(addTagProvider.notifier).notifyOnWrite(),
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: const Icon(Icons.label_rounded),
|
||||
border: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(10),
|
||||
),
|
||||
),
|
||||
labelText: t.tags.createTag.name,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 24),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(t.generic.cancel),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
TextButton(
|
||||
onPressed: provider.nameController.text != ""
|
||||
? () => ref.read(addTagProvider.notifier).onAddTag()
|
||||
: null,
|
||||
child: Text(t.generic.confirm),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:linkdy/screens/tags/provider/tags.provider.dart';
|
||||
import 'package:linkdy/screens/tags/ui/add_tag_modal.dart';
|
||||
import 'package:linkdy/utils/date_to_string.dart';
|
||||
import 'package:linkdy/widgets/error_screen.dart';
|
||||
import 'package:linkdy/widgets/no_data_screen.dart';
|
||||
@@ -17,22 +18,13 @@ class TagsScreen extends ConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final tags = ref.watch(tagsRequestProvider);
|
||||
|
||||
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),
|
||||
// );
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
useRootNavigator: true,
|
||||
isScrollControlled: true,
|
||||
builder: (context) => const AddTagModal(),
|
||||
);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
@@ -101,7 +93,7 @@ class TagsScreen extends ConsumerWidget {
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
dateToString(tag.dateAdded!),
|
||||
t.tags.created(created: dateToString(tag.dateAdded!)),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
|
||||
@@ -70,7 +70,7 @@ class ApiClientService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<ApiResponse<Bookmark>> fetchPostBookmark(PostBookmark bookmark) async {
|
||||
Future<ApiResponse<Bookmark>> postBookmark(PostBookmark bookmark) async {
|
||||
try {
|
||||
final response = await dioInstance.post(
|
||||
"/bookmarks/",
|
||||
@@ -105,4 +105,20 @@ class ApiClientService {
|
||||
return const ApiResponse(successful: false);
|
||||
}
|
||||
}
|
||||
|
||||
Future<ApiResponse<Tag>> postTag(String name) async {
|
||||
try {
|
||||
final response = await dioInstance.post(
|
||||
"/tags/",
|
||||
data: FormData.fromMap({"name": name}),
|
||||
);
|
||||
return ApiResponse(
|
||||
successful: true,
|
||||
content: Tag.fromJson(response.data),
|
||||
);
|
||||
} catch (e, stackTrace) {
|
||||
Sentry.captureException(e, stackTrace: stackTrace);
|
||||
return const ApiResponse(successful: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user