chore: Update image pack file format to match that of the emote msc

This commit is contained in:
Sorunome 2021-07-04 15:45:21 +02:00
parent aa0033c0a6
commit b05189230f
No known key found for this signature in database
GPG Key ID: B19471D07FC9BE9C
5 changed files with 128 additions and 105 deletions

View File

@ -24,8 +24,6 @@ class EmoteEntry {
String emote; String emote;
String mxc; String mxc;
EmoteEntry({this.emote, this.mxc}); EmoteEntry({this.emote, this.mxc});
String get emoteClean => emote.substring(1, emote.length - 1);
} }
class EmotesSettingsController extends State<EmotesSettings> { class EmotesSettingsController extends State<EmotesSettings> {
@ -39,54 +37,57 @@ class EmotesSettingsController extends State<EmotesSettings> {
TextEditingController newEmoteController = TextEditingController(); TextEditingController newEmoteController = TextEditingController();
TextEditingController newMxcController = TextEditingController(); TextEditingController newMxcController = TextEditingController();
ImagePackContent _getPack(BuildContext context) {
final client = Matrix.of(context).client;
final event = (room != null
? room.getState('im.ponies.room_emotes', stateKey ?? '')
: client.accountData['im.ponies.user_emotes']) ??
BasicEvent.fromJson(<String, dynamic>{
'type': 'm.dummy',
'content': <String, dynamic>{},
});
// make sure we work on a *copy* of the event
return BasicEvent.fromJson(event.toJson()).parsedImagePackContent;
}
Future<void> _save(BuildContext context) async { Future<void> _save(BuildContext context) async {
if (readonly) { if (readonly) {
return; return;
} }
final client = Matrix.of(context).client; final client = Matrix.of(context).client;
// be sure to preserve any data not in "short" final pack = _getPack(context);
Map<String, dynamic> content;
if (room != null) {
content =
room.getState('im.ponies.room_emotes', stateKey ?? '')?.content ??
<String, dynamic>{};
} else {
content = client.accountData['im.ponies.user_emotes']?.content ??
<String, dynamic>{};
}
if (!(content['emoticons'] is Map)) {
content['emoticons'] = <String, dynamic>{};
}
// add / update changed emotes // add / update changed emotes
final allowedShortcodes = <String>{}; final allowedShortcodes = <String>{};
for (final emote in emotes) { for (final emote in emotes) {
allowedShortcodes.add(emote.emote); allowedShortcodes.add(emote.emote);
if (!(content['emoticons'][emote.emote] is Map)) { if (pack.images.containsKey(emote.emote)) {
content['emoticons'][emote.emote] = <String, dynamic>{}; pack.images[emote.emote].url = Uri.parse(emote.mxc);
} else {
pack.images[emote.emote] =
ImagePackImageContent.fromJson(<String, dynamic>{
'url': emote.mxc,
});
} }
content['emoticons'][emote.emote]['url'] = emote.mxc;
} }
// remove emotes no more needed // remove emotes no more needed
// we make the iterator .toList() here so that we don't get into trouble modifying the very // we make the iterator .toList() here so that we don't get into trouble modifying the very
// thing we are iterating over // thing we are iterating over
for (final shortcode in content['emoticons'].keys.toList()) { for (final shortcode in pack.images.keys.toList()) {
if (!allowedShortcodes.contains(shortcode)) { if (!allowedShortcodes.contains(shortcode)) {
content['emoticons'].remove(shortcode); pack.images.remove(shortcode);
} }
} }
// remove the old "short" key
content.remove('short');
if (room != null) { if (room != null) {
await showFutureLoadingDialog( await showFutureLoadingDialog(
context: context, context: context,
future: () => client.setRoomStateWithKey( future: () => client.setRoomStateWithKey(
room.id, 'im.ponies.room_emotes', content, stateKey ?? ''), room.id, 'im.ponies.room_emotes', pack.toJson(), stateKey ?? ''),
); );
} else { } else {
await showFutureLoadingDialog( await showFutureLoadingDialog(
context: context, context: context,
future: () => client.setAccountData( future: () => client.setAccountData(
client.userID, 'im.ponies.user_emotes', content), client.userID, 'im.ponies.user_emotes', pack.toJson()),
); );
} }
} }
@ -126,14 +127,13 @@ class EmotesSettingsController extends State<EmotesSettings> {
}); });
void submitEmoteAction( void submitEmoteAction(
String s, String emoteCode,
EmoteEntry emote, EmoteEntry emote,
TextEditingController controller, TextEditingController controller,
) { ) {
final emoteCode = ':$s:';
if (emotes.indexWhere((e) => e.emote == emoteCode && e.mxc != emote.mxc) != if (emotes.indexWhere((e) => e.emote == emoteCode && e.mxc != emote.mxc) !=
-1) { -1) {
controller.text = emote.emoteClean; controller.text = emote.emote;
showOkAlertDialog( showOkAlertDialog(
useRootNavigator: false, useRootNavigator: false,
context: context, context: context,
@ -142,8 +142,8 @@ class EmotesSettingsController extends State<EmotesSettings> {
); );
return; return;
} }
if (!RegExp(r'^:[-\w]+:$').hasMatch(emoteCode)) { if (!RegExp(r'^[-\w]+$').hasMatch(emoteCode)) {
controller.text = emote.emoteClean; controller.text = emote.emote;
showOkAlertDialog( showOkAlertDialog(
useRootNavigator: false, useRootNavigator: false,
context: context, context: context,
@ -190,7 +190,7 @@ class EmotesSettingsController extends State<EmotesSettings> {
); );
return; return;
} }
final emoteCode = ':${newEmoteController.text}:'; final emoteCode = '${newEmoteController.text}';
final mxc = newMxcController.text; final mxc = newMxcController.text;
if (emotes.indexWhere((e) => e.emote == emoteCode && e.mxc != mxc) != -1) { if (emotes.indexWhere((e) => e.emote == emoteCode && e.mxc != mxc) != -1) {
await showOkAlertDialog( await showOkAlertDialog(
@ -201,7 +201,7 @@ class EmotesSettingsController extends State<EmotesSettings> {
); );
return; return;
} }
if (!RegExp(r'^:[-\w]+:$').hasMatch(emoteCode)) { if (!RegExp(r'^[-\w]+$').hasMatch(emoteCode)) {
await showOkAlertDialog( await showOkAlertDialog(
useRootNavigator: false, useRootNavigator: false,
context: context, context: context,
@ -262,35 +262,10 @@ class EmotesSettingsController extends State<EmotesSettings> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (emotes == null) { if (emotes == null) {
emotes = <EmoteEntry>[]; emotes = <EmoteEntry>[];
Map<String, dynamic> emoteSource; final pack = _getPack(context);
if (room != null) { for (final entry in pack.images.entries) {
emoteSource = emotes
room.getState('im.ponies.room_emotes', stateKey ?? '')?.content; .add(EmoteEntry(emote: entry.key, mxc: entry.value.url.toString()));
} else {
emoteSource = Matrix.of(context)
.client
.accountData['im.ponies.user_emotes']
?.content;
}
if (emoteSource != null) {
if (emoteSource['emoticons'] is Map) {
emoteSource['emoticons'].forEach((key, value) {
if (key is String &&
value is Map &&
value['url'] is String &&
value['url'].startsWith('mxc://')) {
emotes.add(EmoteEntry(emote: key, mxc: value['url']));
}
});
} else if (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 EmotesSettingsView(this); return EmotesSettingsView(this);

View File

@ -1,8 +1,10 @@
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:fluffychat/widgets/layouts/max_width_body.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import '../../widgets/matrix.dart'; import '../../widgets/matrix.dart';
import '../settings_emotes.dart'; import '../settings_emotes.dart';
@ -117,7 +119,9 @@ class EmotesSettingsView extends StatelessWidget {
final emote = controller.emotes[i]; final emote = controller.emotes[i];
final textEditingController = final textEditingController =
TextEditingController(); TextEditingController();
textEditingController.text = emote.emoteClean; textEditingController.text = emote.emote;
final useShortCuts = (PlatformInfos.isWeb ||
PlatformInfos.isDesktop);
return ListTile( return ListTile(
leading: Container( leading: Container(
width: 180.0, width: 180.0,
@ -129,6 +133,28 @@ class EmotesSettingsView extends StatelessWidget {
color: color:
Theme.of(context).secondaryHeaderColor, Theme.of(context).secondaryHeaderColor,
), ),
child: Shortcuts(
shortcuts: !useShortCuts
? {}
: {
LogicalKeySet(
LogicalKeyboardKey.enter):
SubmitLineIntent(),
},
child: Actions(
actions: !useShortCuts
? {}
: {
SubmitLineIntent:
CallbackAction(onInvoke: (i) {
controller.submitEmoteAction(
textEditingController.text,
emote,
textEditingController,
);
return null;
}),
},
child: TextField( child: TextField(
readOnly: controller.readonly, readOnly: controller.readonly,
controller: textEditingController, controller: textEditingController,
@ -136,7 +162,8 @@ class EmotesSettingsView extends StatelessWidget {
minLines: 1, minLines: 1,
maxLines: 1, maxLines: 1,
decoration: InputDecoration( decoration: InputDecoration(
hintText: L10n.of(context).emoteShortcode, hintText:
L10n.of(context).emoteShortcode,
prefixText: ': ', prefixText: ': ',
suffixText: ':', suffixText: ':',
prefixStyle: TextStyle( prefixStyle: TextStyle(
@ -161,6 +188,8 @@ class EmotesSettingsView extends StatelessWidget {
), ),
), ),
), ),
),
),
title: _EmoteImage(emote.mxc), title: _EmoteImage(emote.mxc),
trailing: controller.readonly trailing: controller.readonly
? null ? null
@ -232,3 +261,5 @@ class _EmoteImagePickerState extends State<_EmoteImagePicker> {
} }
} }
} }
class SubmitLineIntent extends Intent {}

View File

@ -63,16 +63,18 @@ class InputBar extends StatelessWidget {
if (emojiMatch != null) { if (emojiMatch != null) {
final packSearch = emojiMatch[1]; final packSearch = emojiMatch[1];
final emoteSearch = emojiMatch[2].toLowerCase(); final emoteSearch = emojiMatch[2].toLowerCase();
final emotePacks = room.emotePacks; final emotePacks = room.getImagePacks(ImagePackUsage.emoticon);
if (packSearch == null || packSearch.isEmpty) { if (packSearch == null || packSearch.isEmpty) {
for (final pack in emotePacks.entries) { for (final pack in emotePacks.entries) {
for (final emote in pack.value.entries) { for (final emote in pack.value.images.entries) {
if (emote.key.toLowerCase().contains(emoteSearch)) { if (emote.key.toLowerCase().contains(emoteSearch)) {
ret.add({ ret.add({
'type': 'emote', 'type': 'emote',
'name': emote.key, 'name': emote.key,
'pack': pack.key, 'pack': pack.key,
'mxc': emote.value, 'pack_avatar_url': pack.value.pack.avatarUrl?.toString(),
'pack_display_name': pack.value.pack.displayName ?? pack.key,
'mxc': emote.value.url.toString(),
}); });
} }
if (ret.length > maxResults) { if (ret.length > maxResults) {
@ -84,13 +86,17 @@ class InputBar extends StatelessWidget {
} }
} }
} else if (emotePacks[packSearch] != null) { } else if (emotePacks[packSearch] != null) {
for (final emote in emotePacks[packSearch].entries) { for (final emote in emotePacks[packSearch].images.entries) {
if (emote.key.toLowerCase().contains(emoteSearch)) { if (emote.key.toLowerCase().contains(emoteSearch)) {
ret.add({ ret.add({
'type': 'emote', 'type': 'emote',
'name': emote.key, 'name': emote.key,
'pack': packSearch, 'pack': packSearch,
'mxc': emote.value, 'pack_avatar_url':
emotePacks[packSearch].pack.avatarUrl?.toString(),
'pack_display_name':
emotePacks[packSearch].pack.displayName ?? packSearch,
'mxc': emote.value.url.toString(),
}); });
} }
if (ret.length > maxResults) { if (ret.length > maxResults) {
@ -239,8 +245,15 @@ class InputBar extends StatelessWidget {
child: Align( child: Align(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: Opacity( child: Opacity(
opacity: 0.5, opacity: suggestion['pack_avatar_url'] != null ? 0.8 : 0.5,
child: Text(suggestion['pack']), child: suggestion['pack_avatar_url'] != null
? Avatar(
Uri.parse(suggestion['pack_avatar_url']),
suggestion['pack_display_name'],
size: size * 0.9,
client: client,
)
: Text(suggestion['pack_display_name']),
), ),
), ),
), ),
@ -289,12 +302,12 @@ class InputBar extends StatelessWidget {
var isUnique = true; var isUnique = true;
final insertEmote = suggestion['name']; final insertEmote = suggestion['name'];
final insertPack = suggestion['pack']; final insertPack = suggestion['pack'];
final emotePacks = room.emotePacks; final emotePacks = room.getImagePacks(ImagePackUsage.emoticon);
for (final pack in emotePacks.entries) { for (final pack in emotePacks.entries) {
if (pack.key == insertPack) { if (pack.key == insertPack) {
continue; continue;
} }
for (final emote in pack.value.entries) { for (final emote in pack.value.images.entries) {
if (emote.key == insertEmote) { if (emote.key == insertEmote) {
isUnique = false; isUnique = false;
break; break;
@ -304,10 +317,7 @@ class InputBar extends StatelessWidget {
break; break;
} }
} }
insertText = (isUnique insertText = ':${isUnique ? '' : insertPack + '~'}$insertEmote: ';
? insertEmote
: ':$insertPack~${insertEmote.substring(1)}') +
' ';
startText = replaceText.replaceAllMapped( startText = replaceText.replaceAllMapped(
RegExp(r'(\s|^)(:(?:[-\w]+~)?[-\w]+)$'), RegExp(r'(\s|^)(:(?:[-\w]+~)?[-\w]+)$'),
(Match m) => '${m[1]}$insertText', (Match m) => '${m[1]}$insertText',

View File

@ -647,14 +647,14 @@ packages:
name: matrix name: matrix
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.1.7" version: "0.1.8"
matrix_api_lite: matrix_api_lite:
dependency: transitive dependency: transitive
description: description:
name: matrix_api_lite name: matrix_api_lite
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.3.3" version: "0.3.5"
matrix_link_text: matrix_link_text:
dependency: transitive dependency: transitive
description: description:
@ -1110,6 +1110,13 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.99" version: "0.0.99"
slugify:
dependency: transitive
description:
name: slugify
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
source_map_stack_trace: source_map_stack_trace:
dependency: transitive dependency: transitive
description: description:

View File

@ -48,7 +48,7 @@ dependencies:
intl: any intl: any
localstorage: ^4.0.0+1 localstorage: ^4.0.0+1
lottie: ^1.1.0 lottie: ^1.1.0
matrix: ^0.1.7 matrix: ^0.1.8
native_imaging: native_imaging:
git: git:
url: https://gitlab.com/famedly/libraries/native_imaging.git url: https://gitlab.com/famedly/libraries/native_imaging.git