From 225df61aa05db1bda7b098c9d6b08570dc3ebabb Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Wed, 29 Apr 2020 13:42:01 +0200 Subject: [PATCH] Can upload new custom emojies --- lib/helpers/settings_helper.dart | 11 ++ lib/helpers/users_helper.dart | 3 +- lib/models/new_emoji.dart | 18 +++ .../emojies_account_settings.dart | 127 ++++++++++++++++++ lib/utils/input_utils.dart | 4 + 5 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 lib/models/new_emoji.dart diff --git a/lib/helpers/settings_helper.dart b/lib/helpers/settings_helper.dart index 80a42cf..e807775 100644 --- a/lib/helpers/settings_helper.dart +++ b/lib/helpers/settings_helper.dart @@ -4,6 +4,7 @@ import 'package:comunic/enums/user_page_visibility.dart'; import 'package:comunic/models/account_image_settings.dart'; import 'package:comunic/models/api_request.dart'; import 'package:comunic/models/general_settings.dart'; +import 'package:comunic/models/new_emoji.dart'; /// Settings helper /// @@ -123,4 +124,14 @@ class SettingsHelper { (await APIRequest(uri: "settings/delete_account_image", needLogin: true) .exec()) .isOK; + + /// Upload a new emoji + static Future uploadNewCustomEmoji(NewEmoji newEmoji) async => + (await APIRequest( + uri: "settings/upload_custom_emoji", + needLogin: true, + args: {"shortcut": newEmoji.shortcut}) + .addFile("image", newEmoji.image) + .execWithFiles()) + .assertOk(); } diff --git a/lib/helpers/users_helper.dart b/lib/helpers/users_helper.dart index be0f7e4..01d259a 100644 --- a/lib/helpers/users_helper.dart +++ b/lib/helpers/users_helper.dart @@ -73,7 +73,8 @@ class UsersHelper { /// of failure Future getListWithThrow(Set users, {bool forceDownload = false}) async { - final list = await getUsersInfo(users.toList()); + final list = + await getUsersInfo(users.toList(), forceDownload: forceDownload); if (list == null) throw Exception( diff --git a/lib/models/new_emoji.dart b/lib/models/new_emoji.dart new file mode 100644 index 0000000..de96983 --- /dev/null +++ b/lib/models/new_emoji.dart @@ -0,0 +1,18 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; + +/// New emoji information +/// +/// @author Pierre HUBERT + +class NewEmoji { + final String shortcut; + final File image; + + const NewEmoji({ + @required this.shortcut, + @required this.image, + }) : assert(shortcut != null), + assert(image != null); +} diff --git a/lib/ui/routes/account_settings/emojies_account_settings.dart b/lib/ui/routes/account_settings/emojies_account_settings.dart index 9baa236..78334d3 100644 --- a/lib/ui/routes/account_settings/emojies_account_settings.dart +++ b/lib/ui/routes/account_settings/emojies_account_settings.dart @@ -1,9 +1,16 @@ +import 'dart:io'; + +import 'package:comunic/helpers/settings_helper.dart'; import 'package:comunic/helpers/users_helper.dart'; +import 'package:comunic/models/new_emoji.dart'; import 'package:comunic/models/user.dart'; import 'package:comunic/ui/widgets/async_screen_widget.dart'; import 'package:comunic/ui/widgets/network_image_widget.dart'; import 'package:comunic/utils/account_utils.dart'; +import 'package:comunic/utils/files_utils.dart'; +import 'package:comunic/utils/input_utils.dart'; import 'package:comunic/utils/intl_utils.dart'; +import 'package:comunic/utils/ui_utils.dart'; import 'package:flutter/material.dart'; /// Emojies account settings @@ -30,6 +37,8 @@ class _EmojiesAccountBody extends StatefulWidget { class __EmojiesAccountBodyState extends State<_EmojiesAccountBody> { User _user; + final _key = GlobalKey(); + Future _reload() async { _user = await UsersHelper().getSingleWithThrow( userID(), @@ -40,13 +49,21 @@ class __EmojiesAccountBodyState extends State<_EmojiesAccountBody> { @override Widget build(BuildContext context) { return AsyncScreenWidget( + key: _key, onReload: _reload, onBuild: _buildSettings, errorMessage: tr("Could not refresh user information!"), + showOldDataWhileUpdating: true, ); } Widget _buildSettings() { + return Stack( + children: [_buildList(), _buildAddButton()], + ); + } + + Widget _buildList() { return ListView( children: _user.customEmojies .map((u) => ListTile( @@ -55,4 +72,114 @@ class __EmojiesAccountBodyState extends State<_EmojiesAccountBody> { )) .toList()); } + + Widget _buildAddButton() { + return Positioned( + child: FloatingActionButton( + onPressed: _addEmoji, + child: Icon(Icons.add), + ), + right: 20, + bottom: 20, + ); + } + + /// Add a custom emoji + void _addEmoji() async { + try { + final newEmoji = await showDialog( + context: context, + builder: (c) => _NewEmojiDialog(), + ); + + await SettingsHelper.uploadNewCustomEmoji(newEmoji); + } catch (e, stack) { + print("Could not add a new emoji: $e\n$stack"); + showSimpleSnack(context, tr("Could not upload emoji!")); + } + + _key.currentState.refresh(); + } +} + +class _NewEmojiDialog extends StatefulWidget { + @override + __NewEmojiDialogState createState() => __NewEmojiDialogState(); +} + +class __NewEmojiDialogState extends State<_NewEmojiDialog> { + final _controller = TextEditingController(); + File _file; + + bool get _hasImage => _file != null; + + String get _shortcut => _controller.text; + + bool get _shortcutValid => + _shortcut.isNotEmpty && validateShortcut(_shortcut); + + bool get _valid => _hasImage && _shortcutValid; + + NewEmoji get _emoji => NewEmoji(shortcut: _shortcut, image: _file); + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(tr("Add new emoji")), + content: ConstrainedBox( + constraints: + BoxConstraints(maxHeight: MediaQuery.of(context).size.height - 50), + child: SingleChildScrollView( + child: _buildBody(), + ), + ), + actions: [ + MaterialButton( + onPressed: () => Navigator.of(context).pop(), + child: Text(tr("Cancel").toUpperCase()), + ), + MaterialButton( + onPressed: _valid ? () => Navigator.of(context).pop(_emoji) : null, + child: Text(tr("Add").toUpperCase()), + ), + ], + ); + } + + Widget _buildBody() { + return Column( + children: [ + TextField( + controller: _controller, + onChanged: (s) => setState(() {}), + decoration: InputDecoration( + alignLabelWithHint: true, + labelText: tr("Shortcut"), + hintText: tr(":yourShortcut:"), + errorText: _shortcut.isNotEmpty && !_shortcutValid + ? tr("Invalid shortcut!") + : null, + ), + ), + MaterialButton( + onPressed: _pickImage, + child: Text(_hasImage ? tr("Replace image") : tr("Add image")), + ) + ], + ); + } + + void _pickImage() async { + try { + final image = await pickImage(context); + + if (image == null) return; + + setState(() { + _file = image; + }); + } catch (e, stack) { + print("Could not pick an image! $e\n$stack"); + } + } } diff --git a/lib/utils/input_utils.dart b/lib/utils/input_utils.dart index bf88238..5dd2308 100644 --- a/lib/utils/input_utils.dart +++ b/lib/utils/input_utils.dart @@ -31,3 +31,7 @@ bool validateUrl(String url) { /// Validate directory reference bool validateDirectoryReference(String ref) => RegExp(r'@[a-zA-Z0-9]+').hasMatch(ref); + +/// Validated a shortcut +bool validateShortcut(String shortcut) => + RegExp(r'^:[a-zA-Z0-9]+:$').hasMatch(shortcut);