Make DM names hoverable and channel icons clickable

- Both names in direct messages show their team on hover
- Clicking a channel icon suggests the command to write a single
  message into that channel
This commit is contained in:
2026-10-16 11:09:24 +02:00
parent be915770b1
commit 8a86ebde83
10 changed files with 75 additions and 12 deletions
@@ -580,7 +580,9 @@ CHAT_GLOBAL = §8» {7}{2}
CHAT_TEAM_SENDER = §e{0} CHAT_TEAM_SENDER = §e{0}
CHAT_TEAM = §8» §f{2} CHAT_TEAM = §8» §f{2}
CHAT_MSG_SENDER = §e{0} CHAT_MSG_SENDER = §e{0}
CHAT_MSG = §8»§e{1} §7{2} CHAT_MSG_SEPARATOR = §8»
CHAT_MSG_RECEIVER = §e{0}
CHAT_MSG = §7{0}
CHAT_SPECIAL_ALERT = §e{0}§8»§e{1} §7{2} CHAT_SPECIAL_ALERT = §e{0}§8»§e{1} §7{2}
CHAT_FIGHT_PARTICIPANT_SENDER = {0}{1} CHAT_FIGHT_PARTICIPANT_SENDER = {0}{1}
CHAT_FIGHT_PARTICIPANT = §8» §7{0} CHAT_FIGHT_PARTICIPANT = §8» §7{0}
@@ -545,7 +545,9 @@ CHAT_GLOBAL = §8» {7}{2}
CHAT_TEAM_SENDER = §e{0} CHAT_TEAM_SENDER = §e{0}
CHAT_TEAM = §8» §f{2} CHAT_TEAM = §8» §f{2}
CHAT_MSG_SENDER = §e{0} CHAT_MSG_SENDER = §e{0}
CHAT_MSG = §8»§e{1} §7{2} CHAT_MSG_SEPARATOR = §8»
CHAT_MSG_RECEIVER = §e{0}
CHAT_MSG = §7{0}
CHAT_SPECIAL_ALERT = §e{0}§8»§e{1} §7{2} CHAT_SPECIAL_ALERT = §e{0}§8»§e{1} §7{2}
CHAT_FIGHT_PARTICIPANT_SENDER = {0}{1} CHAT_FIGHT_PARTICIPANT_SENDER = {0}{1}
CHAT_FIGHT_PARTICIPANT = §8» §7{0} CHAT_FIGHT_PARTICIPANT = §8» §7{0}
@@ -25,6 +25,7 @@ import de.steamwar.sql.AuditLog;
import de.steamwar.sql.SteamwarUser; import de.steamwar.sql.SteamwarUser;
import de.steamwar.sql.UserPerm; import de.steamwar.sql.UserPerm;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.event.HoverEvent;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@@ -64,6 +65,28 @@ public abstract class ChatChannel {
return null; return null;
} }
/**
* @return the text put into the chat input when clicking the channel icon to write a single message, null if there is none
*/
protected String suggestion(Chatter receiver) {
return null;
}
/**
* @return the channel icon with its name on hover and its write command on click
*/
Component iconComponent(Chatter receiver, String color) {
Message icon = icon(receiver);
Component component = icon == null ? receiver.parse(tag(receiver)) : receiver.parse(new Message("CHAT_ICON", color, icon));
component = component.hoverEvent(HoverEvent.showText(receiver.parse(hover(receiver))));
String suggestion = suggestion(receiver);
if (suggestion != null) {
component = component.clickEvent(ClickEvent.suggestCommand(suggestion));
}
return component;
}
public abstract Stream<Chatter> readers(); public abstract Stream<Chatter> readers();
/** /**
@@ -107,11 +130,9 @@ public abstract class ChatChannel {
if (!receiver.chatShown()) continue; if (!receiver.chatShown()) continue;
Component msg = format(receiver, sender, coloredMessage); Component msg = format(receiver, sender, coloredMessage);
Message icon = icon(receiver); if (icon(receiver) != null) {
if (icon != null) { Component icon = iconComponent(receiver, ChatManager.getDefault(receiver) == this ? "§e" : "§7");
Component iconComponent = receiver.parse(new Message("CHAT_ICON", ChatManager.getDefault(receiver) == this ? "§e" : "§7", icon)) msg = Component.text().append(icon).append(Component.text(" ")).append(msg).build();
.hoverEvent(HoverEvent.showText(receiver.parse(hover(receiver))));
msg = Component.text().append(iconComponent).append(Component.text(" ")).append(msg).build();
} }
receiver.sendMessage(msg); receiver.sendMessage(msg);
@@ -44,7 +44,6 @@ import de.steamwar.velocitycore.listeners.BasicListener;
import de.steamwar.velocitycore.tablist.TablistManager; import de.steamwar.velocitycore.tablist.TablistManager;
import lombok.Getter; import lombok.Getter;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.HoverEvent;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@@ -368,10 +367,8 @@ public class ChatManager extends BasicListener {
* Tells the player which channel they write into, showing the channel icon with its name on hover. * Tells the player which channel they write into, showing the channel icon with its name on hover.
*/ */
public static void notifyDefault(PlayerChatter player, ChatChannel channel) { public static void notifyDefault(PlayerChatter player, ChatChannel channel) {
Message icon = channel.icon(player);
Component iconComponent = icon == null ? player.parse(channel.tag(player)) : player.parse(new Message("CHAT_ICON", "§a", icon));
player.sendMessage(Component.text() player.sendMessage(Component.text()
.append(iconComponent.hoverEvent(HoverEvent.showText(player.parse(channel.hover(player))))) .append(channel.iconComponent(player, "§a"))
.append(Component.text(" ")) .append(Component.text(" "))
.append(player.parse(new Message("CHAT_CHANNEL_DEFAULT"))) .append(player.parse(new Message("CHAT_CHANNEL_DEFAULT")))
.build()); .build());
@@ -70,6 +70,11 @@ public class DirectChannel extends ChatChannel {
return new Message("CHAT_ICON_MSG"); return new Message("CHAT_ICON_MSG");
} }
@Override
protected String suggestion(Chatter receiver) {
return "/msg " + SteamwarUser.get(partner(receiver.user().getUUID())).getUserName() + " ";
}
@Override @Override
public Stream<Chatter> readers() { public Stream<Chatter> readers() {
return Stream.of(first, second) return Stream.of(first, second)
@@ -95,7 +100,14 @@ public class DirectChannel extends ChatChannel {
@Override @Override
protected Component format(Chatter receiver, Chatter sender, String message) { protected Component format(Chatter receiver, Chatter sender, String message) {
return ChatFormat.player("CHAT_MSG", receiver, sender, Chatter.of(partner(sender.user().getUUID())), message); SteamwarUser partner = SteamwarUser.get(partner(sender.user().getUUID()));
return Component.text()
.append(ChatFormat.withTeamHover(receiver, sender.user(), receiver.parse(new Message("CHAT_MSG_SENDER", sender.user()))))
.append(receiver.parse(new Message("CHAT_MSG_SEPARATOR")))
.append(ChatFormat.withTeamHover(receiver, partner, receiver.parse(new Message("CHAT_MSG_RECEIVER", partner))))
.append(Component.text(" "))
.append(receiver.parse(new Message("CHAT_MSG", ChatFormat.highlightMentions(message, "§7", receiver))))
.build();
} }
@Override @Override
@@ -83,6 +83,12 @@ public class FightTeamChannel extends ChatChannel {
return new Message("CHAT_ICON_FIGHT_TEAM_FOREIGN", color, server.getServerInfo().getName()); return new Message("CHAT_ICON_FIGHT_TEAM_FOREIGN", color, server.getServerInfo().getName());
} }
@Override
protected String suggestion(Chatter receiver) {
// Messages starting with '+' go into the fight team chat while writing into the fight chat
return ChatManager.onServer(receiver, server) ? "+" : null;
}
@Override @Override
public Stream<Chatter> readers() { public Stream<Chatter> readers() {
List<Integer> members = members(); List<Integer> members = members();
@@ -44,6 +44,11 @@ public class GlobalChannel extends ChatChannel {
return new Message("CHAT_ICON_GLOBAL"); return new Message("CHAT_ICON_GLOBAL");
} }
@Override
protected String suggestion(Chatter receiver) {
return "/global ";
}
@Override @Override
public Stream<Chatter> readers() { public Stream<Chatter> readers() {
return Stream.concat(Stream.of(Chatter.console()), Chatter.allPlayers().filter(ChatManager::readsGlobal).map(Chatter::of).filter(chatter -> ChatManager.reads(chatter, persistId()))); return Stream.concat(Stream.of(Chatter.console()), Chatter.allPlayers().filter(ChatManager::readsGlobal).map(Chatter::of).filter(chatter -> ChatManager.reads(chatter, persistId())));
@@ -79,6 +79,14 @@ public class LocalChannel extends ChatChannel {
return new Message("CHAT_ICON_FIGHT_FOREIGN", server.getServerInfo().getName()); return new Message("CHAT_ICON_FIGHT_FOREIGN", server.getServerInfo().getName());
} }
@Override
protected String suggestion(Chatter receiver) {
if (isArena() && !ChatManager.onServer(receiver, server)) {
return "/chat " + server.getServerInfo().getName() + " msg ";
}
return "/local ";
}
@Override @Override
public Stream<Chatter> readers() { public Stream<Chatter> readers() {
Stream<Chatter> players = server.getPlayersConnected().stream().map(Chatter::of).filter(chatter -> ChatManager.reads(chatter, persistId())).map(Chatter.class::cast); Stream<Chatter> players = server.getPlayersConnected().stream().map(Chatter::of).filter(chatter -> ChatManager.reads(chatter, persistId())).map(Chatter.class::cast);
@@ -48,6 +48,11 @@ public class ServerTeamChannel extends ChatChannel {
return new Message("CHAT_ICON_SERVERTEAM"); return new Message("CHAT_ICON_SERVERTEAM");
} }
@Override
protected String suggestion(Chatter receiver) {
return "/stc ";
}
@Override @Override
public Stream<Chatter> readers() { public Stream<Chatter> readers() {
return Arrays.stream(Chatter.serverteam().getChatters()).filter(chatter -> ChatManager.reads(chatter, persistId())); return Arrays.stream(Chatter.serverteam().getChatters()).filter(chatter -> ChatManager.reads(chatter, persistId()));
@@ -54,6 +54,11 @@ public class TeamChannel extends ChatChannel {
return new Message("CHAT_ICON_TEAM"); return new Message("CHAT_ICON_TEAM");
} }
@Override
protected String suggestion(Chatter receiver) {
return "/tc ";
}
@Override @Override
public Stream<Chatter> readers() { public Stream<Chatter> readers() {
return Chatter.allPlayers() return Chatter.allPlayers()