From 663b32b945327f00c148d29e1d165e3f2e646026 Mon Sep 17 00:00:00 2001 From: Artiu <71273256+Artiu@users.noreply.github.com> Date: Mon, 18 Dec 2023 03:47:44 +0100 Subject: [PATCH] Add user mention clickability (#314) * Add handle for clicking on user ping * Add trailing coma --- lib/models/irc.dart | 10 ++++++ .../channel/chat/widgets/chat_message.dart | 33 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/lib/models/irc.dart b/lib/models/irc.dart index 26b39489..a0f836e2 100644 --- a/lib/models/irc.dart +++ b/lib/models/irc.dart @@ -117,6 +117,7 @@ class IRCMessage { required double emoteScale, required bool launchExternal, void Function()? onTapName, + void Function(String)? onTapPingedUser, bool showMessage = true, bool useReadableColors = false, TimestampType timestamp = TimestampType.disabled, @@ -456,6 +457,7 @@ class IRCMessage { text: words[index], style: textStyle, launchExternal: launchExternal, + onTapPingedUser: onTapPingedUser, ), ); } @@ -486,6 +488,7 @@ class IRCMessage { text: word, style: textStyle, launchExternal: launchExternal, + onTapPingedUser: onTapPingedUser, ), ); } @@ -613,11 +616,18 @@ class IRCMessage { required String text, required bool launchExternal, TextStyle? style, + Function(String)? onTapPingedUser, }) { if (text.startsWith('@')) { return TextSpan( text: text, style: style?.copyWith(fontWeight: FontWeight.bold), + recognizer: TapGestureRecognizer() + ..onTap = () { + if (onTapPingedUser != null) { + onTapPingedUser(text.substring(1)); + } + }, ); } else if (RegExp(r'https?:\/\/').hasMatch(text)) { return TextSpan( diff --git a/lib/screens/channel/chat/widgets/chat_message.dart b/lib/screens/channel/chat/widgets/chat_message.dart index b48c9c89..78255910 100644 --- a/lib/screens/channel/chat/widgets/chat_message.dart +++ b/lib/screens/channel/chat/widgets/chat_message.dart @@ -1,12 +1,15 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:frosty/apis/twitch_api.dart'; import 'package:frosty/constants.dart'; import 'package:frosty/models/irc.dart'; import 'package:frosty/screens/channel/chat/stores/chat_store.dart'; import 'package:frosty/screens/channel/chat/widgets/chat_user_modal.dart'; import 'package:frosty/screens/channel/chat/widgets/reply_thread.dart'; +import 'package:frosty/screens/settings/stores/auth_store.dart'; import 'package:frosty/theme.dart'; +import 'package:provider/provider.dart'; class ChatMessage extends StatelessWidget { final IRCMessage ircMessage; @@ -40,6 +43,34 @@ class ChatMessage extends StatelessWidget { ); } + void onTapPingedUser( + BuildContext context, { + required String nickname, + }) { + // Ignore if the message is a recent message in the modal bottom sheet. + if (isModal) return; + + final twitchApi = context.read(); + final authStore = context.read(); + twitchApi + .getUser( + headers: authStore.headersTwitch, + userLogin: nickname, + ) + .then((user) { + showModalBottomSheet( + isScrollControlled: true, + context: context, + builder: (context) => ChatUserModal( + chatStore: chatStore, + username: user.login, + userId: user.id, + displayName: user.displayName, + ), + ); + }); + } + Future copyMessage() async { await Clipboard.setData(ClipboardData(text: ircMessage.message ?? '')); @@ -127,6 +158,8 @@ class ChatMessage extends StatelessWidget { children: ircMessage.generateSpan( context, onTapName: () => onTapName(context), + onTapPingedUser: (nickname) => + onTapPingedUser(context, nickname: nickname), style: defaultTextStyle, assetsStore: chatStore.assetsStore, emoteScale: chatStore.settings.emoteScale,