diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 749e5fa8..a965ab0d 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2481,5 +2481,7 @@ } }, "transparent": "Transparent", - "incomingMessages": "Incoming messages" + "incomingMessages": "Incoming messages", + "stickers": "Stickers", + "discover": "Discover" } \ No newline at end of file diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 1f7eb424..ccd677e4 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -25,7 +25,6 @@ import 'package:fluffychat/pages/chat/chat_view.dart'; import 'package:fluffychat/pages/chat/event_info_dialog.dart'; import 'package:fluffychat/pages/chat/recording_dialog.dart'; import 'package:fluffychat/pages/chat_details/chat_details.dart'; -import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'package:fluffychat/utils/error_reporter.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; @@ -37,7 +36,6 @@ import '../../utils/localized_exception_extension.dart'; import '../../utils/matrix_sdk_extensions/matrix_file_extension.dart'; import 'send_file_dialog.dart'; import 'send_location_dialog.dart'; -import 'sticker_picker_dialog.dart'; class ChatPage extends StatelessWidget { final String roomId; @@ -594,24 +592,6 @@ class ChatController extends State ); } - void sendStickerAction() async { - final sticker = await showAdaptiveBottomSheet( - context: context, - builder: (c) => StickerPickerDialog(room: room), - ); - if (sticker == null) return; - final eventContent = { - 'body': sticker.body, - if (sticker.info != null) 'info': sticker.info, - 'url': sticker.url.toString(), - }; - // send the sticker - await room.sendEvent( - eventContent, - type: EventTypes.Sticker, - ); - } - void voiceMessageAction() async { final scaffoldMessenger = ScaffoldMessenger.of(context); if (PlatformInfos.isAndroid) { @@ -668,6 +648,10 @@ class ChatController extends State }); } + void hideEmojiPicker() { + setState(() => showEmojiPicker = false); + } + void emojiPickerAction() { if (showEmojiPicker) { inputFocus.requestFocus(); @@ -1146,9 +1130,6 @@ class ChatController extends State if (choice == 'camera-video') { openVideoCameraAction(); } - if (choice == 'sticker') { - sendStickerAction(); - } if (choice == 'location') { sendLocationAction(); } diff --git a/lib/pages/chat/chat_emoji_picker.dart b/lib/pages/chat/chat_emoji_picker.dart index 5e45113b..f399e04f 100644 --- a/lib/pages/chat/chat_emoji_picker.dart +++ b/lib/pages/chat/chat_emoji_picker.dart @@ -2,8 +2,10 @@ import 'package:flutter/material.dart'; import 'package:emoji_picker_flutter/emoji_picker_flutter.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/themes.dart'; +import 'package:fluffychat/pages/chat/sticker_picker_dialog.dart'; import 'chat.dart'; class ChatEmojiPicker extends StatelessWidget { @@ -16,30 +18,65 @@ class ChatEmojiPicker extends StatelessWidget { return AnimatedContainer( duration: FluffyThemes.animationDuration, curve: FluffyThemes.animationCurve, + clipBehavior: Clip.hardEdge, + decoration: const BoxDecoration(), height: controller.showEmojiPicker ? MediaQuery.of(context).size.height / 2 : 0, child: controller.showEmojiPicker - ? EmojiPicker( - onEmojiSelected: controller.onEmojiSelected, - onBackspacePressed: controller.emojiPickerBackspace, - config: Config( - backspaceColor: theme.colorScheme.primary, - bgColor: Color.lerp( - theme.colorScheme.background, - theme.colorScheme.primaryContainer, - 0.25, - )!, - iconColor: theme.colorScheme.primary.withOpacity(0.5), - iconColorSelected: theme.colorScheme.primary, - indicatorColor: theme.colorScheme.primary, - noRecents: const NoRecent(), - skinToneDialogBgColor: Color.lerp( - theme.colorScheme.background, - theme.colorScheme.primaryContainer, - 0.75, - )!, - skinToneIndicatorColor: theme.colorScheme.onBackground, + ? DefaultTabController( + length: 2, + child: Column( + children: [ + TabBar( + tabs: [ + Tab(text: L10n.of(context)!.emojis), + Tab(text: L10n.of(context)!.stickers), + ], + ), + Expanded( + child: TabBarView( + children: [ + EmojiPicker( + onEmojiSelected: controller.onEmojiSelected, + onBackspacePressed: controller.emojiPickerBackspace, + config: Config( + backspaceColor: theme.colorScheme.primary, + bgColor: theme.brightness == Brightness.light + ? Colors.white + : Colors.black, + iconColor: + theme.colorScheme.primary.withOpacity(0.5), + iconColorSelected: theme.colorScheme.primary, + indicatorColor: theme.colorScheme.primary, + noRecents: const NoRecent(), + skinToneDialogBgColor: Color.lerp( + theme.colorScheme.background, + theme.colorScheme.primaryContainer, + 0.75, + )!, + skinToneIndicatorColor: + theme.colorScheme.onBackground, + ), + ), + StickerPickerDialog( + room: controller.room, + onSelected: (sticker) { + controller.room.sendEvent( + { + 'body': sticker.body, + if (sticker.info != null) 'info': sticker.info, + 'url': sticker.url.toString(), + }, + type: EventTypes.Sticker, + ); + controller.hideEmojiPicker(); + }, + ), + ], + ), + ), + ], ), ) : null, diff --git a/lib/pages/chat/chat_input_row.dart b/lib/pages/chat/chat_input_row.dart index e1794da9..c9921607 100644 --- a/lib/pages/chat/chat_input_row.dart +++ b/lib/pages/chat/chat_input_row.dart @@ -164,21 +164,6 @@ class ChatInputRow extends StatelessWidget { contentPadding: const EdgeInsets.all(0), ), ), - if (controller.room - .getImagePacks(ImagePackUsage.sticker) - .isNotEmpty) - PopupMenuItem( - value: 'sticker', - child: ListTile( - leading: const CircleAvatar( - backgroundColor: Colors.orange, - foregroundColor: Colors.white, - child: Icon(Icons.emoji_emotions_outlined), - ), - title: Text(L10n.of(context)!.sendSticker), - contentPadding: const EdgeInsets.all(0), - ), - ), if (PlatformInfos.isMobile) PopupMenuItem( value: 'location', @@ -225,7 +210,7 @@ class ChatInputRow extends StatelessWidget { child: Icon( controller.showEmojiPicker ? Icons.keyboard - : Icons.emoji_emotions_outlined, + : Icons.add_reaction_outlined, key: ValueKey(controller.showEmojiPicker), ), ), diff --git a/lib/pages/chat/sticker_picker_dialog.dart b/lib/pages/chat/sticker_picker_dialog.dart index 16065c91..35d37c97 100644 --- a/lib/pages/chat/sticker_picker_dialog.dart +++ b/lib/pages/chat/sticker_picker_dialog.dart @@ -3,13 +3,20 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/utils/url_launcher.dart'; +import 'package:fluffychat/widgets/mxc_image.dart'; import '../../widgets/avatar.dart'; -import 'events/image_bubble.dart'; class StickerPickerDialog extends StatefulWidget { final Room room; + final void Function(ImagePackImageContent) onSelected; - const StickerPickerDialog({required this.room, super.key}); + const StickerPickerDialog({ + required this.onSelected, + required this.room, + super.key, + }); @override StickerPickerDialogState createState() => StickerPickerDialogState(); @@ -58,24 +65,14 @@ class StickerPickerDialogState extends State { GridView.builder( itemCount: imageKeys.length, gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 100, + maxCrossAxisExtent: 128, ), shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemBuilder: (BuildContext context, int imageIndex) { final image = pack.images[imageKeys[imageIndex]]!; - final fakeEvent = Event( - type: EventTypes.Sticker, - content: { - 'url': image.url.toString(), - 'info': image.info, - }, - originServerTs: DateTime.now(), - room: widget.room, - eventId: 'fake_event', - senderId: widget.room.client.userID!, - ); return InkWell( + radius: AppConfig.borderRadius, key: ValueKey(image.url.toString()), onTap: () { // copy the image @@ -83,17 +80,16 @@ class StickerPickerDialogState extends State { ImagePackImageContent.fromJson(image.toJson().copy()); // set the body, if it doesn't exist, to the key imageCopy.body ??= imageKeys[imageIndex]; - Navigator.of(context, rootNavigator: false) - .pop(imageCopy); + widget.onSelected(imageCopy); }, child: AbsorbPointer( absorbing: true, - child: ImageBubble( - fakeEvent, - tapToView: false, + child: MxcImage( + uri: image.url, fit: BoxFit.contain, - width: 100, - height: 100, + width: 128, + height: 128, + animated: true, ), ), ); @@ -112,28 +108,47 @@ class StickerPickerDialogState extends State { floating: true, pinned: true, automaticallyImplyLeading: false, - titleSpacing: 0, backgroundColor: Theme.of(context).dialogBackgroundColor, - leading: IconButton( - icon: const Icon(Icons.close), - onPressed: Navigator.of(context, rootNavigator: false).pop, - ), - title: TextField( - autofocus: false, - decoration: InputDecoration( - hintText: L10n.of(context)!.search, - suffix: const Icon(Icons.search_outlined), - contentPadding: const EdgeInsets.symmetric(horizontal: 16), + title: SizedBox( + height: 42, + child: TextField( + autofocus: false, + decoration: InputDecoration( + hintText: L10n.of(context)!.search, + prefixIcon: const Icon(Icons.search_outlined), + contentPadding: EdgeInsets.zero, + ), + onChanged: (s) => setState(() => searchFilter = s), ), - onChanged: (s) => setState(() => searchFilter = s), ), ), - SliverList( - delegate: SliverChildBuilderDelegate( - packBuilder, - childCount: packSlugs.length, + if (packSlugs.isEmpty) + SliverFillRemaining( + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text(L10n.of(context)!.noEmotesFound), + const SizedBox(height: 12), + OutlinedButton.icon( + onPressed: () => UrlLauncher( + context, + 'https://matrix.to/#/#fluffychat-stickers:janian.de', + ).launchUrl(), + icon: const Icon(Icons.explore_outlined), + label: Text(L10n.of(context)!.discover), + ), + ], + ), + ), + ) + else + SliverList( + delegate: SliverChildBuilderDelegate( + packBuilder, + childCount: packSlugs.length, + ), ), - ), ], ), ),