janky ahh ui + support for /msg with text and /r with text
This commit is contained in:
@@ -124,7 +124,7 @@ public final class SocialPlugin extends JavaPlugin {
|
|||||||
|
|
||||||
getCommandRegistry().registerCommand(new FriendCommand(friends, resolver));
|
getCommandRegistry().registerCommand(new FriendCommand(friends, resolver));
|
||||||
getCommandRegistry().registerCommand(new GuildCommand(guilds, resolver, chatTargets));
|
getCommandRegistry().registerCommand(new GuildCommand(guilds, resolver, chatTargets));
|
||||||
getCommandRegistry().registerCommand(new MsgCommand(chatTargets, resolver));
|
getCommandRegistry().registerCommand(new MsgCommand(chatTargets, resolver, dms));
|
||||||
getCommandRegistry().registerCommand(new ReplyCommand(chatTargets, dms, presence));
|
getCommandRegistry().registerCommand(new ReplyCommand(chatTargets, dms, presence));
|
||||||
getCommandRegistry().registerCommand(new ChatCommand(chatTargets));
|
getCommandRegistry().registerCommand(new ChatCommand(chatTargets));
|
||||||
getCommandRegistry().registerCommand(new PrivacyCommand(privacy));
|
getCommandRegistry().registerCommand(new PrivacyCommand(privacy));
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.hypixel.hytale.component.Ref;
|
|||||||
import com.hypixel.hytale.component.Store;
|
import com.hypixel.hytale.component.Store;
|
||||||
import com.hypixel.hytale.server.core.Message;
|
import com.hypixel.hytale.server.core.Message;
|
||||||
import com.hypixel.hytale.server.core.command.system.CommandContext;
|
import com.hypixel.hytale.server.core.command.system.CommandContext;
|
||||||
|
import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg;
|
||||||
import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg;
|
import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg;
|
||||||
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes;
|
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes;
|
||||||
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand;
|
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand;
|
||||||
@@ -13,27 +14,34 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
|||||||
import net.kewwbec.networkcore.api.PlayerLocation;
|
import net.kewwbec.networkcore.api.PlayerLocation;
|
||||||
import net.kewwbec.social.SocialPermissionsNodes;
|
import net.kewwbec.social.SocialPermissionsNodes;
|
||||||
import net.kewwbec.social.service.ChatTargetService;
|
import net.kewwbec.social.service.ChatTargetService;
|
||||||
|
import net.kewwbec.social.service.DmRoutingService;
|
||||||
import net.kewwbec.social.util.PlayerResolver;
|
import net.kewwbec.social.util.PlayerResolver;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* /msg <name> enters one-shot DM mode for the caller. The very next chat message they send is
|
* /msg <name> [message...]
|
||||||
* routed to the named player via SocialChatRouter, then their chat falls back to NORMAL.
|
|
||||||
*
|
*
|
||||||
* Hytale args are single-token so we can't take the body as part of the command.
|
* If a message body is supplied, sends it once and leaves chat mode alone.
|
||||||
|
* If only a name is supplied, enters one-shot DM mode for the caller - the next chat message they
|
||||||
|
* send is routed as a DM to that target, then the mode reverts to NORMAL.
|
||||||
*/
|
*/
|
||||||
public final class MsgCommand extends AbstractPlayerCommand {
|
public final class MsgCommand extends AbstractPlayerCommand {
|
||||||
|
|
||||||
private final ChatTargetService targets;
|
private final ChatTargetService targets;
|
||||||
private final PlayerResolver resolver;
|
private final PlayerResolver resolver;
|
||||||
|
private final DmRoutingService dms;
|
||||||
private final RequiredArg<String> nameArg = withRequiredArg("name", "Recipient", ArgTypes.STRING);
|
private final RequiredArg<String> nameArg = withRequiredArg("name", "Recipient", ArgTypes.STRING);
|
||||||
|
private final OptionalArg<String> bodyArg = withOptionalArg("message", "Message body (optional)", ArgTypes.GREEDY_STRING);
|
||||||
|
|
||||||
public MsgCommand(@Nonnull ChatTargetService targets, @Nonnull PlayerResolver resolver) {
|
public MsgCommand(@Nonnull ChatTargetService targets,
|
||||||
super("msg", "Start a DM (your next chat message goes to that player)");
|
@Nonnull PlayerResolver resolver,
|
||||||
|
@Nonnull DmRoutingService dms) {
|
||||||
|
super("msg", "Send a one-shot DM (or enter DM mode if no message given)");
|
||||||
this.targets = targets;
|
this.targets = targets;
|
||||||
this.resolver = resolver;
|
this.resolver = resolver;
|
||||||
|
this.dms = dms;
|
||||||
requirePermission(SocialPermissionsNodes.DM_USE);
|
requirePermission(SocialPermissionsNodes.DM_USE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +51,7 @@ public final class MsgCommand extends AbstractPlayerCommand {
|
|||||||
protected void execute(@Nonnull CommandContext ctx, @Nonnull Store<EntityStore> store, @Nonnull Ref<EntityStore> ref, @Nonnull PlayerRef sender, @Nonnull World world) {
|
protected void execute(@Nonnull CommandContext ctx, @Nonnull Store<EntityStore> store, @Nonnull Ref<EntityStore> ref, @Nonnull PlayerRef sender, @Nonnull World world) {
|
||||||
String name = nameArg.get(ctx);
|
String name = nameArg.get(ctx);
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
ctx.sendMessage(Message.raw("Usage: /msg <name>").color(Color.YELLOW));
|
ctx.sendMessage(Message.raw("Usage: /msg <name> [message...]").color(Color.YELLOW));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PlayerLocation target = resolver.resolve(name);
|
PlayerLocation target = resolver.resolve(name);
|
||||||
@@ -51,7 +59,28 @@ public final class MsgCommand extends AbstractPlayerCommand {
|
|||||||
ctx.sendMessage(Message.raw(name + " is not online.").color(Color.RED));
|
ctx.sendMessage(Message.raw(name + " is not online.").color(Color.RED));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String body = bodyArg.get(ctx);
|
||||||
|
if (body != null && !body.isBlank()) {
|
||||||
|
DmRoutingService.Result r = dms.send(sender, target.username(), body.trim());
|
||||||
|
if (r != DmRoutingService.Result.OK) {
|
||||||
|
ctx.sendMessage(Message.raw(errorText(r, target.username())).color(Color.RED));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
targets.setDm(sender.getUuid(), target.uuid(), target.username());
|
targets.setDm(sender.getUuid(), target.uuid(), target.username());
|
||||||
ctx.sendMessage(Message.raw("DM mode -> " + target.username() + ". Type your next chat to send.").color(Color.LIGHT_GRAY));
|
ctx.sendMessage(Message.raw("DM mode -> " + target.username() + ". Type your next chat to send.").color(Color.LIGHT_GRAY));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private static String errorText(@Nonnull DmRoutingService.Result r, @Nonnull String targetName) {
|
||||||
|
return switch (r) {
|
||||||
|
case SELF -> "You can't DM yourself.";
|
||||||
|
case TARGET_OFFLINE -> targetName + " is not online.";
|
||||||
|
case BLOCKED_BY_PRIVACY -> "You can't DM " + targetName + " right now.";
|
||||||
|
case DB_ERROR -> "DB error sending DM.";
|
||||||
|
case OK -> "";
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import com.hypixel.hytale.component.Ref;
|
|||||||
import com.hypixel.hytale.component.Store;
|
import com.hypixel.hytale.component.Store;
|
||||||
import com.hypixel.hytale.server.core.Message;
|
import com.hypixel.hytale.server.core.Message;
|
||||||
import com.hypixel.hytale.server.core.command.system.CommandContext;
|
import com.hypixel.hytale.server.core.command.system.CommandContext;
|
||||||
|
import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg;
|
||||||
|
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes;
|
||||||
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand;
|
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand;
|
||||||
import com.hypixel.hytale.server.core.universe.PlayerRef;
|
import com.hypixel.hytale.server.core.universe.PlayerRef;
|
||||||
import com.hypixel.hytale.server.core.universe.world.World;
|
import com.hypixel.hytale.server.core.universe.world.World;
|
||||||
@@ -19,18 +21,23 @@ import java.awt.Color;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* /r enters DM mode targeting whoever DMed you last. Then your next chat goes to them.
|
* /r [message...]
|
||||||
|
*
|
||||||
|
* If a body is supplied, sends it once to whoever DMed you last.
|
||||||
|
* If no body, enters one-shot DM mode targeting that player - the next chat message you send goes
|
||||||
|
* to them, then mode reverts to NORMAL.
|
||||||
*/
|
*/
|
||||||
public final class ReplyCommand extends AbstractPlayerCommand {
|
public final class ReplyCommand extends AbstractPlayerCommand {
|
||||||
|
|
||||||
private final ChatTargetService targets;
|
private final ChatTargetService targets;
|
||||||
private final DmRoutingService dms;
|
private final DmRoutingService dms;
|
||||||
private final PlayerPresence presence;
|
private final PlayerPresence presence;
|
||||||
|
private final OptionalArg<String> bodyArg = withOptionalArg("message", "Message body (optional)", ArgTypes.GREEDY_STRING);
|
||||||
|
|
||||||
public ReplyCommand(@Nonnull ChatTargetService targets,
|
public ReplyCommand(@Nonnull ChatTargetService targets,
|
||||||
@Nonnull DmRoutingService dms,
|
@Nonnull DmRoutingService dms,
|
||||||
@Nonnull PlayerPresence presence) {
|
@Nonnull PlayerPresence presence) {
|
||||||
super("r", "Reply to your last DM sender");
|
super("r", "Reply to your last DM sender (one-shot send, or enter DM mode if no body given)");
|
||||||
this.targets = targets;
|
this.targets = targets;
|
||||||
this.dms = dms;
|
this.dms = dms;
|
||||||
this.presence = presence;
|
this.presence = presence;
|
||||||
@@ -51,7 +58,28 @@ public final class ReplyCommand extends AbstractPlayerCommand {
|
|||||||
ctx.sendMessage(Message.raw("That person is offline.").color(Color.RED));
|
ctx.sendMessage(Message.raw("That person is offline.").color(Color.RED));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String body = bodyArg.get(ctx);
|
||||||
|
if (body != null && !body.isBlank()) {
|
||||||
|
DmRoutingService.Result r = dms.send(sender, target.username(), body.trim());
|
||||||
|
if (r != DmRoutingService.Result.OK) {
|
||||||
|
ctx.sendMessage(Message.raw(errorText(r, target.username())).color(Color.RED));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
targets.setDm(sender.getUuid(), target.uuid(), target.username());
|
targets.setDm(sender.getUuid(), target.uuid(), target.username());
|
||||||
ctx.sendMessage(Message.raw("Reply -> " + target.username() + ". Type your next chat to send.").color(Color.LIGHT_GRAY));
|
ctx.sendMessage(Message.raw("Reply -> " + target.username() + ". Type your next chat to send.").color(Color.LIGHT_GRAY));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private static String errorText(@Nonnull DmRoutingService.Result r, @Nonnull String targetName) {
|
||||||
|
return switch (r) {
|
||||||
|
case SELF -> "You can't DM yourself.";
|
||||||
|
case TARGET_OFFLINE -> targetName + " is not online.";
|
||||||
|
case BLOCKED_BY_PRIVACY -> "You can't DM " + targetName + " right now.";
|
||||||
|
case DB_ERROR -> "DB error sending DM.";
|
||||||
|
case OK -> "";
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import net.kewwbec.networkcore.NetworkCore;
|
|||||||
import net.kewwbec.networkcore.api.PlayerLocation;
|
import net.kewwbec.networkcore.api.PlayerLocation;
|
||||||
import net.kewwbec.networkcore.api.PlayerPresence;
|
import net.kewwbec.networkcore.api.PlayerPresence;
|
||||||
import net.kewwbec.social.model.Friendship;
|
import net.kewwbec.social.model.Friendship;
|
||||||
import net.kewwbec.social.service.FriendService;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
@@ -22,88 +21,82 @@ import java.util.UUID;
|
|||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Three tabs (Friends / Incoming / Outgoing) rendered with HyUI's native tab system, so switching
|
||||||
|
* tabs is purely client-side - no page rebuild, no flicker. Row action buttons (remove, accept,
|
||||||
|
* reject, cancel) trigger a rebuild after the action so the lists refresh.
|
||||||
|
*/
|
||||||
public final class FriendsListPage {
|
public final class FriendsListPage {
|
||||||
|
|
||||||
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
|
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
|
||||||
|
|
||||||
public enum Tab { FRIENDS, INCOMING, OUTGOING }
|
|
||||||
|
|
||||||
private final PlayerRef playerRef;
|
private final PlayerRef playerRef;
|
||||||
private final SocialUIContext ctx;
|
private final SocialUIContext ctx;
|
||||||
private final Tab tab;
|
|
||||||
private final PageBuilder page;
|
private final PageBuilder page;
|
||||||
|
|
||||||
public FriendsListPage(@Nonnull PlayerRef playerRef, @Nonnull SocialUIContext ctx) {
|
public FriendsListPage(@Nonnull PlayerRef playerRef, @Nonnull SocialUIContext ctx) {
|
||||||
this(playerRef, ctx, Tab.FRIENDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
public FriendsListPage(@Nonnull PlayerRef playerRef, @Nonnull SocialUIContext ctx, @Nonnull Tab tab) {
|
|
||||||
this.playerRef = playerRef;
|
this.playerRef = playerRef;
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.tab = tab;
|
|
||||||
|
|
||||||
StringBuilder body = new StringBuilder();
|
|
||||||
body.append("<p>Tabs:</p>");
|
|
||||||
body.append("<button id=\"tabFriends\">").append(marker(tab, Tab.FRIENDS)).append("Friends</button>");
|
|
||||||
body.append("<button id=\"tabIncoming\">").append(marker(tab, Tab.INCOMING)).append("Incoming</button>");
|
|
||||||
body.append("<button id=\"tabOutgoing\">").append(marker(tab, Tab.OUTGOING)).append("Outgoing</button>");
|
|
||||||
body.append("<p> </p>");
|
|
||||||
|
|
||||||
List<RowAction> actions = new ArrayList<>();
|
List<RowAction> actions = new ArrayList<>();
|
||||||
|
String friendsBody;
|
||||||
|
String incomingBody;
|
||||||
|
String outgoingBody;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch (tab) {
|
friendsBody = renderFriends(actions);
|
||||||
case FRIENDS -> renderFriends(body, actions);
|
incomingBody = renderIncoming(actions);
|
||||||
case INCOMING -> renderIncoming(body, actions);
|
outgoingBody = renderOutgoing(actions);
|
||||||
case OUTGOING -> renderOutgoing(body, actions);
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
((HytaleLogger.Api) LOGGER.at(Level.WARNING).withCause(e)).log("Friends panel render failed");
|
((HytaleLogger.Api) LOGGER.at(Level.WARNING).withCause(e)).log("Friends panel render failed");
|
||||||
body.append("<p>DB error: ").append(escape(e.getMessage())).append("</p>");
|
String err = "<p>DB error: " + escape(e.getMessage()) + "</p>";
|
||||||
|
friendsBody = err;
|
||||||
|
incomingBody = err;
|
||||||
|
outgoingBody = err;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.append("<p> </p>");
|
String body = """
|
||||||
body.append("<button id=\"backBtn\">Back to Social</button>");
|
<nav id="friendsTabs" class="tabs"
|
||||||
|
data-tabs="friends:Friends:friendsContent,incoming:Incoming:incomingContent,outgoing:Outgoing:outgoingContent"
|
||||||
String html = """
|
data-selected="friends"></nav>
|
||||||
<div class="page-overlay">
|
<div id="friendsContent" class="tab-content" data-hyui-tab-id="friends">
|
||||||
<div class="container" data-hyui-title="Friends">
|
%s
|
||||||
<div class="container-contents">
|
|
||||||
%s
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
""".formatted(body.toString());
|
<div id="incomingContent" class="tab-content" data-hyui-tab-id="incoming">
|
||||||
|
%s
|
||||||
|
</div>
|
||||||
|
<div id="outgoingContent" class="tab-content" data-hyui-tab-id="outgoing">
|
||||||
|
%s
|
||||||
|
</div>
|
||||||
|
<p> </p>
|
||||||
|
<button id="backBtn">Back to Social</button>
|
||||||
|
""".formatted(friendsBody, incomingBody, outgoingBody);
|
||||||
|
|
||||||
|
String html = PageLayout.wrap("Friends", body);
|
||||||
|
|
||||||
this.page = PageBuilder.pageForPlayer(playerRef)
|
this.page = PageBuilder.pageForPlayer(playerRef)
|
||||||
.withLifetime(CustomPageLifetime.CanDismiss)
|
.withLifetime(CustomPageLifetime.CanDismiss)
|
||||||
.fromHtml(html);
|
.fromHtml(html);
|
||||||
|
|
||||||
page.addEventListener("tabFriends", CustomUIEventBindingType.Activating, (ignored, pCtx) ->
|
|
||||||
openInStore(s -> new FriendsListPage(playerRef, ctx, Tab.FRIENDS).open(s)));
|
|
||||||
page.addEventListener("tabIncoming", CustomUIEventBindingType.Activating, (ignored, pCtx) ->
|
|
||||||
openInStore(s -> new FriendsListPage(playerRef, ctx, Tab.INCOMING).open(s)));
|
|
||||||
page.addEventListener("tabOutgoing", CustomUIEventBindingType.Activating, (ignored, pCtx) ->
|
|
||||||
openInStore(s -> new FriendsListPage(playerRef, ctx, Tab.OUTGOING).open(s)));
|
|
||||||
page.addEventListener("backBtn", CustomUIEventBindingType.Activating, (ignored, pCtx) ->
|
page.addEventListener("backBtn", CustomUIEventBindingType.Activating, (ignored, pCtx) ->
|
||||||
openInStore(s -> new SocialHubPage(playerRef, ctx).open(s)));
|
openInStore(s -> new SocialHubPage(playerRef, ctx).open(s)));
|
||||||
|
|
||||||
for (RowAction a : actions) {
|
for (RowAction a : actions) {
|
||||||
String btnId = a.buttonId;
|
page.addEventListener(a.buttonId, CustomUIEventBindingType.Activating, (ignored, pCtx) -> {
|
||||||
Tab cur = this.tab;
|
|
||||||
page.addEventListener(btnId, CustomUIEventBindingType.Activating, (ignored, pCtx) -> {
|
|
||||||
a.runnable.run();
|
a.runnable.run();
|
||||||
openInStore(s -> new FriendsListPage(playerRef, ctx, cur).open(s));
|
openInStore(s -> new FriendsListPage(playerRef, ctx).open(s));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderFriends(@Nonnull StringBuilder body, @Nonnull List<RowAction> actions) throws SQLException {
|
@Nonnull
|
||||||
|
private String renderFriends(@Nonnull List<RowAction> actions) throws SQLException {
|
||||||
List<Friendship> friends = ctx.friends.list(playerRef.getUuid());
|
List<Friendship> friends = ctx.friends.list(playerRef.getUuid());
|
||||||
body.append("<p><b>Friends (").append(friends.size()).append(")</b></p>");
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("<p><b>Friends (").append(friends.size()).append(")</b></p>");
|
||||||
if (friends.isEmpty()) {
|
if (friends.isEmpty()) {
|
||||||
body.append("<p>None yet. Use /friend add <name> to send a request.</p>");
|
sb.append("<p>None yet. Use /friend add <name> to send a request.</p>");
|
||||||
return;
|
return sb.toString();
|
||||||
}
|
}
|
||||||
PlayerPresence presence = NetworkCore.getInstance().getPlayerPresence();
|
PlayerPresence presence = NetworkCore.getInstance().getPlayerPresence();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@@ -112,9 +105,9 @@ public final class FriendsListPage {
|
|||||||
String otherName = f.fromUuid().equals(playerRef.getUuid()) ? f.toName() : f.fromName();
|
String otherName = f.fromUuid().equals(playerRef.getUuid()) ? f.toName() : f.fromName();
|
||||||
PlayerLocation loc = (presence == null) ? null : presence.getLocation(otherUuid);
|
PlayerLocation loc = (presence == null) ? null : presence.getLocation(otherUuid);
|
||||||
String status = (loc == null) ? "offline" : "on " + loc.serverId();
|
String status = (loc == null) ? "offline" : "on " + loc.serverId();
|
||||||
body.append("<p>").append(escape(otherName)).append(" (").append(escape(status)).append(")</p>");
|
sb.append("<p>").append(escape(otherName)).append(" (").append(escape(status)).append(")</p>");
|
||||||
String removeId = "removeFriend" + i;
|
String removeId = "removeFriend" + i;
|
||||||
body.append("<button id=\"").append(removeId).append("\">Remove ").append(escape(otherName)).append("</button>");
|
sb.append("<button id=\"").append(removeId).append("\" class=\"secondary-button\">Remove ").append(escape(otherName)).append("</button>");
|
||||||
UUID finalOther = otherUuid;
|
UUID finalOther = otherUuid;
|
||||||
String finalName = otherName;
|
String finalName = otherName;
|
||||||
actions.add(new RowAction(removeId, () ->
|
actions.add(new RowAction(removeId, () ->
|
||||||
@@ -122,24 +115,27 @@ public final class FriendsListPage {
|
|||||||
));
|
));
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderIncoming(@Nonnull StringBuilder body, @Nonnull List<RowAction> actions) throws SQLException {
|
@Nonnull
|
||||||
|
private String renderIncoming(@Nonnull List<RowAction> actions) throws SQLException {
|
||||||
List<Friendship> incoming = ctx.friends.incomingPending(playerRef.getUuid());
|
List<Friendship> incoming = ctx.friends.incomingPending(playerRef.getUuid());
|
||||||
body.append("<p><b>Incoming requests (").append(incoming.size()).append(")</b></p>");
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("<p><b>Incoming requests (").append(incoming.size()).append(")</b></p>");
|
||||||
if (incoming.isEmpty()) {
|
if (incoming.isEmpty()) {
|
||||||
body.append("<p>No one has sent you a friend request.</p>");
|
sb.append("<p>No one has sent you a friend request.</p>");
|
||||||
return;
|
return sb.toString();
|
||||||
}
|
}
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (Friendship f : incoming) {
|
for (Friendship f : incoming) {
|
||||||
UUID fromUuid = f.fromUuid();
|
UUID fromUuid = f.fromUuid();
|
||||||
String fromName = f.fromName();
|
String fromName = f.fromName();
|
||||||
body.append("<p>from ").append(escape(fromName)).append("</p>");
|
sb.append("<p>from ").append(escape(fromName)).append("</p>");
|
||||||
String acceptId = "accept" + i;
|
String acceptId = "accept" + i;
|
||||||
String rejectId = "reject" + i;
|
String rejectId = "reject" + i;
|
||||||
body.append("<button id=\"").append(acceptId).append("\">Accept ").append(escape(fromName)).append("</button>");
|
sb.append("<button id=\"").append(acceptId).append("\">Accept ").append(escape(fromName)).append("</button>");
|
||||||
body.append("<button id=\"").append(rejectId).append("\">Reject ").append(escape(fromName)).append("</button>");
|
sb.append("<button id=\"").append(rejectId).append("\" class=\"secondary-button\">Reject ").append(escape(fromName)).append("</button>");
|
||||||
actions.add(new RowAction(acceptId, () ->
|
actions.add(new RowAction(acceptId, () ->
|
||||||
ctx.friends.accept(fromUuid, fromName, playerRef.getUuid(), playerRef.getUsername())
|
ctx.friends.accept(fromUuid, fromName, playerRef.getUuid(), playerRef.getUsername())
|
||||||
));
|
));
|
||||||
@@ -148,27 +144,31 @@ public final class FriendsListPage {
|
|||||||
));
|
));
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderOutgoing(@Nonnull StringBuilder body, @Nonnull List<RowAction> actions) throws SQLException {
|
@Nonnull
|
||||||
|
private String renderOutgoing(@Nonnull List<RowAction> actions) throws SQLException {
|
||||||
List<Friendship> outgoing = ctx.friends.outgoingPending(playerRef.getUuid());
|
List<Friendship> outgoing = ctx.friends.outgoingPending(playerRef.getUuid());
|
||||||
body.append("<p><b>Outgoing requests (").append(outgoing.size()).append(")</b></p>");
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("<p><b>Outgoing requests (").append(outgoing.size()).append(")</b></p>");
|
||||||
if (outgoing.isEmpty()) {
|
if (outgoing.isEmpty()) {
|
||||||
body.append("<p>No outgoing requests.</p>");
|
sb.append("<p>No outgoing requests.</p>");
|
||||||
return;
|
return sb.toString();
|
||||||
}
|
}
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (Friendship f : outgoing) {
|
for (Friendship f : outgoing) {
|
||||||
UUID toUuid = f.toUuid();
|
UUID toUuid = f.toUuid();
|
||||||
String toName = f.toName();
|
String toName = f.toName();
|
||||||
body.append("<p>to ").append(escape(toName)).append("</p>");
|
sb.append("<p>to ").append(escape(toName)).append("</p>");
|
||||||
String cancelId = "cancel" + i;
|
String cancelId = "cancel" + i;
|
||||||
body.append("<button id=\"").append(cancelId).append("\">Cancel request to ").append(escape(toName)).append("</button>");
|
sb.append("<button id=\"").append(cancelId).append("\" class=\"secondary-button\">Cancel request to ").append(escape(toName)).append("</button>");
|
||||||
actions.add(new RowAction(cancelId, () ->
|
actions.add(new RowAction(cancelId, () ->
|
||||||
ctx.friends.remove(playerRef.getUuid(), playerRef.getUsername(), toUuid, toName)
|
ctx.friends.remove(playerRef.getUuid(), playerRef.getUsername(), toUuid, toName)
|
||||||
));
|
));
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void open(@Nonnull Store<EntityStore> store) {
|
public void open(@Nonnull Store<EntityStore> store) {
|
||||||
@@ -181,11 +181,6 @@ public final class FriendsListPage {
|
|||||||
action.accept(ref.getStore());
|
action.accept(ref.getStore());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private static String marker(@Nonnull Tab current, @Nonnull Tab target) {
|
|
||||||
return current == target ? "[*] " : "[ ] ";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private static String escape(@Nonnull String s) {
|
private static String escape(@Nonnull String s) {
|
||||||
return s == null ? "" : s.replace("&", "&").replace("<", "<").replace(">", ">").replace("\"", """);
|
return s == null ? "" : s.replace("&", "&").replace("<", "<").replace(">", ">").replace("\"", """);
|
||||||
|
|||||||
@@ -0,0 +1,144 @@
|
|||||||
|
package net.kewwbec.social.ui;
|
||||||
|
|
||||||
|
import au.ellie.hyui.builders.PageBuilder;
|
||||||
|
import com.hypixel.hytale.component.Ref;
|
||||||
|
import com.hypixel.hytale.component.Store;
|
||||||
|
import com.hypixel.hytale.logger.HytaleLogger;
|
||||||
|
import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime;
|
||||||
|
import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType;
|
||||||
|
import com.hypixel.hytale.server.core.universe.PlayerRef;
|
||||||
|
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||||
|
import net.kewwbec.social.model.GuildPermission;
|
||||||
|
import net.kewwbec.social.model.GuildRank;
|
||||||
|
import net.kewwbec.social.service.GuildService;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists ranks for the caller's guild; each non-owner rank can be edited or deleted. Plus a button
|
||||||
|
* to open the form for a new rank. The owner rank is shown but locked.
|
||||||
|
*
|
||||||
|
* Requires MANAGE_RANKS on the caller's rank to actually mutate; the service enforces this.
|
||||||
|
*/
|
||||||
|
public final class GuildRankEditorPage {
|
||||||
|
|
||||||
|
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
|
||||||
|
|
||||||
|
private final PlayerRef playerRef;
|
||||||
|
private final SocialUIContext ctx;
|
||||||
|
private final PageBuilder page;
|
||||||
|
|
||||||
|
public GuildRankEditorPage(@Nonnull PlayerRef playerRef, @Nonnull SocialUIContext ctx) {
|
||||||
|
this.playerRef = playerRef;
|
||||||
|
this.ctx = ctx;
|
||||||
|
|
||||||
|
GuildService.GuildContext gc = ctx.guilds.contextFor(playerRef.getUuid());
|
||||||
|
StringBuilder body = new StringBuilder();
|
||||||
|
List<Action> actions = new ArrayList<>();
|
||||||
|
|
||||||
|
if (gc == null) {
|
||||||
|
body.append("<p>You are not in a guild.</p>");
|
||||||
|
} else {
|
||||||
|
boolean canManage = GuildPermission.MANAGE_RANKS.isIn(gc.rank().permissions());
|
||||||
|
body.append("<p>Guild: <b>").append(escape(gc.guild().name())).append("</b></p>");
|
||||||
|
body.append("<p>Your rank: <b>").append(escape(gc.rank().name())).append("</b></p>");
|
||||||
|
if (!canManage) {
|
||||||
|
body.append("<p>You can't edit ranks. Need MANAGE_RANKS permission on your rank.</p>");
|
||||||
|
}
|
||||||
|
body.append("<p> </p>");
|
||||||
|
|
||||||
|
try {
|
||||||
|
List<GuildRank> ranks = ctx.guilds.rankList(gc.guild().id());
|
||||||
|
body.append("<p><b>Ranks (highest first)</b></p>");
|
||||||
|
int i = 0;
|
||||||
|
for (GuildRank r : ranks) {
|
||||||
|
body.append("<p><b>").append(escape(r.name())).append("</b>")
|
||||||
|
.append(" - id=").append(r.rankId())
|
||||||
|
.append(" prio=").append(r.priority())
|
||||||
|
.append(r.isOwnerRank() ? " [OWNER, locked]" : "")
|
||||||
|
.append("</p>");
|
||||||
|
body.append("<p>").append(escape(describePerms(r.permissions()))).append("</p>");
|
||||||
|
|
||||||
|
if (canManage && !r.isOwnerRank()) {
|
||||||
|
String editId = "edit" + i;
|
||||||
|
String delId = "del" + i;
|
||||||
|
body.append("<button id=\"").append(editId).append("\">Edit ")
|
||||||
|
.append(escape(r.name())).append("</button>");
|
||||||
|
body.append("<button id=\"").append(delId).append("\" class=\"secondary-button\">Delete ")
|
||||||
|
.append(escape(r.name())).append("</button>");
|
||||||
|
int rankId = r.rankId();
|
||||||
|
actions.add(new Action(editId, () ->
|
||||||
|
openInStore(s -> new GuildRankFormPage(playerRef, ctx, rankId).open(s))
|
||||||
|
));
|
||||||
|
actions.add(new Action(delId, () -> {
|
||||||
|
ctx.guilds.deleteRank(playerRef.getUuid(), rankId);
|
||||||
|
openInStore(s -> new GuildRankEditorPage(playerRef, ctx).open(s));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
body.append("<p> </p>");
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
((HytaleLogger.Api) LOGGER.at(Level.WARNING).withCause(e)).log("Rank list failed");
|
||||||
|
body.append("<p>DB error: ").append(escape(e.getMessage())).append("</p>");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canManage) {
|
||||||
|
body.append("<button id=\"newBtn\">+ Create new rank</button>");
|
||||||
|
actions.add(new Action("newBtn", () ->
|
||||||
|
openInStore(s -> new GuildRankFormPage(playerRef, ctx, -1).open(s))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body.append("<p> </p>");
|
||||||
|
body.append("<button id=\"backBtn\">Back to Guild</button>");
|
||||||
|
|
||||||
|
String html = PageLayout.wrap("Guild Ranks", body.toString());
|
||||||
|
|
||||||
|
this.page = PageBuilder.pageForPlayer(playerRef)
|
||||||
|
.withLifetime(CustomPageLifetime.CanDismiss)
|
||||||
|
.fromHtml(html);
|
||||||
|
|
||||||
|
for (Action a : actions) {
|
||||||
|
page.addEventListener(a.id, CustomUIEventBindingType.Activating, (ignored, pCtx) -> a.runnable.run());
|
||||||
|
}
|
||||||
|
page.addEventListener("backBtn", CustomUIEventBindingType.Activating, (ignored, pCtx) ->
|
||||||
|
openInStore(s -> new GuildRosterPage(playerRef, ctx).open(s)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void open(@Nonnull Store<EntityStore> store) {
|
||||||
|
page.open(store);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openInStore(@Nonnull Consumer<Store<EntityStore>> action) {
|
||||||
|
Ref<EntityStore> ref = playerRef.getReference();
|
||||||
|
if (ref == null) return;
|
||||||
|
action.accept(ref.getStore());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private static String describePerms(int mask) {
|
||||||
|
if (mask == 0) return "(no permissions)";
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (GuildPermission p : GuildPermission.values()) {
|
||||||
|
if (p.isIn(mask)) {
|
||||||
|
if (sb.length() > 0) sb.append(", ");
|
||||||
|
sb.append(p.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private static String escape(@Nonnull String s) {
|
||||||
|
return s == null ? "" : s.replace("&", "&").replace("<", "<").replace(">", ">").replace("\"", """);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record Action(@Nonnull String id, @Nonnull Runnable runnable) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
package net.kewwbec.social.ui;
|
||||||
|
|
||||||
|
import au.ellie.hyui.builders.PageBuilder;
|
||||||
|
import com.hypixel.hytale.component.Ref;
|
||||||
|
import com.hypixel.hytale.component.Store;
|
||||||
|
import com.hypixel.hytale.logger.HytaleLogger;
|
||||||
|
import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime;
|
||||||
|
import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType;
|
||||||
|
import com.hypixel.hytale.server.core.universe.PlayerRef;
|
||||||
|
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||||
|
import net.kewwbec.social.model.GuildPermission;
|
||||||
|
import net.kewwbec.social.model.GuildRank;
|
||||||
|
import net.kewwbec.social.service.GuildService;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form to edit one rank (or create a new one if rankId is -1). Shows a name field, priority field,
|
||||||
|
* and a checkbox per GuildPermission. Save calls the appropriate service method; Cancel returns
|
||||||
|
* to the editor list.
|
||||||
|
*/
|
||||||
|
public final class GuildRankFormPage {
|
||||||
|
|
||||||
|
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
|
||||||
|
|
||||||
|
private final PlayerRef playerRef;
|
||||||
|
private final SocialUIContext ctx;
|
||||||
|
private final int rankId;
|
||||||
|
private final PageBuilder page;
|
||||||
|
|
||||||
|
public GuildRankFormPage(@Nonnull PlayerRef playerRef, @Nonnull SocialUIContext ctx, int rankId) {
|
||||||
|
this.playerRef = playerRef;
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.rankId = rankId;
|
||||||
|
|
||||||
|
GuildService.GuildContext gc = ctx.guilds.contextFor(playerRef.getUuid());
|
||||||
|
StringBuilder body = new StringBuilder();
|
||||||
|
String title = "Edit rank";
|
||||||
|
boolean isNew = (rankId < 0);
|
||||||
|
boolean formReady = false;
|
||||||
|
String nameValue = "";
|
||||||
|
int priorityValue = 10;
|
||||||
|
int permsValue = GuildPermission.CHAT.bit();
|
||||||
|
|
||||||
|
if (gc == null) {
|
||||||
|
body.append("<p>You are not in a guild.</p>");
|
||||||
|
} else if (isNew) {
|
||||||
|
title = "New rank";
|
||||||
|
formReady = true;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
Optional<GuildRank> existing = findRank(gc.guild().id(), rankId);
|
||||||
|
if (existing.isPresent()) {
|
||||||
|
nameValue = existing.get().name();
|
||||||
|
priorityValue = existing.get().priority();
|
||||||
|
permsValue = existing.get().permissions();
|
||||||
|
formReady = true;
|
||||||
|
} else {
|
||||||
|
body.append("<p>Rank not found.</p>");
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
((HytaleLogger.Api) LOGGER.at(Level.WARNING).withCause(e)).log("Rank lookup failed");
|
||||||
|
body.append("<p>DB error: ").append(escape(e.getMessage())).append("</p>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formReady) {
|
||||||
|
body.append("<p><b>").append(isNew ? "New rank" : "Edit rank").append("</b></p>");
|
||||||
|
body.append("<p>Name:</p>");
|
||||||
|
body.append("<input type=\"text\" id=\"nameInput\" placeholder=\"Rank name\" value=\"")
|
||||||
|
.append(escape(nameValue)).append("\" />");
|
||||||
|
body.append("<p>Priority (higher = outranks lower):</p>");
|
||||||
|
body.append("<input type=\"number\" id=\"priorityInput\" value=\"").append(priorityValue).append("\" />");
|
||||||
|
body.append("<p> </p>");
|
||||||
|
body.append("<p><b>Permissions</b></p>");
|
||||||
|
|
||||||
|
for (GuildPermission p : GuildPermission.values()) {
|
||||||
|
if (p == GuildPermission.DISBAND) continue;
|
||||||
|
boolean checked = p.isIn(permsValue);
|
||||||
|
body.append("<input type=\"checkbox\" id=\"perm").append(p.name()).append("\"")
|
||||||
|
.append(checked ? " checked=\"true\"" : "").append(" />");
|
||||||
|
body.append("<label> ").append(p.name()).append(" - ").append(describe(p)).append("</label>");
|
||||||
|
body.append("<p> </p>");
|
||||||
|
}
|
||||||
|
|
||||||
|
body.append("<button id=\"saveBtn\">").append(isNew ? "Create" : "Save").append("</button>");
|
||||||
|
}
|
||||||
|
body.append("<button id=\"backBtn\" class=\"secondary-button\">").append(formReady ? "Cancel" : "Back").append("</button>");
|
||||||
|
|
||||||
|
String html = PageLayout.wrap(title, body.toString());
|
||||||
|
this.page = PageBuilder.pageForPlayer(playerRef)
|
||||||
|
.withLifetime(CustomPageLifetime.CanDismiss)
|
||||||
|
.fromHtml(html);
|
||||||
|
|
||||||
|
page.addEventListener("backBtn", CustomUIEventBindingType.Activating, (ignored, pCtx) ->
|
||||||
|
openInStore(s -> new GuildRankEditorPage(playerRef, ctx).open(s)));
|
||||||
|
|
||||||
|
if (formReady) {
|
||||||
|
page.addEventListener("saveBtn", CustomUIEventBindingType.Activating, (ignored, pCtx) -> {
|
||||||
|
String name = pCtx.getValue("nameInput", String.class).orElse("").trim();
|
||||||
|
Integer priority = pCtx.getValue("priorityInput", Integer.class).orElse(10);
|
||||||
|
int mask = 0;
|
||||||
|
for (GuildPermission p : GuildPermission.values()) {
|
||||||
|
if (p == GuildPermission.DISBAND) continue;
|
||||||
|
boolean checked = pCtx.getValue("perm" + p.name(), Boolean.class).orElse(false);
|
||||||
|
if (checked) mask |= p.bit();
|
||||||
|
}
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (rankId < 0) {
|
||||||
|
ctx.guilds.createRank(playerRef.getUuid(), name, priority, mask);
|
||||||
|
} else {
|
||||||
|
ctx.guilds.updateRank(playerRef.getUuid(), rankId, name, priority, mask);
|
||||||
|
}
|
||||||
|
openInStore(s -> new GuildRankEditorPage(playerRef, ctx).open(s));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private Optional<GuildRank> findRank(long guildId, int targetRankId) throws SQLException {
|
||||||
|
for (GuildRank r : ctx.guilds.rankList(guildId)) {
|
||||||
|
if (r.rankId() == targetRankId) return Optional.of(r);
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void open(@Nonnull Store<EntityStore> store) {
|
||||||
|
page.open(store);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openInStore(@Nonnull Consumer<Store<EntityStore>> action) {
|
||||||
|
Ref<EntityStore> ref = playerRef.getReference();
|
||||||
|
if (ref == null) return;
|
||||||
|
action.accept(ref.getStore());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private static String describe(@Nonnull GuildPermission p) {
|
||||||
|
return switch (p) {
|
||||||
|
case INVITE -> "invite players to the guild";
|
||||||
|
case KICK -> "kick lower-ranked members";
|
||||||
|
case PROMOTE -> "promote members to a higher rank";
|
||||||
|
case DEMOTE -> "demote members to a lower rank";
|
||||||
|
case MOTD -> "change the guild MOTD";
|
||||||
|
case MANAGE_RANKS -> "create / edit / delete ranks";
|
||||||
|
case DISBAND -> "(owner only)";
|
||||||
|
case CHAT -> "speak in guild chat";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private static String escape(@Nonnull String s) {
|
||||||
|
return s == null ? "" : s.replace("&", "&").replace("<", "<").replace(">", ">").replace("\"", """);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -102,17 +102,11 @@ public final class GuildRosterPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body.append("<p> </p>");
|
body.append("<p> </p>");
|
||||||
|
if (gc != null) {
|
||||||
|
body.append("<button id=\"ranksBtn\" class=\"secondary-button\">Edit ranks</button>");
|
||||||
|
}
|
||||||
body.append("<button id=\"backBtn\">Back to Social</button>");
|
body.append("<button id=\"backBtn\">Back to Social</button>");
|
||||||
|
String html = PageLayout.wrap(title, body.toString());
|
||||||
String html = """
|
|
||||||
<div class="page-overlay">
|
|
||||||
<div class="container" data-hyui-title="%s">
|
|
||||||
<div class="container-contents">
|
|
||||||
%s
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
""".formatted(escape(title), body.toString());
|
|
||||||
|
|
||||||
this.page = PageBuilder.pageForPlayer(playerRef)
|
this.page = PageBuilder.pageForPlayer(playerRef)
|
||||||
.withLifetime(CustomPageLifetime.CanDismiss)
|
.withLifetime(CustomPageLifetime.CanDismiss)
|
||||||
@@ -120,6 +114,11 @@ public final class GuildRosterPage {
|
|||||||
|
|
||||||
page.addEventListener("backBtn", CustomUIEventBindingType.Activating, (ignored, pCtx) ->
|
page.addEventListener("backBtn", CustomUIEventBindingType.Activating, (ignored, pCtx) ->
|
||||||
openInStore(s -> new SocialHubPage(playerRef, ctx).open(s)));
|
openInStore(s -> new SocialHubPage(playerRef, ctx).open(s)));
|
||||||
|
|
||||||
|
if (gc != null) {
|
||||||
|
page.addEventListener("ranksBtn", CustomUIEventBindingType.Activating, (ignored, pCtx) ->
|
||||||
|
openInStore(s -> new GuildRankEditorPage(playerRef, ctx).open(s)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void open(@Nonnull Store<EntityStore> store) {
|
public void open(@Nonnull Store<EntityStore> store) {
|
||||||
|
|||||||
@@ -55,16 +55,7 @@ public final class PrivacySettingsPage {
|
|||||||
|
|
||||||
body.append("<p> </p>");
|
body.append("<p> </p>");
|
||||||
body.append("<button id=\"backBtn\">Back to Social</button>");
|
body.append("<button id=\"backBtn\">Back to Social</button>");
|
||||||
|
String html = PageLayout.wrap("Privacy", body.toString());
|
||||||
String html = """
|
|
||||||
<div class="page-overlay">
|
|
||||||
<div class="container" data-hyui-title="Privacy">
|
|
||||||
<div class="container-contents">
|
|
||||||
%s
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
""".formatted(body.toString());
|
|
||||||
|
|
||||||
this.page = PageBuilder.pageForPlayer(playerRef)
|
this.page = PageBuilder.pageForPlayer(playerRef)
|
||||||
.withLifetime(CustomPageLifetime.CanDismiss)
|
.withLifetime(CustomPageLifetime.CanDismiss)
|
||||||
|
|||||||
@@ -27,24 +27,21 @@ public final class SocialHubPage {
|
|||||||
int incoming = countIncomingRequests();
|
int incoming = countIncomingRequests();
|
||||||
String guildLine = guildSummary();
|
String guildLine = guildSummary();
|
||||||
|
|
||||||
String html = """
|
String body = """
|
||||||
<div class="page-overlay">
|
<p>Signed in as %s.</p>
|
||||||
<div class="container" data-hyui-title="Social">
|
<p> </p>
|
||||||
<div class="container-contents">
|
<p><b>Friends</b></p>
|
||||||
<p>Signed in as %s.</p>
|
<button id="friendsBtn">Friends List - %d friend(s), %d pending request(s)</button>
|
||||||
<p> </p>
|
<p> </p>
|
||||||
<p><b>Friends</b></p>
|
<p><b>Guild</b></p>
|
||||||
<button id="friendsBtn">Friends List - %d friend(s), %d pending request(s)</button>
|
<button id="guildBtn">%s</button>
|
||||||
<p><b>Guild</b></p>
|
<p> </p>
|
||||||
<button id="guildBtn">%s</button>
|
<p><b>Privacy</b></p>
|
||||||
<p><b>Privacy</b></p>
|
<button id="privacyBtn">Settings - who can DM, friend-request, or invite you</button>
|
||||||
<button id="privacyBtn">Settings - who can DM, friend-request, or invite you</button>
|
<p> </p>
|
||||||
<p> </p>
|
<button id="closeBtn" class="secondary-button">Close</button>
|
||||||
<button id="closeBtn">Close</button>
|
""".formatted(PageLayout.escape(playerRef.getUsername()), friendCount, incoming, PageLayout.escape(guildLine));
|
||||||
</div>
|
String html = PageLayout.wrap("Social", body);
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
""".formatted(escape(playerRef.getUsername()), friendCount, incoming, escape(guildLine));
|
|
||||||
|
|
||||||
this.page = PageBuilder.pageForPlayer(playerRef)
|
this.page = PageBuilder.pageForPlayer(playerRef)
|
||||||
.withLifetime(CustomPageLifetime.CanDismiss)
|
.withLifetime(CustomPageLifetime.CanDismiss)
|
||||||
|
|||||||
Reference in New Issue
Block a user