mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-11-23 12:39:29 +01:00
feat: implement an emoji keyboard
- add button to show emoji keyboard - change database directory for debug builds Signed-off-by: TheOneWithTheBraid <the-one@with-the-braid.cf>
This commit is contained in:
parent
0eba2ae859
commit
30ce5c7f57
@ -2711,7 +2711,6 @@
|
||||
"markAsRead": "Mark as read",
|
||||
"reportUser": "Report user",
|
||||
"dismiss": "Dismiss",
|
||||
"markAsRead": "Mark as read",
|
||||
"matrixWidgets": "Matrix Widgets",
|
||||
"integrationsNotImplemented": "Editing widgets and integrations is not possible yet.",
|
||||
"editIntegrations": "Edit widgets and integrations",
|
||||
@ -2725,5 +2724,6 @@
|
||||
},
|
||||
"pinMessage": "Pin to room",
|
||||
"pinnedEventsError": "Error loading pinned messages",
|
||||
"confirmEventUnpin": "Are you sure to permanently unpin the event?"
|
||||
"confirmEventUnpin": "Are you sure to permanently unpin the event?",
|
||||
"emojis": "Emojis"
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ To run code after the widget was created first we use the WidgetBindings in the
|
||||
```dart
|
||||
@override
|
||||
void initState() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
WidgetsBinding.instance!.addPostFrameCallback((_) {
|
||||
// Do something when build is finished
|
||||
});
|
||||
super.initState();
|
||||
|
@ -8,6 +8,7 @@ import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||
import 'package:desktop_drop/desktop_drop.dart';
|
||||
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
|
||||
import 'package:file_picker_cross/file_picker_cross.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||
@ -125,6 +126,8 @@ class ChatController extends State<Chat> {
|
||||
|
||||
bool showEmojiPicker = false;
|
||||
|
||||
EmojiPickerType emojiPickerType = EmojiPickerType.keyboard;
|
||||
|
||||
void startCallAction() async {
|
||||
final url =
|
||||
'${AppConfig.jitsiInstance}${Uri.encodeComponent(Matrix.of(context).client.generateUniqueTransactionId())}';
|
||||
@ -178,6 +181,8 @@ class ChatController extends State<Chat> {
|
||||
@override
|
||||
void initState() {
|
||||
scrollController.addListener(_updateScrollController);
|
||||
|
||||
inputFocus.addListener(_inputFocusListener);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@ -239,6 +244,7 @@ class ChatController extends State<Chat> {
|
||||
void dispose() {
|
||||
timeline?.cancelSubscriptions();
|
||||
timeline = null;
|
||||
inputFocus.removeListener(_inputFocusListener);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@ -426,6 +432,20 @@ class ChatController extends State<Chat> {
|
||||
});
|
||||
}
|
||||
|
||||
void emojiPickerAction() {
|
||||
emojiPickerType = EmojiPickerType.keyboard;
|
||||
setState(() => showEmojiPicker = !showEmojiPicker);
|
||||
_inputFocusListener();
|
||||
}
|
||||
|
||||
void _inputFocusListener() {
|
||||
if (showEmojiPicker) {
|
||||
inputFocus.unfocus();
|
||||
} else {
|
||||
inputFocus.requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void sendLocationAction() async {
|
||||
await showDialog(
|
||||
context: context,
|
||||
@ -668,7 +688,18 @@ class ChatController extends State<Chat> {
|
||||
|
||||
void scrollDown() => scrollController.jumpTo(0);
|
||||
|
||||
void onEmojiSelected(_, emoji) {
|
||||
void onEmojiSelected(_, Emoji? emoji) {
|
||||
switch (emojiPickerType) {
|
||||
case EmojiPickerType.reaction:
|
||||
senEmojiReaction(emoji);
|
||||
break;
|
||||
case EmojiPickerType.keyboard:
|
||||
typeEmoji(emoji);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void senEmojiReaction(Emoji? emoji) {
|
||||
setState(() => showEmojiPicker = false);
|
||||
if (emoji == null) return;
|
||||
// make sure we don't send the same emoji twice
|
||||
@ -677,12 +708,41 @@ class ChatController extends State<Chat> {
|
||||
return sendEmojiAction(emoji.emoji);
|
||||
}
|
||||
|
||||
void typeEmoji(Emoji? emoji) {
|
||||
if (emoji == null) return;
|
||||
final text = sendController.text;
|
||||
final selection = sendController.selection;
|
||||
final newText = sendController.text.isEmpty
|
||||
? emoji.emoji
|
||||
: text.replaceRange(selection.start, selection.end, emoji.emoji);
|
||||
sendController.value = TextEditingValue(
|
||||
text: newText,
|
||||
selection: TextSelection.collapsed(
|
||||
// don't forget an UTF-8 combined emoji might have a length > 1
|
||||
offset: selection.baseOffset + emoji.emoji.length,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
late Iterable<Event> _allReactionEvents;
|
||||
|
||||
void cancelEmojiPicker() => setState(() => showEmojiPicker = false);
|
||||
void emojiPickerBackspace() {
|
||||
switch (emojiPickerType) {
|
||||
case EmojiPickerType.reaction:
|
||||
setState(() => showEmojiPicker = false);
|
||||
break;
|
||||
case EmojiPickerType.keyboard:
|
||||
sendController
|
||||
..text = sendController.text.characters.skipLast(1).toString()
|
||||
..selection = TextSelection.fromPosition(
|
||||
TextPosition(offset: sendController.text.length));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void pickEmojiAction(Iterable<Event> allReactionEvents) async {
|
||||
void pickEmojiReactionAction(Iterable<Event> allReactionEvents) async {
|
||||
_allReactionEvents = allReactionEvents;
|
||||
emojiPickerType = EmojiPickerType.reaction;
|
||||
setState(() => showEmojiPicker = true);
|
||||
}
|
||||
|
||||
@ -902,3 +962,5 @@ class ChatController extends State<Chat> {
|
||||
@override
|
||||
Widget build(BuildContext context) => ChatView(this);
|
||||
}
|
||||
|
||||
enum EmojiPickerType { reaction, keyboard }
|
||||
|
@ -18,7 +18,7 @@ class ChatEmojiPicker extends StatelessWidget {
|
||||
child: controller.showEmojiPicker
|
||||
? EmojiPicker(
|
||||
onEmojiSelected: controller.onEmojiSelected,
|
||||
onBackspacePressed: controller.cancelEmojiPicker,
|
||||
onBackspacePressed: controller.emojiPickerBackspace,
|
||||
)
|
||||
: null,
|
||||
);
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:animations/animations.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
@ -8,16 +9,19 @@ import 'package:fluffychat/utils/platform_infos.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'chat.dart';
|
||||
import 'encryption_button.dart';
|
||||
import 'input_bar.dart';
|
||||
|
||||
class ChatInputRow extends StatelessWidget {
|
||||
final ChatController controller;
|
||||
|
||||
const ChatInputRow(this.controller, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (controller.showEmojiPicker) return Container();
|
||||
if (controller.showEmojiPicker &&
|
||||
controller.emojiPickerType == EmojiPickerType.reaction) {
|
||||
return Container();
|
||||
}
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
@ -176,7 +180,31 @@ class ChatInputRow extends StatelessWidget {
|
||||
Container(
|
||||
height: 56,
|
||||
alignment: Alignment.center,
|
||||
child: EncryptionButton(controller.room!),
|
||||
child: IconButton(
|
||||
tooltip: L10n.of(context)!.emojis,
|
||||
icon: PageTransitionSwitcher(
|
||||
transitionBuilder: (
|
||||
Widget child,
|
||||
Animation<double> primaryAnimation,
|
||||
Animation<double> secondaryAnimation,
|
||||
) {
|
||||
return SharedAxisTransition(
|
||||
animation: primaryAnimation,
|
||||
secondaryAnimation: secondaryAnimation,
|
||||
transitionType: SharedAxisTransitionType.scaled,
|
||||
child: child,
|
||||
fillColor: Colors.transparent,
|
||||
);
|
||||
},
|
||||
child: Icon(
|
||||
controller.showEmojiPicker
|
||||
? Icons.keyboard
|
||||
: Icons.emoji_emotions_outlined,
|
||||
key: ValueKey(controller.showEmojiPicker),
|
||||
),
|
||||
),
|
||||
onPressed: controller.emojiPickerAction,
|
||||
),
|
||||
),
|
||||
if (controller.matrix!.isMultiAccount &&
|
||||
controller.matrix!.hasComplexBundles &&
|
||||
|
@ -12,6 +12,7 @@ import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pages/chat/chat.dart';
|
||||
import 'package:fluffychat/pages/chat/chat_app_bar_title.dart';
|
||||
import 'package:fluffychat/pages/chat/encryption_button.dart';
|
||||
import 'package:fluffychat/pages/chat/pinned_events.dart';
|
||||
import 'package:fluffychat/pages/chat/reactions_picker.dart';
|
||||
import 'package:fluffychat/pages/chat/reply_display.dart';
|
||||
@ -119,6 +120,7 @@ class ChatView extends StatelessWidget {
|
||||
icon: const Icon(Icons.widgets),
|
||||
tooltip: L10n.of(context)!.matrixWidgets,
|
||||
),
|
||||
EncryptionButton(controller.room!),
|
||||
ChatSettingsPopupMenu(controller.room!, !controller.room!.isDirectChat),
|
||||
];
|
||||
}
|
||||
|
@ -77,7 +77,8 @@ class ReactionsPicker extends StatelessWidget {
|
||||
),
|
||||
child: const Icon(Icons.add_outlined),
|
||||
),
|
||||
onTap: () => controller.pickEmojiAction(allReactionEvents))
|
||||
onTap: () =>
|
||||
controller.pickEmojiReactionAction(allReactionEvents))
|
||||
]);
|
||||
}),
|
||||
),
|
||||
|
@ -94,6 +94,11 @@ class FlutterFluffyBoxDatabase extends FluffyBoxDatabase {
|
||||
directory = Directory.current;
|
||||
}
|
||||
}
|
||||
// do not destroy your stable FluffyChat in debug mode
|
||||
if (kDebugMode) {
|
||||
directory = Directory(directory.uri.resolve('debug').toFilePath());
|
||||
directory.create(recursive: true);
|
||||
}
|
||||
path = directory.path;
|
||||
}
|
||||
return path;
|
||||
|
@ -9,6 +9,9 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||
url_launcher_windows
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
||||
foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
||||
@ -17,3 +20,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST})
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
|
||||
endforeach(plugin)
|
||||
|
||||
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
|
||||
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
|
||||
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
|
||||
endforeach(ffi_plugin)
|
||||
|
Loading…
Reference in New Issue
Block a user