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 net.md_5.bungee.api.ChatColor;
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.command.Command;
import org.bukkit.command.CommandExecutor;
@ -64,7 +68,11 @@ public class ChatCommands implements CommandExecutor {
authenticated = true;
}
} else {
component = Language.getComponent("chatNoSuchRoom", ChatColor.RED, id);
if (ChatRoomLoader.validateId(id) == 0) {
component = Language.getComponent("chatNoSuchRoom", ChatColor.RED, id);
} else {
component = Language.getComponent("chatNoSuchRoomInvalidId", ChatColor.RED, id);
}
}
if (authenticated) {
@ -84,110 +92,153 @@ public class ChatCommands implements CommandExecutor {
ChatRoom chatRoom = manager.getPlayerChatRoom(player);
boolean isOwner = player.equals(chatRoom.owner);
if (args.length > 1) {
String action = args[0];
String argument = args[1];
String action = args.length > 0 ? args[0] : null;
String argument = args.length > 1 ? args[1] : null;
switch (action) {
case "create" -> {
try {
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");
} catch (ChatModule.InvalidIdException e) {
sender.sendMessage("ID is invalid: " + e.getMessage());
} catch (ChatModule.ChatRoomExistsException e) {
sender.sendMessage("Room %s already exists".formatted(argument));
} catch (IOException e) {
sender.sendMessage("Error creating room");
throw new RuntimeException(e);
}
switch (action) {
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;
}
case "delete" -> {
try {
ChatRoom newRoom = manager.createChatRoom(argument, null, player);
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) {
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) {
sender.sendMessage("Room %s already exists".formatted(argument));
} catch (IOException e) {
sender.sendMessage("Error creating room");
throw new RuntimeException(e);
}
}
case "delete" -> {
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)) {
if (isOwner) {
manager.deleteChatRoom(chatRoom);
sender.sendMessage("Room %s deleted".formatted(chatRoom.id));
} else {
sender.sendMessage("You're not the owner of %s, please enter the room you want to make changes in".formatted(chatRoom.id));
}
manager.deleteChatRoom(chatRoom);
sender.sendMessage("Room %s deleted".formatted(chatRoom.id));
} else {
sender.sendMessage("Pass %s as an argument to confirm".formatted(chatRoom.id));
sender.sendMessage("Pass \"%s\" as an argument to confirm".formatted(chatRoom.id));
}
} else {
sender.sendMessage("You're not the owner of %s, please enter the room you want to make changes in".formatted(chatRoom.id));
}
case "setowner" -> {
if (isOwner) {
Player newOwner = Bukkit.getPlayer(argument);
if (newOwner != null && newOwner.isOnline()) {
chatRoom.owner = newOwner;
try {
manager.saveChatRoom(chatRoom);
sender.sendMessage("Owner changed to " + newOwner.getName());
} catch (IOException e) {
sender.sendMessage("Error changing owner");
throw new RuntimeException(e);
}
} else {
sender.sendMessage("Player must be online");
}
} else {
sender.sendMessage("You're not the owner of %s, please enter the room you want to make changes in".formatted(chatRoom.id));
}
case "setowner" -> {
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;
}
}
case "setpassword" -> {
if (isOwner) {
chatRoom.password = Arrays.stream(args).skip(1).collect(Collectors.joining(" "));
Player newOwner = Bukkit.getPlayer(argument);
if (newOwner != null && newOwner.isOnline()) {
chatRoom.owner = newOwner;
try {
manager.saveChatRoom(chatRoom);
sender.sendMessage("Password changed");
sender.sendMessage("Owner changed to " + newOwner.getName());
} catch (IOException e) {
sender.sendMessage("Error changing password");
sender.sendMessage("Error changing owner");
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));
sender.sendMessage("Player must be online");
}
} 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" -> {
if (isOwner) {
ChatColor newColor = ChatColor.of(argument);
if (newColor != null) {
chatRoom.color = newColor;
try {
manager.saveChatRoom(chatRoom);
sender.sendMessage("Message color changed to " + newColor.getName());
} catch (IOException e) {
sender.sendMessage("Error changing color");
throw new RuntimeException(e);
}
} else {
sender.sendMessage("Invalid color");
}
case "setpassword" -> {
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(" "));
try {
manager.saveChatRoom(chatRoom);
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) {
sender.sendMessage("Error changing 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 "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" -> {
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);
if (newColor != null) {
chatRoom.color = newColor;
try {
manager.saveChatRoom(chatRoom);
sender.sendMessage("Message color changed to " + newColor.getName());
} catch (IOException e) {
sender.sendMessage("Error changing color");
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));
sender.sendMessage("Invalid color");
}
}
default -> {
sender.sendMessage("Actions: create, delete, setowner, setpassword, setcolor");
} else {
sender.sendMessage("You're not the owner of %s, please enter the room you want to make changes in".formatted(chatRoom.id));
}
}
} 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");
case null, default -> {
sender.sendMessage("Actions: create, delete, setowner, setpassword, unsetpassword, setcolor");
}
} else {
sender.sendMessage("Actions: create, delete, setowner, setpassword, setcolor");
}
}

View file

@ -153,11 +153,11 @@ public class ChatModule extends TweaksModule {
case 0:
break;
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:
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:
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)

View file

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

View file

@ -13,17 +13,18 @@ languageEnglish = English
updateAvailableNotice = Available updates (%d):
# Used in /updates
updatesNotChecked = Not checked yet
updatesNotChecked = Not checked yet.
# %s is time as HH:mm
updatesNoUpdates = No available updates. Last checked: %s
# %s is update title
updatesClickToOpen = Click to open on SpigotMC "%s"
# Used in /chat
chatPasswordProtected = This room is password protected
chatWrongPassword = Wrong password
chatNoSuchRoom = No room named %s
chatAlreadyHere = You're already in this room
chatPasswordProtected = This room is password protected.
chatWrongPassword = Wrong password.
chatNoSuchRoom = Room %s doesn't exist.
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
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
clickToCopy = Click to copy to clipboard
clickToExecuteCommand = Click to execute command
durabilityEnabled = Enabled durability alert
durabilityDisabled = Disabled durability alert