add emote direct feature

This commit is contained in:
yuxuan-qin 2026-04-17 09:26:21 -04:00
parent 2b81f2cb31
commit d27d46cd33

View file

@ -611,7 +611,8 @@ class IRCMessage {
),
],
),
url: emoteStack.last.url,
imageUrl: emoteStack.last.url,
browserUrl: _getEmoteBrowserUrl(emoteStack.last), // split url source to image and browser urls
title: nextWordIsEmoji
? emoji
: '${emoteStack.last.name} (${emoteStack.last.type})',
@ -771,7 +772,8 @@ class IRCMessage {
isSvg: isSvg,
size: 56,
),
url: badge.url,
imageUrl: badge.url,
browserUrl: badge.url,
title: badge.name,
subtitle: Text(badge.type.toString()),
launchExternal: launchExternal,
@ -860,7 +862,8 @@ class IRCMessage {
_showAssetDetailsBottomSheet(
context,
leading: FrostyCachedNetworkImage(imageUrl: emote.url, width: 56),
url: emote.url,
imageUrl: emote.url,
browserUrl: _getEmoteBrowserUrl(emote),
title: emote.realName != null
? '${emote.name} (${emote.realName})'
: emote.name,
@ -881,7 +884,8 @@ class IRCMessage {
static void _showAssetDetailsBottomSheet(
BuildContext context, {
required Widget leading,
required String url,
required String imageUrl,
required String browserUrl,
required String title,
required Widget subtitle,
required bool launchExternal,
@ -897,7 +901,7 @@ class IRCMessage {
leading: InkWell(
onTap: () => showDialog(
context: context,
builder: (context) => FrostyPhotoViewDialog(imageUrl: url),
builder: (context) => FrostyPhotoViewDialog(imageUrl: imageUrl),
),
child: leading,
),
@ -914,7 +918,6 @@ class IRCMessage {
title: const Text('Copy name'),
onTap: () {
Clipboard.setData(ClipboardData(text: title.split(' (')[0]));
Navigator.pop(context);
},
),
@ -922,8 +925,7 @@ class IRCMessage {
leading: const Icon(Icons.copy_rounded),
title: const Text('Copy image URL'),
onTap: () {
Clipboard.setData(ClipboardData(text: url));
Clipboard.setData(ClipboardData(text: imageUrl));
Navigator.pop(context);
},
),
@ -932,7 +934,7 @@ class IRCMessage {
title: const Text('Open in browser'),
onTap: () {
launchUrl(
Uri.parse(url),
Uri.parse(browserUrl),
mode: launchExternal
? LaunchMode.externalApplication
: LaunchMode.inAppBrowserView,
@ -946,6 +948,46 @@ class IRCMessage {
);
}
// a helper function for extracting the emote id and return the emote url from the third party
static String _getEmoteBrowserUrl(Emote emote) {
final uri = Uri.tryParse(emote.url);
final segments = uri?.pathSegments ?? [];
switch (emote.type) {
case EmoteType.sevenTVGlobal:
case EmoteType.sevenTVChannel:
if (segments.length >= 2 && segments[0] == 'emote') {
return 'https://7tv.app/emotes/${segments[1]}';
}
return emote.url;
case EmoteType.bttvGlobal:
case EmoteType.bttvChannel:
case EmoteType.bttvShared:
if (segments.length >= 2 && segments[0] == 'emote') {
return 'https://betterttv.com/emotes/${segments[1]}';
}
return emote.url;
case EmoteType.ffzGlobal:
case EmoteType.ffzChannel:
final emoticonIndex = segments.indexOf('emoticon');
if (emoticonIndex != -1 && segments.length > emoticonIndex + 1) {
return 'https://www.frankerfacez.com/emoticon/${segments[emoticonIndex + 1]}';
}
final emoteIndex = segments.indexOf('emote');
if (emoteIndex != -1 && segments.length > emoteIndex + 1) {
return 'https://www.frankerfacez.com/emoticon/${segments[emoteIndex + 1]}';
}
return emote.url;
default:
return emote.url;
}
}
/// Parses an IRC string and returns its corresponding [IRCMessage] object.
factory IRCMessage.fromString(String whole, {String? userLogin}) {
// We have three parts: