mirror of
				https://gitlab.com/famedly/fluffychat.git
				synced 2025-11-03 22:07:23 +01:00 
			
		
		
		
	add basic emote picker
This commit is contained in:
		
							parent
							
								
									b3ecefd354
								
							
						
					
					
						commit
						f8a0233b74
					
				@ -107,6 +107,30 @@ class SimpleDialogs {
 | 
			
		||||
    return confirmed;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> inform({
 | 
			
		||||
    String titleText,
 | 
			
		||||
    String contentText,
 | 
			
		||||
    String okText,
 | 
			
		||||
  }) async {
 | 
			
		||||
    await showDialog(
 | 
			
		||||
      context: context,
 | 
			
		||||
      builder: (c) => AlertDialog(
 | 
			
		||||
        title: titleText != null ? Text(titleText) : null,
 | 
			
		||||
        content: contentText != null ? Text(contentText) : null,
 | 
			
		||||
        actions:  <Widget>[
 | 
			
		||||
          FlatButton(
 | 
			
		||||
            child: Text(
 | 
			
		||||
              okText ?? L10n.of(context).ok.toUpperCase(),
 | 
			
		||||
            ),
 | 
			
		||||
            onPressed: () {
 | 
			
		||||
              Navigator.of(context).pop();
 | 
			
		||||
            },
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<dynamic> tryRequestWithLoadingDialog(Future<dynamic> request,
 | 
			
		||||
      {Function(MatrixException) onAdditionalAuth}) async {
 | 
			
		||||
    showLoadingDialog(context);
 | 
			
		||||
 | 
			
		||||
@ -424,6 +424,31 @@
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "Emote Settings": "Emote Einstellungen",
 | 
			
		||||
  "@Emote Settings": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "Emote shortcode": "Emote kürzel",
 | 
			
		||||
  "@Emote shortcode": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "emoteWarnNeedToPick": "Wähle ein Emote-kürzel und ein Bild!",
 | 
			
		||||
  "@emoteWarnNeedToPick": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "emoteExists": "Emote existiert bereits!",
 | 
			
		||||
  "@emoteExists": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "emoteInvalid": "Ungültiges Emote-kürzel!",
 | 
			
		||||
  "@emoteInvalid": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "Empty chat": "Leerer Chat",
 | 
			
		||||
  "@Empty chat": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
@ -768,6 +793,11 @@
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "No emotes found. 😕": "Keine Emotes gefunden. 😕",
 | 
			
		||||
  "@No emotes found. 😕": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "No permission": "Keine Berechtigung",
 | 
			
		||||
  "@No permission": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
@ -790,6 +820,11 @@
 | 
			
		||||
      "number": {}
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "ok": "ok",
 | 
			
		||||
  "@ok": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "Oops something went wrong...": "Hoppla! Da ist etwas schief gelaufen ...",
 | 
			
		||||
  "@Oops something went wrong...": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
@ -815,6 +850,11 @@
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "Pick image": "Wähle Bild",
 | 
			
		||||
  "@Pick image": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "play": "Play {fileName}",
 | 
			
		||||
  "@play": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
@ -1327,4 +1367,4 @@
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
{
 | 
			
		||||
  "@@last_modified": "2020-05-09T15:29:08.901368",
 | 
			
		||||
  "@@last_modified": "2020-05-12T08:42:24.358124",
 | 
			
		||||
  "About": "About",
 | 
			
		||||
  "@About": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
@ -424,6 +424,31 @@
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "Emote Settings": "Emote Settings",
 | 
			
		||||
  "@Emote Settings": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "Emote shortcode": "Emote shortcode",
 | 
			
		||||
  "@Emote shortcode": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "emoteWarnNeedToPick": "You need to pick an emote shortcode and an image!",
 | 
			
		||||
  "@emoteWarnNeedToPick": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "emoteExists": "Emote already exists!",
 | 
			
		||||
  "@emoteExists": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "emoteInvalid": "Invalid emote shortcode!",
 | 
			
		||||
  "@emoteInvalid": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "Empty chat": "Empty chat",
 | 
			
		||||
  "@Empty chat": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
@ -768,6 +793,11 @@
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "No emotes found. 😕": "No emotes found. 😕",
 | 
			
		||||
  "@No emotes found. 😕": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "No permission": "No permission",
 | 
			
		||||
  "@No permission": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
@ -790,6 +820,11 @@
 | 
			
		||||
      "number": {}
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "ok": "ok",
 | 
			
		||||
  "@ok": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "Oops something went wrong...": "Oops something went wrong...",
 | 
			
		||||
  "@Oops something went wrong...": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
@ -820,6 +855,11 @@
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "Pick image": "Pick image",
 | 
			
		||||
  "@Pick image": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
    "placeholders": {}
 | 
			
		||||
  },
 | 
			
		||||
  "play": "Play {fileName}",
 | 
			
		||||
  "@play": {
 | 
			
		||||
    "type": "text",
 | 
			
		||||
 | 
			
		||||
@ -298,6 +298,20 @@ class L10n extends MatrixLocalizations {
 | 
			
		||||
 | 
			
		||||
  String get editDisplayname => Intl.message("Edit displayname");
 | 
			
		||||
 | 
			
		||||
  String get emoteSettings => Intl.message('Emote Settings');
 | 
			
		||||
 | 
			
		||||
  String get emoteShortcode => Intl.message('Emote shortcode');
 | 
			
		||||
 | 
			
		||||
  String get emoteWarnNeedToPick =>
 | 
			
		||||
      Intl.message('You need to pick an emote shortcode and an image!',
 | 
			
		||||
          name: 'emoteWarnNeedToPick');
 | 
			
		||||
 | 
			
		||||
  String get emoteExists =>
 | 
			
		||||
      Intl.message('Emote already exists!', name: 'emoteExists');
 | 
			
		||||
 | 
			
		||||
  String get emoteInvalid =>
 | 
			
		||||
      Intl.message('Invalid emote shortcode!', name: 'emoteInvalid');
 | 
			
		||||
 | 
			
		||||
  String get emptyChat => Intl.message("Empty chat");
 | 
			
		||||
 | 
			
		||||
  String get enableEncryptionWarning => Intl.message(
 | 
			
		||||
@ -482,6 +496,8 @@ class L10n extends MatrixLocalizations {
 | 
			
		||||
 | 
			
		||||
  String get none => Intl.message("None");
 | 
			
		||||
 | 
			
		||||
  String get noEmotesFound => Intl.message('No emotes found. 😕');
 | 
			
		||||
 | 
			
		||||
  String get noPermission => Intl.message("No permission");
 | 
			
		||||
 | 
			
		||||
  String get noRoomsFound => Intl.message("No rooms found...");
 | 
			
		||||
@ -491,6 +507,8 @@ class L10n extends MatrixLocalizations {
 | 
			
		||||
  String numberSelected(String number) =>
 | 
			
		||||
      Intl.message("$number selected", name: "numberSelected", args: [number]);
 | 
			
		||||
 | 
			
		||||
  String get ok => Intl.message('ok');
 | 
			
		||||
 | 
			
		||||
  String get oopsSomethingWentWrong =>
 | 
			
		||||
      Intl.message("Oops something went wrong...");
 | 
			
		||||
 | 
			
		||||
@ -505,6 +523,8 @@ class L10n extends MatrixLocalizations {
 | 
			
		||||
 | 
			
		||||
  String get password => Intl.message("Password");
 | 
			
		||||
 | 
			
		||||
  String get pickImage => Intl.message('Pick image');
 | 
			
		||||
 | 
			
		||||
  String play(String fileName) => Intl.message(
 | 
			
		||||
        "Play $fileName",
 | 
			
		||||
        name: "play",
 | 
			
		||||
 | 
			
		||||
@ -193,6 +193,8 @@ class MessageLookup extends MessageLookupByLibrary {
 | 
			
		||||
    "Download file" : MessageLookupByLibrary.simpleMessage("Datei herunterladen"),
 | 
			
		||||
    "Edit Jitsi instance" : MessageLookupByLibrary.simpleMessage("Jitsi Instanz ändern"),
 | 
			
		||||
    "Edit displayname" : MessageLookupByLibrary.simpleMessage("Anzeigename ändern"),
 | 
			
		||||
    "Emote Settings" : MessageLookupByLibrary.simpleMessage("Emote Einstellungen"),
 | 
			
		||||
    "Emote shortcode" : MessageLookupByLibrary.simpleMessage("Emote kürzel"),
 | 
			
		||||
    "Empty chat" : MessageLookupByLibrary.simpleMessage("Leerer Chat"),
 | 
			
		||||
    "Encryption algorithm" : MessageLookupByLibrary.simpleMessage("Verschlüsselungsalgorithmus"),
 | 
			
		||||
    "Encryption is not enabled" : MessageLookupByLibrary.simpleMessage("Verschlüsselung ist nicht aktiviert"),
 | 
			
		||||
@ -241,6 +243,7 @@ class MessageLookup extends MessageLookupByLibrary {
 | 
			
		||||
    "Mute chat" : MessageLookupByLibrary.simpleMessage("Stummschalten"),
 | 
			
		||||
    "New message in FluffyChat" : MessageLookupByLibrary.simpleMessage("Neue Nachricht in FluffyChat"),
 | 
			
		||||
    "New private chat" : MessageLookupByLibrary.simpleMessage("Neuer privater Chat"),
 | 
			
		||||
    "No emotes found. 😕" : MessageLookupByLibrary.simpleMessage("Keine Emotes gefunden. 😕"),
 | 
			
		||||
    "No permission" : MessageLookupByLibrary.simpleMessage("Keine Berechtigung"),
 | 
			
		||||
    "No rooms found..." : MessageLookupByLibrary.simpleMessage("Keine Räume gefunden ..."),
 | 
			
		||||
    "None" : MessageLookupByLibrary.simpleMessage("Keiner"),
 | 
			
		||||
@ -249,6 +252,7 @@ class MessageLookup extends MessageLookupByLibrary {
 | 
			
		||||
    "Open camera" : MessageLookupByLibrary.simpleMessage("Kamera öffnen"),
 | 
			
		||||
    "Participating user devices" : MessageLookupByLibrary.simpleMessage("Teilnehmende Geräte"),
 | 
			
		||||
    "Password" : MessageLookupByLibrary.simpleMessage("Passwort"),
 | 
			
		||||
    "Pick image" : MessageLookupByLibrary.simpleMessage("Wähle Bild"),
 | 
			
		||||
    "Please be aware that you need Pantalaimon to use end-to-end encryption for now." : MessageLookupByLibrary.simpleMessage("Bitte beachte, dass du Pantalaimon brauchst, um Ende-zu-Ende-Verschlüsselung benutzen zu können."),
 | 
			
		||||
    "Please choose a username" : MessageLookupByLibrary.simpleMessage("Bitte wähle einen Benutzernamen"),
 | 
			
		||||
    "Please enter a matrix identifier" : MessageLookupByLibrary.simpleMessage("Bitte eine Matrix ID eingeben"),
 | 
			
		||||
@ -339,6 +343,9 @@ class MessageLookup extends MessageLookupByLibrary {
 | 
			
		||||
    "dateAndTimeOfDay" : m21,
 | 
			
		||||
    "dateWithYear" : m22,
 | 
			
		||||
    "dateWithoutYear" : m23,
 | 
			
		||||
    "emoteExists" : MessageLookupByLibrary.simpleMessage("Emote existiert bereits!"),
 | 
			
		||||
    "emoteInvalid" : MessageLookupByLibrary.simpleMessage("Ungültiges Emote-kürzel!"),
 | 
			
		||||
    "emoteWarnNeedToPick" : MessageLookupByLibrary.simpleMessage("Wähle ein Emote-kürzel und ein Bild!"),
 | 
			
		||||
    "groupWith" : m24,
 | 
			
		||||
    "hasWithdrawnTheInvitationFor" : m25,
 | 
			
		||||
    "inviteContactToGroup" : m26,
 | 
			
		||||
@ -352,6 +359,7 @@ class MessageLookup extends MessageLookupByLibrary {
 | 
			
		||||
    "loadCountMoreParticipants" : m33,
 | 
			
		||||
    "logInTo" : m34,
 | 
			
		||||
    "numberSelected" : m35,
 | 
			
		||||
    "ok" : MessageLookupByLibrary.simpleMessage("ok"),
 | 
			
		||||
    "play" : m36,
 | 
			
		||||
    "redactedAnEvent" : m37,
 | 
			
		||||
    "rejectedTheInvitation" : m38,
 | 
			
		||||
 | 
			
		||||
@ -193,6 +193,8 @@ class MessageLookup extends MessageLookupByLibrary {
 | 
			
		||||
    "Download file" : MessageLookupByLibrary.simpleMessage("Download file"),
 | 
			
		||||
    "Edit Jitsi instance" : MessageLookupByLibrary.simpleMessage("Edit Jitsi instance"),
 | 
			
		||||
    "Edit displayname" : MessageLookupByLibrary.simpleMessage("Edit displayname"),
 | 
			
		||||
    "Emote Settings" : MessageLookupByLibrary.simpleMessage("Emote Settings"),
 | 
			
		||||
    "Emote shortcode" : MessageLookupByLibrary.simpleMessage("Emote shortcode"),
 | 
			
		||||
    "Empty chat" : MessageLookupByLibrary.simpleMessage("Empty chat"),
 | 
			
		||||
    "Encryption algorithm" : MessageLookupByLibrary.simpleMessage("Encryption algorithm"),
 | 
			
		||||
    "Encryption is not enabled" : MessageLookupByLibrary.simpleMessage("Encryption is not enabled"),
 | 
			
		||||
@ -242,6 +244,7 @@ class MessageLookup extends MessageLookupByLibrary {
 | 
			
		||||
    "Mute chat" : MessageLookupByLibrary.simpleMessage("Mute chat"),
 | 
			
		||||
    "New message in FluffyChat" : MessageLookupByLibrary.simpleMessage("New message in FluffyChat"),
 | 
			
		||||
    "New private chat" : MessageLookupByLibrary.simpleMessage("New private chat"),
 | 
			
		||||
    "No emotes found. 😕" : MessageLookupByLibrary.simpleMessage("No emotes found. 😕"),
 | 
			
		||||
    "No permission" : MessageLookupByLibrary.simpleMessage("No permission"),
 | 
			
		||||
    "No rooms found..." : MessageLookupByLibrary.simpleMessage("No rooms found..."),
 | 
			
		||||
    "None" : MessageLookupByLibrary.simpleMessage("None"),
 | 
			
		||||
@ -251,6 +254,7 @@ class MessageLookup extends MessageLookupByLibrary {
 | 
			
		||||
    "Open camera" : MessageLookupByLibrary.simpleMessage("Open camera"),
 | 
			
		||||
    "Participating user devices" : MessageLookupByLibrary.simpleMessage("Participating user devices"),
 | 
			
		||||
    "Password" : MessageLookupByLibrary.simpleMessage("Password"),
 | 
			
		||||
    "Pick image" : MessageLookupByLibrary.simpleMessage("Pick image"),
 | 
			
		||||
    "Please be aware that you need Pantalaimon to use end-to-end encryption for now." : MessageLookupByLibrary.simpleMessage("Please be aware that you need Pantalaimon to use end-to-end encryption for now."),
 | 
			
		||||
    "Please choose a username" : MessageLookupByLibrary.simpleMessage("Please choose a username"),
 | 
			
		||||
    "Please enter a matrix identifier" : MessageLookupByLibrary.simpleMessage("Please enter a matrix identifier"),
 | 
			
		||||
@ -341,6 +345,9 @@ class MessageLookup extends MessageLookupByLibrary {
 | 
			
		||||
    "dateAndTimeOfDay" : m21,
 | 
			
		||||
    "dateWithYear" : m22,
 | 
			
		||||
    "dateWithoutYear" : m23,
 | 
			
		||||
    "emoteExists" : MessageLookupByLibrary.simpleMessage("Emote already exists!"),
 | 
			
		||||
    "emoteInvalid" : MessageLookupByLibrary.simpleMessage("Invalid emote shortcode!"),
 | 
			
		||||
    "emoteWarnNeedToPick" : MessageLookupByLibrary.simpleMessage("You need to pick an emote shortcode and an image!"),
 | 
			
		||||
    "groupWith" : m24,
 | 
			
		||||
    "hasWithdrawnTheInvitationFor" : m25,
 | 
			
		||||
    "inviteContactToGroup" : m26,
 | 
			
		||||
@ -354,6 +361,7 @@ class MessageLookup extends MessageLookupByLibrary {
 | 
			
		||||
    "loadCountMoreParticipants" : m33,
 | 
			
		||||
    "logInTo" : m34,
 | 
			
		||||
    "numberSelected" : m35,
 | 
			
		||||
    "ok" : MessageLookupByLibrary.simpleMessage("ok"),
 | 
			
		||||
    "play" : m36,
 | 
			
		||||
    "redactedAnEvent" : m37,
 | 
			
		||||
    "rejectedTheInvitation" : m38,
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,7 @@ import 'package:flutter/services.dart';
 | 
			
		||||
import 'package:flutter_styled_toast/flutter_styled_toast.dart';
 | 
			
		||||
import 'package:image_picker/image_picker.dart';
 | 
			
		||||
import 'package:link_text/link_text.dart';
 | 
			
		||||
import './settings_emotes.dart';
 | 
			
		||||
 | 
			
		||||
class ChatDetails extends StatefulWidget {
 | 
			
		||||
  final Room room;
 | 
			
		||||
@ -274,6 +275,22 @@ class _ChatDetailsState extends State<ChatDetails> {
 | 
			
		||||
                                        ? widget.room.canonicalAlias
 | 
			
		||||
                                        : L10n.of(context).none),
 | 
			
		||||
                              ),
 | 
			
		||||
                            ListTile(
 | 
			
		||||
                              leading: CircleAvatar(
 | 
			
		||||
                                backgroundColor:
 | 
			
		||||
                                    Theme.of(context).scaffoldBackgroundColor,
 | 
			
		||||
                                foregroundColor: Colors.grey,
 | 
			
		||||
                                child: Icon(Icons.insert_emoticon),
 | 
			
		||||
                              ),
 | 
			
		||||
                              title: Text(L10n.of(context).emoteSettings),
 | 
			
		||||
                              onTap: () async =>
 | 
			
		||||
                                  await Navigator.of(context).push(
 | 
			
		||||
                                AppRoute.defaultRoute(
 | 
			
		||||
                                  context,
 | 
			
		||||
                                  EmotesSettingsView(room: widget.room),
 | 
			
		||||
                                ),
 | 
			
		||||
                              ),
 | 
			
		||||
                            ),
 | 
			
		||||
                            PopupMenuButton(
 | 
			
		||||
                              child: ListTile(
 | 
			
		||||
                                leading: CircleAvatar(
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,7 @@ import '../components/content_banner.dart';
 | 
			
		||||
import '../components/matrix.dart';
 | 
			
		||||
import '../l10n/l10n.dart';
 | 
			
		||||
import '../utils/app_route.dart';
 | 
			
		||||
import 'settings_emotes.dart';
 | 
			
		||||
 | 
			
		||||
class SettingsView extends StatelessWidget {
 | 
			
		||||
  @override
 | 
			
		||||
@ -226,11 +227,22 @@ class _SettingsState extends State<Settings> {
 | 
			
		||||
                activeColor: Theme.of(context).primaryColor,
 | 
			
		||||
                onChanged: (bool newValue) async {
 | 
			
		||||
                  Matrix.of(context).renderHtml = newValue;
 | 
			
		||||
                  await client.storeAPI.setItem("chat.fluffy.renderHtml", newValue ? "1" : "0");
 | 
			
		||||
                  await client.storeAPI
 | 
			
		||||
                      .setItem("chat.fluffy.renderHtml", newValue ? "1" : "0");
 | 
			
		||||
                  setState(() => null);
 | 
			
		||||
                },
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              title: Text(L10n.of(context).emoteSettings),
 | 
			
		||||
              onTap: () async => await Navigator.of(context).push(
 | 
			
		||||
                AppRoute.defaultRoute(
 | 
			
		||||
                  context,
 | 
			
		||||
                  EmotesSettingsView(),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              trailing: Icon(Icons.insert_emoticon),
 | 
			
		||||
            ),
 | 
			
		||||
            Divider(thickness: 1),
 | 
			
		||||
            ListTile(
 | 
			
		||||
              title: Text(
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										398
									
								
								lib/views/settings_emotes.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										398
									
								
								lib/views/settings_emotes.dart
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,398 @@
 | 
			
		||||
import 'dart:io';
 | 
			
		||||
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter/foundation.dart';
 | 
			
		||||
import 'package:flutter_advanced_networkimage/provider.dart';
 | 
			
		||||
import 'package:famedlysdk/famedlysdk.dart';
 | 
			
		||||
import 'package:image_picker/image_picker.dart';
 | 
			
		||||
import 'package:flutter_styled_toast/flutter_styled_toast.dart';
 | 
			
		||||
 | 
			
		||||
import 'chat_list.dart';
 | 
			
		||||
import '../components/adaptive_page_layout.dart';
 | 
			
		||||
import '../components/matrix.dart';
 | 
			
		||||
import '../components/dialogs/simple_dialogs.dart';
 | 
			
		||||
import '../l10n/l10n.dart';
 | 
			
		||||
 | 
			
		||||
class EmotesSettingsView extends StatelessWidget {
 | 
			
		||||
  final Room room;
 | 
			
		||||
 | 
			
		||||
  EmotesSettingsView({this.room});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return AdaptivePageLayout(
 | 
			
		||||
      primaryPage: FocusPage.SECOND,
 | 
			
		||||
      firstScaffold: ChatList(),
 | 
			
		||||
      secondScaffold: EmotesSettings(room: room),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class EmotesSettings extends StatefulWidget {
 | 
			
		||||
  final Room room;
 | 
			
		||||
 | 
			
		||||
  EmotesSettings({this.room});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  _EmotesSettingsState createState() => _EmotesSettingsState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _EmoteEntry {
 | 
			
		||||
  String emote;
 | 
			
		||||
  String mxc;
 | 
			
		||||
  _EmoteEntry({this.emote, this.mxc});
 | 
			
		||||
 | 
			
		||||
  String get emoteClean => emote.substring(1, emote.length - 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _EmotesSettingsState extends State<EmotesSettings> {
 | 
			
		||||
  List<_EmoteEntry> emotes;
 | 
			
		||||
  bool showSave = false;
 | 
			
		||||
  TextEditingController newEmoteController = TextEditingController();
 | 
			
		||||
  TextEditingController newMxcController = TextEditingController();
 | 
			
		||||
 | 
			
		||||
  Future<void> _save(BuildContext context) async {
 | 
			
		||||
    if (readonly) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    debugPrint("Saving....");
 | 
			
		||||
    final client = Matrix.of(context).client;
 | 
			
		||||
    // be sure to preserve any data not in "short"
 | 
			
		||||
    Map<String, dynamic> content;
 | 
			
		||||
    if (widget.room != null) {
 | 
			
		||||
      content = widget.room.getState('im.ponies.room_emotes')?.content ??
 | 
			
		||||
          <String, dynamic>{};
 | 
			
		||||
    } else {
 | 
			
		||||
      content = client.accountData['im.ponies.user_emotes']?.content ??
 | 
			
		||||
          <String, dynamic>{};
 | 
			
		||||
    }
 | 
			
		||||
    debugPrint(content.toString());
 | 
			
		||||
    content['short'] = <String, String>{};
 | 
			
		||||
    for (final emote in emotes) {
 | 
			
		||||
      content['short'][emote.emote] = emote.mxc;
 | 
			
		||||
    }
 | 
			
		||||
    debugPrint(content.toString());
 | 
			
		||||
    var path = '';
 | 
			
		||||
    if (widget.room != null) {
 | 
			
		||||
      path = '/client/r0/rooms/${widget.room.id}/state/im.ponies.room_emotes/';
 | 
			
		||||
    } else {
 | 
			
		||||
      path =
 | 
			
		||||
          '/client/r0/user/${client.userID}/account_data/im.ponies.user_emotes';
 | 
			
		||||
    }
 | 
			
		||||
    debugPrint(path);
 | 
			
		||||
    await SimpleDialogs(context).tryRequestWithLoadingDialog(
 | 
			
		||||
      client.jsonRequest(
 | 
			
		||||
        type: HTTPType.PUT,
 | 
			
		||||
        action: path,
 | 
			
		||||
        data: content,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool get readonly => widget.room == null
 | 
			
		||||
      ? false
 | 
			
		||||
      : !(widget.room.canSendEvent('im.ponies.room_emotes'));
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    Client client = Matrix.of(context).client;
 | 
			
		||||
    if (emotes == null) {
 | 
			
		||||
      emotes = <_EmoteEntry>[];
 | 
			
		||||
      Map<String, dynamic> emoteSource;
 | 
			
		||||
      if (widget.room != null) {
 | 
			
		||||
        emoteSource = widget.room.getState('im.ponies.room_emotes')?.content;
 | 
			
		||||
      } else {
 | 
			
		||||
        emoteSource = client.accountData['im.ponies.user_emotes']?.content;
 | 
			
		||||
      }
 | 
			
		||||
      if (emoteSource != null && emoteSource['short'] is Map) {
 | 
			
		||||
        emoteSource['short'].forEach((key, value) {
 | 
			
		||||
          if (key is String && value is String && value.startsWith('mxc://')) {
 | 
			
		||||
            emotes.add(_EmoteEntry(emote: key, mxc: value));
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return Scaffold(
 | 
			
		||||
      appBar: AppBar(
 | 
			
		||||
        title: Text(L10n.of(context).emoteSettings),
 | 
			
		||||
      ),
 | 
			
		||||
      floatingActionButton: showSave
 | 
			
		||||
          ? FloatingActionButton(
 | 
			
		||||
              child: Icon(Icons.save, color: Colors.white),
 | 
			
		||||
              onPressed: () async {
 | 
			
		||||
                await _save(context);
 | 
			
		||||
                setState(() {
 | 
			
		||||
                  showSave = false;
 | 
			
		||||
                });
 | 
			
		||||
              },
 | 
			
		||||
              backgroundColor: Theme.of(context).primaryColor,
 | 
			
		||||
            )
 | 
			
		||||
          : null,
 | 
			
		||||
      body: StreamBuilder(
 | 
			
		||||
          stream: widget.room?.onUpdate?.stream,
 | 
			
		||||
          builder: (context, snapshot) {
 | 
			
		||||
            return Column(
 | 
			
		||||
              children: <Widget>[
 | 
			
		||||
                if (!readonly)
 | 
			
		||||
                  Container(
 | 
			
		||||
                    child: ListTile(
 | 
			
		||||
                      leading: Container(
 | 
			
		||||
                        width: 180.0,
 | 
			
		||||
                        height: 38,
 | 
			
		||||
                        padding: EdgeInsets.symmetric(horizontal: 8),
 | 
			
		||||
                        decoration: BoxDecoration(
 | 
			
		||||
                          borderRadius: BorderRadius.all(Radius.circular(10)),
 | 
			
		||||
                          color: Theme.of(context).secondaryHeaderColor,
 | 
			
		||||
                        ),
 | 
			
		||||
                        child: TextField(
 | 
			
		||||
                          controller: newEmoteController,
 | 
			
		||||
                          autocorrect: false,
 | 
			
		||||
                          minLines: 1,
 | 
			
		||||
                          maxLines: 1,
 | 
			
		||||
                          decoration: InputDecoration(
 | 
			
		||||
                            hintText: L10n.of(context).emoteShortcode,
 | 
			
		||||
                            prefixText: ': ',
 | 
			
		||||
                            suffixText: ':',
 | 
			
		||||
                            prefixStyle: TextStyle(
 | 
			
		||||
                              color: Theme.of(context).primaryColor,
 | 
			
		||||
                              fontWeight: FontWeight.bold,
 | 
			
		||||
                            ),
 | 
			
		||||
                            suffixStyle: TextStyle(
 | 
			
		||||
                              color: Theme.of(context).primaryColor,
 | 
			
		||||
                              fontWeight: FontWeight.bold,
 | 
			
		||||
                            ),
 | 
			
		||||
                            border: InputBorder.none,
 | 
			
		||||
                          ),
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                      title: _EmoteImagePicker(newMxcController),
 | 
			
		||||
                      trailing: InkWell(
 | 
			
		||||
                        child: Icon(
 | 
			
		||||
                          Icons.add,
 | 
			
		||||
                          color: Colors.green,
 | 
			
		||||
                          size: 32.0,
 | 
			
		||||
                        ),
 | 
			
		||||
                        onTap: () async {
 | 
			
		||||
                          debugPrint("blah");
 | 
			
		||||
                          if (newEmoteController.text == null ||
 | 
			
		||||
                              newEmoteController.text.isEmpty ||
 | 
			
		||||
                              newMxcController.text == null ||
 | 
			
		||||
                              newMxcController.text.isEmpty) {
 | 
			
		||||
                            await SimpleDialogs(context).inform(
 | 
			
		||||
                                contentText:
 | 
			
		||||
                                    L10n.of(context).emoteWarnNeedToPick);
 | 
			
		||||
                            return;
 | 
			
		||||
                          }
 | 
			
		||||
                          final emoteCode = ':${newEmoteController.text}:';
 | 
			
		||||
                          final mxc = newMxcController.text;
 | 
			
		||||
                          if (emotes.indexWhere((e) =>
 | 
			
		||||
                                  e.emote == emoteCode && e.mxc != mxc) !=
 | 
			
		||||
                              -1) {
 | 
			
		||||
                            await SimpleDialogs(context).inform(
 | 
			
		||||
                                contentText: L10n.of(context).emoteExists);
 | 
			
		||||
                            return;
 | 
			
		||||
                          }
 | 
			
		||||
                          if (!RegExp(r'^:[-\w]+:$').hasMatch(emoteCode)) {
 | 
			
		||||
                            await SimpleDialogs(context).inform(
 | 
			
		||||
                                contentText: L10n.of(context).emoteInvalid);
 | 
			
		||||
                            return;
 | 
			
		||||
                          }
 | 
			
		||||
                          emotes.add(_EmoteEntry(emote: emoteCode, mxc: mxc));
 | 
			
		||||
                          await _save(context);
 | 
			
		||||
                          setState(() {
 | 
			
		||||
                            newEmoteController.text = '';
 | 
			
		||||
                            newMxcController.text = '';
 | 
			
		||||
                            showSave = false;
 | 
			
		||||
                          });
 | 
			
		||||
                        },
 | 
			
		||||
                      ),
 | 
			
		||||
                    ),
 | 
			
		||||
                    padding: EdgeInsets.symmetric(
 | 
			
		||||
                      vertical: 8.0,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                if (!readonly)
 | 
			
		||||
                  Divider(
 | 
			
		||||
                    height: 2,
 | 
			
		||||
                    thickness: 2,
 | 
			
		||||
                    color: Theme.of(context).primaryColor,
 | 
			
		||||
                  ),
 | 
			
		||||
                Expanded(
 | 
			
		||||
                  child: emotes.isEmpty
 | 
			
		||||
                      ? Center(
 | 
			
		||||
                          child: Padding(
 | 
			
		||||
                            padding: EdgeInsets.all(16),
 | 
			
		||||
                            child: Text(
 | 
			
		||||
                              L10n.of(context).noEmotesFound,
 | 
			
		||||
                              style: TextStyle(fontSize: 20),
 | 
			
		||||
                            ),
 | 
			
		||||
                          ),
 | 
			
		||||
                        )
 | 
			
		||||
                      : ListView.separated(
 | 
			
		||||
                          separatorBuilder: (BuildContext context, int i) =>
 | 
			
		||||
                              Container(),
 | 
			
		||||
                          itemCount: emotes.length + 1,
 | 
			
		||||
                          itemBuilder: (BuildContext context, int i) {
 | 
			
		||||
                            if (i >= emotes.length) {
 | 
			
		||||
                              return Container(height: 70);
 | 
			
		||||
                            }
 | 
			
		||||
                            final emote = emotes[i];
 | 
			
		||||
                            final controller = TextEditingController();
 | 
			
		||||
                            controller.text = emote.emoteClean;
 | 
			
		||||
                            return ListTile(
 | 
			
		||||
                              leading: Container(
 | 
			
		||||
                                width: 180.0,
 | 
			
		||||
                                height: 38,
 | 
			
		||||
                                padding: EdgeInsets.symmetric(horizontal: 8),
 | 
			
		||||
                                decoration: BoxDecoration(
 | 
			
		||||
                                  borderRadius:
 | 
			
		||||
                                      BorderRadius.all(Radius.circular(10)),
 | 
			
		||||
                                  color: Theme.of(context).secondaryHeaderColor,
 | 
			
		||||
                                ),
 | 
			
		||||
                                child: TextField(
 | 
			
		||||
                                  readOnly: readonly,
 | 
			
		||||
                                  controller: controller,
 | 
			
		||||
                                  autocorrect: false,
 | 
			
		||||
                                  minLines: 1,
 | 
			
		||||
                                  maxLines: 1,
 | 
			
		||||
                                  decoration: InputDecoration(
 | 
			
		||||
                                    hintText: L10n.of(context).emoteShortcode,
 | 
			
		||||
                                    prefixText: ': ',
 | 
			
		||||
                                    suffixText: ':',
 | 
			
		||||
                                    prefixStyle: TextStyle(
 | 
			
		||||
                                      color: Theme.of(context).primaryColor,
 | 
			
		||||
                                      fontWeight: FontWeight.bold,
 | 
			
		||||
                                    ),
 | 
			
		||||
                                    suffixStyle: TextStyle(
 | 
			
		||||
                                      color: Theme.of(context).primaryColor,
 | 
			
		||||
                                      fontWeight: FontWeight.bold,
 | 
			
		||||
                                    ),
 | 
			
		||||
                                    border: InputBorder.none,
 | 
			
		||||
                                  ),
 | 
			
		||||
                                  onSubmitted: (s) {
 | 
			
		||||
                                    final emoteCode = ':${s}:';
 | 
			
		||||
                                    if (emotes.indexWhere((e) =>
 | 
			
		||||
                                            e.emote == emoteCode &&
 | 
			
		||||
                                            e.mxc != emote.mxc) !=
 | 
			
		||||
                                        -1) {
 | 
			
		||||
                                      controller.text = emote.emoteClean;
 | 
			
		||||
                                      SimpleDialogs(context).inform(
 | 
			
		||||
                                          contentText:
 | 
			
		||||
                                              L10n.of(context).emoteExists);
 | 
			
		||||
                                      return;
 | 
			
		||||
                                    }
 | 
			
		||||
                                    if (!RegExp(r'^:[-\w]+:$')
 | 
			
		||||
                                        .hasMatch(emoteCode)) {
 | 
			
		||||
                                      controller.text = emote.emoteClean;
 | 
			
		||||
                                      SimpleDialogs(context).inform(
 | 
			
		||||
                                          contentText:
 | 
			
		||||
                                              L10n.of(context).emoteInvalid);
 | 
			
		||||
                                      return;
 | 
			
		||||
                                    }
 | 
			
		||||
                                    setState(() {
 | 
			
		||||
                                      emote.emote = emoteCode;
 | 
			
		||||
                                      showSave = true;
 | 
			
		||||
                                    });
 | 
			
		||||
                                  },
 | 
			
		||||
                                ),
 | 
			
		||||
                              ),
 | 
			
		||||
                              title: _EmoteImage(emote.mxc),
 | 
			
		||||
                              trailing: readonly
 | 
			
		||||
                                  ? null
 | 
			
		||||
                                  : InkWell(
 | 
			
		||||
                                      child: Icon(
 | 
			
		||||
                                        Icons.delete_forever,
 | 
			
		||||
                                        color: Colors.red,
 | 
			
		||||
                                        size: 32.0,
 | 
			
		||||
                                      ),
 | 
			
		||||
                                      onTap: () => setState(() {
 | 
			
		||||
                                        emotes.removeWhere(
 | 
			
		||||
                                            (e) => e.emote == emote.emote);
 | 
			
		||||
                                        showSave = true;
 | 
			
		||||
                                      }),
 | 
			
		||||
                                    ),
 | 
			
		||||
                            );
 | 
			
		||||
                          },
 | 
			
		||||
                        ),
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            );
 | 
			
		||||
          }),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _EmoteImage extends StatelessWidget {
 | 
			
		||||
  final String mxc;
 | 
			
		||||
  _EmoteImage(this.mxc);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    final size = 38.0;
 | 
			
		||||
    final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
 | 
			
		||||
    final url = Uri.parse(mxc)?.getThumbnail(
 | 
			
		||||
      Matrix.of(context).client,
 | 
			
		||||
      width: size * devicePixelRatio,
 | 
			
		||||
      height: size * devicePixelRatio,
 | 
			
		||||
      method: ThumbnailMethod.scale,
 | 
			
		||||
    );
 | 
			
		||||
    return Image(
 | 
			
		||||
      image: AdvancedNetworkImage(
 | 
			
		||||
        url,
 | 
			
		||||
        useDiskCache: !kIsWeb,
 | 
			
		||||
      ),
 | 
			
		||||
      fit: BoxFit.contain,
 | 
			
		||||
      width: size,
 | 
			
		||||
      height: size,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _EmoteImagePicker extends StatefulWidget {
 | 
			
		||||
  final TextEditingController controller;
 | 
			
		||||
 | 
			
		||||
  _EmoteImagePicker(this.controller);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  _EmoteImagePickerState createState() => _EmoteImagePickerState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _EmoteImagePickerState extends State<_EmoteImagePicker> {
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    if (widget.controller.text == null || widget.controller.text.isEmpty) {
 | 
			
		||||
      return RaisedButton(
 | 
			
		||||
        color: Theme.of(context).primaryColor,
 | 
			
		||||
        elevation: 5,
 | 
			
		||||
        textColor: Colors.white,
 | 
			
		||||
        child: Text(L10n.of(context).pickImage),
 | 
			
		||||
        shape: RoundedRectangleBorder(
 | 
			
		||||
          borderRadius: BorderRadius.circular(10.0),
 | 
			
		||||
        ),
 | 
			
		||||
        onPressed: () async {
 | 
			
		||||
          if (kIsWeb) {
 | 
			
		||||
            showToast(L10n.of(context).notSupportedInWeb);
 | 
			
		||||
            return;
 | 
			
		||||
          }
 | 
			
		||||
          File file = await ImagePicker.pickImage(
 | 
			
		||||
              source: ImageSource.gallery,
 | 
			
		||||
              imageQuality: 50,
 | 
			
		||||
              maxWidth: 128,
 | 
			
		||||
              maxHeight: 128);
 | 
			
		||||
          if (file == null) return;
 | 
			
		||||
          final uploadResp =
 | 
			
		||||
              await SimpleDialogs(context).tryRequestWithLoadingDialog(
 | 
			
		||||
            Matrix.of(context).client.upload(
 | 
			
		||||
                  MatrixFile(bytes: await file.readAsBytes(), path: file.path),
 | 
			
		||||
                ),
 | 
			
		||||
          );
 | 
			
		||||
          setState(() {
 | 
			
		||||
            widget.controller.text = uploadResp;
 | 
			
		||||
          });
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      return _EmoteImage(widget.controller.text);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user