feat(chat): improve chat room management UX

- Added hover and click actions to chat commands for better interaction.
- Enhanced error and confirmation messages for clarity.
- Introduced validation for room IDs and improved handling of invalid IDs.

Signed-off-by: Minecon724 <git@m724.eu>
This commit is contained in:
Minecon724 2025-02-03 11:25:26 +01:00
parent 6ff6ec9d6b
commit 316d479fd8
Signed by: Minecon724
GPG key ID: 3CCC4D267742C8E8
4 changed files with 148 additions and 94 deletions

View file

@ -9,6 +9,10 @@ package eu.m724.tweaks.module.chat;
import eu.m724.tweaks.Language; import eu.m724.tweaks.Language;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.hover.content.Text;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
@ -64,7 +68,11 @@ public class ChatCommands implements CommandExecutor {
authenticated = true; authenticated = true;
} }
} else { } else {
if (ChatRoomLoader.validateId(id) == 0) {
component = Language.getComponent("chatNoSuchRoom", ChatColor.RED, id); component = Language.getComponent("chatNoSuchRoom", ChatColor.RED, id);
} else {
component = Language.getComponent("chatNoSuchRoomInvalidId", ChatColor.RED, id);
}
} }
if (authenticated) { if (authenticated) {
@ -84,18 +92,35 @@ public class ChatCommands implements CommandExecutor {
ChatRoom chatRoom = manager.getPlayerChatRoom(player); ChatRoom chatRoom = manager.getPlayerChatRoom(player);
boolean isOwner = player.equals(chatRoom.owner); boolean isOwner = player.equals(chatRoom.owner);
if (args.length > 1) { String action = args.length > 0 ? args[0] : null;
String action = args[0]; String argument = args.length > 1 ? args[1] : null;
String argument = args[1];
switch (action) { switch (action) {
case "create" -> { case "create" -> {
if (argument == null) {
sender.sendMessage("Please pass a room name as an argument. The room name can contain only characters and digits.");
return true;
}
try { try {
ChatRoom newRoom = manager.createChatRoom(argument, null, player); ChatRoom newRoom = manager.createChatRoom(argument, null, player);
sender.sendMessage("Created a chat room. Join it: /c " + newRoom.id);
sender.sendMessage("You might also want to protect it with a password: /cm setpassword"); var component = new ComponentBuilder("Created a chat room. Join it: ").color(ChatColor.GOLD)
.append("/c " + newRoom.id).color(ChatColor.AQUA)
.event(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/c " + newRoom.id))
.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(Language.getString("clickToExecuteCommand"))))
.append("\n")
.append("To protect it with a password, join it and use ").color(ChatColor.GRAY)
.append("/cm setpassword <password>").color(ChatColor.DARK_PURPLE)
.event(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/cm setpassword "))
.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(Language.getString("clickToExecuteCommand"))));
sender.spigot().sendMessage(component.build());
} catch (ChatModule.InvalidIdException e) { } catch (ChatModule.InvalidIdException e) {
sender.sendMessage("ID is invalid: " + e.getMessage()); var component = new ComponentBuilder("ID is invalid: ").color(ChatColor.GRAY)
.append(e.getMessage()).color(ChatColor.RED);
sender.spigot().sendMessage(component.build());
} catch (ChatModule.ChatRoomExistsException e) { } catch (ChatModule.ChatRoomExistsException e) {
sender.sendMessage("Room %s already exists".formatted(argument)); sender.sendMessage("Room %s already exists".formatted(argument));
} catch (IOException e) { } catch (IOException e) {
@ -104,19 +129,29 @@ public class ChatCommands implements CommandExecutor {
} }
} }
case "delete" -> { case "delete" -> {
if (argument.equals(chatRoom.id)) {
if (isOwner) { if (isOwner) {
if (argument == null) {
sender.sendMessage("You want to delete room \"%s\". Confirm by passing the ID as an argument.".formatted(chatRoom.id));
return true;
}
if (argument.equals(chatRoom.id)) {
manager.deleteChatRoom(chatRoom); manager.deleteChatRoom(chatRoom);
sender.sendMessage("Room %s deleted".formatted(chatRoom.id)); sender.sendMessage("Room %s deleted".formatted(chatRoom.id));
} else { } else {
sender.sendMessage("You're not the owner of %s, please enter the room you want to make changes in".formatted(chatRoom.id)); sender.sendMessage("Pass \"%s\" as an argument to confirm".formatted(chatRoom.id));
} }
} else { } else {
sender.sendMessage("Pass %s as an argument to confirm".formatted(chatRoom.id)); sender.sendMessage("You're not the owner of %s, please enter the room you want to make changes in".formatted(chatRoom.id));
} }
} }
case "setowner" -> { case "setowner" -> {
if (isOwner) { if (isOwner) {
if (argument == null) {
sender.sendMessage("To transfer ownership of room %s, pass the new owner name as an argument for this action.".formatted(chatRoom.id));
return true;
}
Player newOwner = Bukkit.getPlayer(argument); Player newOwner = Bukkit.getPlayer(argument);
if (newOwner != null && newOwner.isOnline()) { if (newOwner != null && newOwner.isOnline()) {
chatRoom.owner = newOwner; chatRoom.owner = newOwner;
@ -136,10 +171,25 @@ public class ChatCommands implements CommandExecutor {
} }
case "setpassword" -> { case "setpassword" -> {
if (isOwner) { if (isOwner) {
if (argument == null) {
sender.sendMessage("To change the password of room %s, pass the new password as an argument for this action.".formatted(chatRoom.id));
return true;
}
chatRoom.password = Arrays.stream(args).skip(1).collect(Collectors.joining(" ")); chatRoom.password = Arrays.stream(args).skip(1).collect(Collectors.joining(" "));
try { try {
manager.saveChatRoom(chatRoom); manager.saveChatRoom(chatRoom);
sender.sendMessage("Password changed");
var component = new ComponentBuilder("Password changed to ").color(ChatColor.GREEN)
.append("(hover to view)").color(ChatColor.AQUA)
.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(chatRoom.password)))
.append("\n")
.append("To unset, ").color(ChatColor.GRAY)
.append("/cm unsetpassword").color(ChatColor.DARK_PURPLE)
.event(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/cm unsetpassword"))
.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(Language.getString("clickToExecuteCommand"))));
sender.spigot().sendMessage(component.build());
} catch (IOException e) { } catch (IOException e) {
sender.sendMessage("Error changing password"); sender.sendMessage("Error changing password");
throw new RuntimeException(e); throw new RuntimeException(e);
@ -148,8 +198,27 @@ public class ChatCommands implements CommandExecutor {
sender.sendMessage("You're not the owner of %s, please enter the room you want to make changes in".formatted(chatRoom.id)); sender.sendMessage("You're not the owner of %s, please enter the room you want to make changes in".formatted(chatRoom.id));
} }
} }
case "unsetpassword" -> {
if (isOwner) {
chatRoom.password = null;
try {
manager.saveChatRoom(chatRoom);
sender.sendMessage("Password removed from " + chatRoom.id);
} catch (IOException e) {
sender.sendMessage("Error removing password");
throw new RuntimeException(e);
}
} else {
sender.sendMessage("You're not the owner of %s, please enter the room you want to make changes in".formatted(chatRoom.id));
}
}
case "setcolor" -> { case "setcolor" -> {
if (isOwner) { if (isOwner) {
if (argument == null) {
sender.sendMessage("To change the message color of room %s, pass the new color as an argument for this action. #hex or color name.".formatted(chatRoom.id));
return true;
}
ChatColor newColor = ChatColor.of(argument); ChatColor newColor = ChatColor.of(argument);
if (newColor != null) { if (newColor != null) {
chatRoom.color = newColor; chatRoom.color = newColor;
@ -167,28 +236,10 @@ public class ChatCommands implements CommandExecutor {
sender.sendMessage("You're not the owner of %s, please enter the room you want to make changes in".formatted(chatRoom.id)); sender.sendMessage("You're not the owner of %s, please enter the room you want to make changes in".formatted(chatRoom.id));
} }
} }
default -> { case null, default -> {
sender.sendMessage("Actions: create, delete, setowner, setpassword, setcolor"); sender.sendMessage("Actions: create, delete, setowner, setpassword, unsetpassword, setcolor");
} }
} }
} else if (args.length > 0) {
switch (args[0]) {
case "create" ->
sender.sendMessage("Please pass a room name as an argument. The room name must be of characters and digits.");
case "delete" ->
sender.sendMessage("You want to delete room %s. Confirm by passing its name as an argument for this action.".formatted(chatRoom.id));
case "setowner" ->
sender.sendMessage("To transfer ownership of room %s, pass the new owner name as an argument for this action.".formatted(chatRoom.id));
case "setpassword" ->
sender.sendMessage("To change the password of room %s, pass the new password as an argument for this action.".formatted(chatRoom.id));
case "setcolor" ->
sender.sendMessage("To change the message color of room %s, pass the new color as an argument for this action. #hex or color name.".formatted(chatRoom.id));
default ->
sender.sendMessage("Actions: create, delete, setowner, setpassword, setcolor");
}
} else {
sender.sendMessage("Actions: create, delete, setowner, setpassword, setcolor");
}
} }
return true; return true;

View file

@ -153,11 +153,11 @@ public class ChatModule extends TweaksModule {
case 0: case 0:
break; break;
case 1: case 1:
throw new InvalidIdException("ID is too short, make it at least 2 chars"); throw new InvalidIdException("ID is too short, it must be at least 2 chars long");
case 2: case 2:
throw new InvalidIdException("ID is too long, make it 20 chars or shorter"); throw new InvalidIdException("ID is too long, it mustn't be longer than 20 chars");
case 4: case 4:
throw new InvalidIdException("ID must be composed from characters a-z and numbers 0-9"); throw new InvalidIdException("ID must be of characters a-z and numbers 0-9");
} }
if (getById(id) != null) if (getById(id) != null)

View file

@ -29,7 +29,7 @@ public class ChatRoomLoader {
*/ */
static File getFile(String id) { static File getFile(String id) {
if (validateId(id) != 0) if (validateId(id) != 0)
throw new RuntimeException("Invalid id: " + id); return null;
return new File(chatRoomsDir, id + ".yml"); return new File(chatRoomsDir, id + ".yml");
} }
@ -66,7 +66,8 @@ public class ChatRoomLoader {
*/ */
static ChatRoom load(String id) { static ChatRoom load(String id) {
File chatRoomFile = getFile(id); File chatRoomFile = getFile(id);
if (!chatRoomFile.exists()) return null; if (chatRoomFile == null || !chatRoomFile.exists())
return null;
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(chatRoomFile); YamlConfiguration configuration = YamlConfiguration.loadConfiguration(chatRoomFile);

View file

@ -13,17 +13,18 @@ languageEnglish = English
updateAvailableNotice = Available updates (%d): updateAvailableNotice = Available updates (%d):
# Used in /updates # Used in /updates
updatesNotChecked = Not checked yet updatesNotChecked = Not checked yet.
# %s is time as HH:mm # %s is time as HH:mm
updatesNoUpdates = No available updates. Last checked: %s updatesNoUpdates = No available updates. Last checked: %s
# %s is update title # %s is update title
updatesClickToOpen = Click to open on SpigotMC "%s" updatesClickToOpen = Click to open on SpigotMC "%s"
# Used in /chat # Used in /chat
chatPasswordProtected = This room is password protected chatPasswordProtected = This room is password protected.
chatWrongPassword = Wrong password chatWrongPassword = Wrong password.
chatNoSuchRoom = No room named %s chatNoSuchRoom = Room %s doesn't exist.
chatAlreadyHere = You're already in this room chatNoSuchRoomInvalidId = Room %s doesn't exist, because the ID is invalid.
chatAlreadyHere = You're already in this room.
# Used when a player joins using the wrong key or no key # Used when a player joins using the wrong key or no key
authKickWrongKey = You're connecting to the wrong server address. You must connect to the one you're registered to. authKickWrongKey = You're connecting to the wrong server address. You must connect to the one you're registered to.
@ -34,6 +35,7 @@ authKickError = An error occured. Please try again. If this persists, contact an
redstoneGatewayItem = Redstone gateway redstoneGatewayItem = Redstone gateway
clickToCopy = Click to copy to clipboard clickToCopy = Click to copy to clipboard
clickToExecuteCommand = Click to execute command
durabilityEnabled = Enabled durability alert durabilityEnabled = Enabled durability alert
durabilityDisabled = Disabled durability alert durabilityDisabled = Disabled durability alert