mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-12-25 06:52:35 +01:00
Merge branch 'soru/emotepicker' into 'master'
add basic emote picker See merge request ChristianPauly/fluffychat-flutter!45
This commit is contained in:
commit
e736d32322
@ -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…
Reference in New Issue
Block a user