Add bold & highlighted user mentions in chat
This commit is contained in:
parent
0a729fbfa7
commit
108bd5573e
3 changed files with 50 additions and 35 deletions
|
|
@ -15,6 +15,7 @@ class IRCMessage {
|
|||
final Map<String, Emote> localEmotes;
|
||||
Command command;
|
||||
String? message;
|
||||
bool mention;
|
||||
|
||||
IRCMessage({
|
||||
required this.raw,
|
||||
|
|
@ -24,6 +25,7 @@ class IRCMessage {
|
|||
required this.user,
|
||||
required this.localEmotes,
|
||||
required this.message,
|
||||
required this.mention,
|
||||
});
|
||||
|
||||
/// Returns a list of messages where the gievn CLEARCHAT message is applied.
|
||||
|
|
@ -241,7 +243,7 @@ class IRCMessage {
|
|||
}
|
||||
|
||||
// Italicize the text it was called with an IRC Action i.e., "/me".
|
||||
final textStyle = action == true ? const TextStyle(fontStyle: FontStyle.italic) : null;
|
||||
final textStyle = action == true ? const TextStyle(fontStyle: FontStyle.italic) : style;
|
||||
|
||||
if (hideMessage) {
|
||||
span.add(const TextSpan(text: ' <message deleted>'));
|
||||
|
|
@ -317,7 +319,7 @@ class IRCMessage {
|
|||
|
||||
if (nextEmote == null && !nextWordIsEmoji) {
|
||||
localSpan.add(const TextSpan(text: ' '));
|
||||
localSpan.add(TextSpan(text: words[index], style: textStyle));
|
||||
localSpan.add(_createTextSpan(text: words[index], style: textStyle));
|
||||
}
|
||||
} else {
|
||||
localSpan.add(
|
||||
|
|
@ -329,26 +331,18 @@ class IRCMessage {
|
|||
);
|
||||
}
|
||||
} else {
|
||||
localSpan.add(TextSpan(text: word, style: textStyle));
|
||||
localSpan.add(_createTextSpan(text: word, style: textStyle));
|
||||
}
|
||||
localSpan.add(const TextSpan(text: ' '));
|
||||
index--;
|
||||
}
|
||||
span.addAll(localSpan.reversed);
|
||||
} else {
|
||||
// Use a string buffer to minimize TextSpan widgets.
|
||||
// Instead of one TextSpan widget per word, we can have one TextSpan widget across multiple.
|
||||
final buffer = StringBuffer();
|
||||
|
||||
for (final word in words) {
|
||||
buffer.write(' ');
|
||||
span.add(const TextSpan(text: ' '));
|
||||
|
||||
final emote = emoteToObject[word] ?? localEmotes[word];
|
||||
if (emote != null) {
|
||||
span.add(TextSpan(text: buffer.toString(), style: textStyle));
|
||||
|
||||
buffer.clear();
|
||||
|
||||
span.add(
|
||||
_createEmoteSpan(
|
||||
emoteUrl: emote.url,
|
||||
|
|
@ -357,13 +351,9 @@ class IRCMessage {
|
|||
),
|
||||
);
|
||||
} else {
|
||||
buffer.write(word);
|
||||
span.add(_createTextSpan(text: word, style: textStyle));
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer.isNotEmpty) {
|
||||
span.add(TextSpan(text: buffer.toString(), style: textStyle));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -414,8 +404,16 @@ class IRCMessage {
|
|||
);
|
||||
}
|
||||
|
||||
TextSpan _createTextSpan({required String text, TextStyle? style}) {
|
||||
if (text.startsWith('@')) {
|
||||
return TextSpan(text: text, style: style?.copyWith(fontWeight: FontWeight.bold));
|
||||
} else {
|
||||
return TextSpan(text: text, style: style);
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses an IRC string and returns its corresponding [IRCMessage] object.
|
||||
factory IRCMessage.fromString(String whole) {
|
||||
factory IRCMessage.fromString(String whole, {String? username}) {
|
||||
// We have three parts:
|
||||
// 1. The tags of the IRC message.
|
||||
// 2. The metadata (user, command, and channel).
|
||||
|
|
@ -472,6 +470,10 @@ class IRCMessage {
|
|||
message = message.substring(8, message.length - 1);
|
||||
}
|
||||
|
||||
// Check if the message mentions the logged-in user
|
||||
var mention = false;
|
||||
if (message != null && username != null) mention = message.toLowerCase().contains(username);
|
||||
|
||||
// Now process any Twitch emotes contained in the message tags.
|
||||
// The map containing emotes from the user's tags to their URL.
|
||||
// This may include sub emotes that they can access but other users cannot.
|
||||
|
|
@ -555,6 +557,7 @@ class IRCMessage {
|
|||
localEmotes: localEmotes,
|
||||
user: user,
|
||||
message: message,
|
||||
mention: mention,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -566,6 +569,7 @@ class IRCMessage {
|
|||
command: Command.notice,
|
||||
user: null,
|
||||
message: message,
|
||||
mention: false,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,24 +27,35 @@ class ChatMessage extends StatelessWidget {
|
|||
case Command.privateMessage:
|
||||
case Command.userState:
|
||||
// Render normal chat message (PRIVMSG).
|
||||
return Padding(
|
||||
padding: padding,
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: ircMessage.generateSpan(
|
||||
style: DefaultTextStyle.of(context).style,
|
||||
emoteToObject: assetsStore.emoteToObject,
|
||||
twitchBadgeToObject: assetsStore.twitchBadgesToObject,
|
||||
ffzUserToBadges: assetsStore.userToFFZBadges,
|
||||
sevenTVUserToBadges: assetsStore.userTo7TVBadges,
|
||||
bttvUserToBadge: assetsStore.userToBTTVBadges,
|
||||
ffzRoomInfo: assetsStore.ffzRoomInfo,
|
||||
zeroWidthEnabled: settingsStore.zeroWidthEnabled,
|
||||
timestamp: timeStamps,
|
||||
),
|
||||
final span = Text.rich(
|
||||
TextSpan(
|
||||
children: ircMessage.generateSpan(
|
||||
style: DefaultTextStyle.of(context).style,
|
||||
emoteToObject: assetsStore.emoteToObject,
|
||||
twitchBadgeToObject: assetsStore.twitchBadgesToObject,
|
||||
ffzUserToBadges: assetsStore.userToFFZBadges,
|
||||
sevenTVUserToBadges: assetsStore.userTo7TVBadges,
|
||||
bttvUserToBadge: assetsStore.userToBTTVBadges,
|
||||
ffzRoomInfo: assetsStore.ffzRoomInfo,
|
||||
zeroWidthEnabled: settingsStore.zeroWidthEnabled,
|
||||
timestamp: timeStamps,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (ircMessage.mention) {
|
||||
return Container(
|
||||
padding: padding,
|
||||
color: const Color(0x4DFF0000),
|
||||
child: span,
|
||||
);
|
||||
} else {
|
||||
return Padding(
|
||||
padding: padding,
|
||||
child: span,
|
||||
);
|
||||
}
|
||||
|
||||
case Command.clearChat:
|
||||
case Command.clearMessage:
|
||||
// Render timeouts and bans
|
||||
|
|
@ -102,7 +113,7 @@ class ChatMessage extends StatelessWidget {
|
|||
case Command.userNotice:
|
||||
return Container(
|
||||
padding: padding,
|
||||
color: const Color(0xFF673AB7).withOpacity(0.25),
|
||||
color: const Color(0x339147FF),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ abstract class _ChatStoreBase with Store {
|
|||
// To account for this, split by CRLF, then loop and process each message.
|
||||
for (final message in data.trimRight().split('\r\n')) {
|
||||
if (message.startsWith('@')) {
|
||||
final parsedIRCMessage = IRCMessage.fromString(message);
|
||||
final parsedIRCMessage = IRCMessage.fromString(message, username: auth.user.details?.login);
|
||||
|
||||
// Filter messages from any blocked users if not a moderator or not the channel owner.
|
||||
if (!_userState.mod &&
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue