From 089932a9f4a39729ddd3a94d4dcdcf387a6df15c Mon Sep 17 00:00:00 2001 From: krille-chan Date: Thu, 20 Nov 2025 20:30:49 +0100 Subject: [PATCH] feat: Create new sticker packs --- lib/l10n/intl_en.arb | 4 +- .../settings_emotes/settings_emotes.dart | 56 +++++++++++++++++-- .../settings_emotes/settings_emotes_view.dart | 48 ++++++++++++++-- 3 files changed, 97 insertions(+), 11 deletions(-) diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 694a130fe..22d4190ae 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -3456,5 +3456,7 @@ "saveChanges": "Save changes", "createSticker": "Create sticker or emoji", "useAsSticker": "Use as sticker", - "useAsEmoji": "Use as emoji" + "useAsEmoji": "Use as emoji", + "stickerPackNameAlreadyExists": "Sticker pack name already exists", + "newStickerPack": "New sticker pack" } diff --git a/lib/pages/settings_emotes/settings_emotes.dart b/lib/pages/settings_emotes/settings_emotes.dart index 4b50975da..e412c1b9d 100644 --- a/lib/pages/settings_emotes/settings_emotes.dart +++ b/lib/pages/settings_emotes/settings_emotes.dart @@ -11,6 +11,7 @@ import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/file_selector.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_file_extension.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; +import 'package:fluffychat/widgets/adaptive_dialogs/show_text_input_dialog.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; import '../../widgets/matrix.dart'; import 'import_archive_dialog.dart'; @@ -28,9 +29,7 @@ class EmotesSettings extends StatefulWidget { } class EmotesSettingsController extends State { - Room? get room => widget.roomId != null - ? Matrix.of(context).client.getRoomById(widget.roomId!) - : null; + late final Room? room; String? stateKey; @@ -45,6 +44,9 @@ class EmotesSettingsController extends State { @override void initState() { super.initState(); + room = widget.roomId != null + ? Matrix.of(context).client.getRoomById(widget.roomId!) + : null; stateKey = packKeys?.firstOrNull; } @@ -194,7 +196,9 @@ class EmotesSettingsController extends State { ?.tryGetMap(stateKey ?? '') != null; - bool get readonly => room?.canSendEvent('im.ponies.room_emotes') ?? false; + bool get readonly => room == null + ? false + : room?.canChangeStateEvent('im.ponies.room_emotes') == false; void resetAction() { setState(() { @@ -203,6 +207,50 @@ class EmotesSettingsController extends State { }); } + void createImagePack() async { + final room = this.room; + if (room == null) throw Exception('Cannot create image pack without room'); + + final input = await showTextInputDialog( + context: context, + title: L10n.of(context).newStickerPack, + hintText: L10n.of(context).name, + okLabel: L10n.of(context).create, + ); + final name = input?.trim(); + if (name == null || name.isEmpty) return; + if (!mounted) return; + + final keyName = name.toLowerCase().replaceAll(' ', '_'); + + if (packKeys?.contains(name) ?? false) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(L10n.of(context).stickerPackNameAlreadyExists), + ), + ); + return; + } + + await showFutureLoadingDialog( + context: context, + future: () => room.client.setRoomStateWithKey( + room.id, + 'im.ponies.room_emotes', + keyName, + { + 'images': {}, + 'pack': {'display_name': name}, + }, + ), + ); + if (!mounted) return; + setState(() {}); + await room.client.oneShotSync(); + if (!mounted) return; + setState(() {}); + } + void saveAction() async { await save(context); setState(() { diff --git a/lib/pages/settings_emotes/settings_emotes_view.dart b/lib/pages/settings_emotes/settings_emotes_view.dart index 31b61b102..8a55d8bfe 100644 --- a/lib/pages/settings_emotes/settings_emotes_view.dart +++ b/lib/pages/settings_emotes/settings_emotes_view.dart @@ -20,11 +20,25 @@ class EmotesSettingsView extends StatelessWidget { @override Widget build(BuildContext context) { + if (controller.widget.roomId != null && controller.room == null) { + return Scaffold( + appBar: AppBar( + title: Text(L10n.of(context).oopsSomethingWentWrong), + ), + body: Center( + child: Text(L10n.of(context).youAreNoLongerParticipatingInThisChat), + ), + ); + } final theme = Theme.of(context); final client = Matrix.of(context).client; final imageKeys = controller.pack!.images.keys.toList(); final packKeys = controller.packKeys; + if (packKeys != null && packKeys.isEmpty) { + packKeys.add(''); + } + return Scaffold( appBar: AppBar( automaticallyImplyLeading: !controller.showSave, @@ -71,7 +85,7 @@ class EmotesSettingsView extends StatelessWidget { ], ), ], - bottom: packKeys == null || packKeys.isEmpty + bottom: packKeys == null ? null : PreferredSize( preferredSize: const Size.fromHeight(48), @@ -81,8 +95,28 @@ class EmotesSettingsView extends StatelessWidget { height: 40, child: ListView.builder( scrollDirection: Axis.horizontal, - itemCount: packKeys.length, + itemCount: packKeys.length + 1, itemBuilder: (context, i) { + if (i == 0) { + if (controller.readonly) { + return const SizedBox.shrink(); + } + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 4.0, + ), + child: FilterChip( + label: const Icon( + Icons.add_outlined, + size: 20, + ), + onSelected: controller.showSave + ? null + : (_) => controller.createImagePack(), + ), + ); + } + i--; final key = packKeys[i]; final event = controller.room ?.getState('im.ponies.room_emotes', packKeys[i]); @@ -90,7 +124,7 @@ class EmotesSettingsView extends StatelessWidget { final eventPack = event?.content.tryGetMap('pack'); final packName = - eventPack?.tryGet('displayname') ?? + eventPack?.tryGet('display_name') ?? eventPack?.tryGet('name') ?? (key.isNotEmpty ? key : 'Default'); @@ -100,9 +134,11 @@ class EmotesSettingsView extends StatelessWidget { ), child: FilterChip( label: Text(packName), - selected: controller.stateKey == packKeys[i], - onSelected: (_) => - controller.setStateKey(packKeys[i]), + selected: controller.stateKey == key || + (controller.stateKey == null && key.isEmpty), + onSelected: controller.showSave + ? null + : (_) => controller.setStateKey(key), ), ); },