fix(limbo): 登录门聊天和 /link 回复里的网址可以直接点击打开
This commit is contained in:
8 files changed
+182
-22
No files matched your search
+2
-15
@@ -397,21 +397,8 @@ public final class FelisVelocityPlugin {
|
||||
async(player, () -> {
|
||||
try {
|
||||
LinkCode code = linkClient.requestCode(player.getUniqueId());
|
||||
player.sendMessage(Component.text(
|
||||
zh ? "你的绑定码:" : "Your link code: ", NamedTextColor.GREEN)
|
||||
.append(Component.text(code.code(), NamedTextColor.YELLOW)));
|
||||
String panelUrl = code.panelUrl();
|
||||
if (panelUrl != null) {
|
||||
player.sendMessage(Component.text(
|
||||
zh ? "在这里输入它完成绑定(几分钟内有效):"
|
||||
: "Enter it here to finish linking (valid a few minutes):",
|
||||
NamedTextColor.GRAY));
|
||||
player.sendMessage(Component.text(" " + panelUrl, NamedTextColor.WHITE));
|
||||
} else {
|
||||
player.sendMessage(Component.text(
|
||||
zh ? "在网页控制台 → 账户 中输入它完成绑定(几分钟内有效)。"
|
||||
: "Enter it on the web console → Account to finish linking (valid a few minutes).",
|
||||
NamedTextColor.GRAY));
|
||||
for (Component line : LinkCodeCard.lines(code.code(), code.panelUrl(), zh)) {
|
||||
player.sendMessage(line);
|
||||
}
|
||||
} catch (LinkException e) {
|
||||
logger.warn("link code request failed for {} (status={}, code={}): {}",
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package best.lolicon.felis.velocity;
|
||||
|
||||
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.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* LinkCodeCard builds the reply to {@code /link}: the bind code, then where to enter
|
||||
* it. The panel address is a click-to-open link, because the client makes nothing in
|
||||
* server chat clickable by itself and a bare address had to be typed out by hand.
|
||||
*
|
||||
* <p>Like {@link InviteCard} it holds no Velocity types, so {@link LinkCodeCardTest}
|
||||
* can check the link without a live proxy. Sending is left to the caller.
|
||||
*/
|
||||
final class LinkCodeCard {
|
||||
|
||||
private LinkCodeCard() {
|
||||
}
|
||||
|
||||
/** lines renders the reply; panelUrl is null when felis-api minted none. */
|
||||
static List<Component> lines(String code, String panelUrl, boolean zh) {
|
||||
Component codeLine = Component.text(zh ? "你的绑定码:" : "Your link code: ", NamedTextColor.GREEN)
|
||||
.append(Component.text(code, NamedTextColor.YELLOW));
|
||||
if (panelUrl == null) {
|
||||
return List.of(codeLine, Component.text(
|
||||
zh ? "在网页控制台 → 账户 中输入它完成绑定(几分钟内有效)。"
|
||||
: "Enter it on the web console → Account to finish linking (valid a few minutes).",
|
||||
NamedTextColor.GRAY));
|
||||
}
|
||||
Component link = Component.text(panelUrl, NamedTextColor.AQUA, TextDecoration.UNDERLINED)
|
||||
.clickEvent(ClickEvent.openUrl(panelUrl))
|
||||
.hoverEvent(HoverEvent.showText(Component.text(
|
||||
zh ? "点击在浏览器中打开" : "Click to open it in your browser")));
|
||||
return List.of(codeLine,
|
||||
Component.text(zh ? "在这里输入它完成绑定(几分钟内有效):"
|
||||
: "Enter it here to finish linking (valid a few minutes):",
|
||||
NamedTextColor.GRAY),
|
||||
Component.text(" ").append(link));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package best.lolicon.felis.velocity;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* LinkCodeCardTest checks the {@code /link} reply: the code, and a panel address the
|
||||
* player can click open instead of typing it out.
|
||||
*
|
||||
* <p>Hermetic and framework-free like {@link InviteCardTest}, and run the same way:
|
||||
* {@code javac -cp <adventure-api.jar>:<adventure-key.jar>:<examination-api.jar> -d <out>
|
||||
* velocity/src/main/java/best/lolicon/felis/velocity/LinkCodeCard.java
|
||||
* velocity/test/best/lolicon/felis/velocity/LinkCodeCardTest.java && java -cp
|
||||
* <out>:<adventure-api.jar>:<adventure-key.jar>:<examination-api.jar>
|
||||
* best.lolicon.felis.velocity.LinkCodeCardTest}.
|
||||
*/
|
||||
public final class LinkCodeCardTest {
|
||||
|
||||
private static final String URL = "https://console.example.test/link";
|
||||
|
||||
private static int checks;
|
||||
|
||||
public static void main(String[] args) {
|
||||
for (boolean zh : new boolean[] {true, false}) {
|
||||
List<Component> card = LinkCodeCard.lines("K7P2QX", URL, zh);
|
||||
String lang = zh ? "zh: " : "en: ";
|
||||
assertEq(lang + "three lines", 3, card.size());
|
||||
assertEq(lang + "the code line", zh ? "你的绑定码:K7P2QX" : "Your link code: K7P2QX", flat(card.get(0)));
|
||||
assertEq(lang + "the address line", " " + URL, flat(card.get(2)));
|
||||
List<Component> links = card.get(2).children();
|
||||
assertEq(lang + "one link", 1, links.size());
|
||||
Component link = links.get(0);
|
||||
// Checked by equals alone: printing a ClickEvent needs examination-string,
|
||||
// which this classpath leaves out, so a mismatch would die in toString.
|
||||
assertTrue(lang + "the link opens the panel", ClickEvent.openUrl(URL).equals(link.clickEvent()));
|
||||
assertEq(lang + "the link looks like one", TextDecoration.State.TRUE, link.decoration(TextDecoration.UNDERLINED));
|
||||
assertTrue(lang + "the link says what a click does", link.hoverEvent() != null);
|
||||
}
|
||||
List<Component> bare = LinkCodeCard.lines("K7P2QX", null, true);
|
||||
assertEq("no panel URL: two lines", 2, bare.size());
|
||||
assertTrue("no panel URL: points at the console", flat(bare.get(1)).contains("网页控制台"));
|
||||
assertTrue("no panel URL: nothing to click", bare.get(1).clickEvent() == null);
|
||||
System.out.println("LinkCodeCardTest OK (" + checks + " checks)");
|
||||
}
|
||||
|
||||
private static String flat(Component c) {
|
||||
StringBuilder sb = new StringBuilder(c instanceof TextComponent ? ((TextComponent) c).content() : "");
|
||||
for (Component child : c.children()) {
|
||||
sb.append(flat(child));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void assertTrue(String what, boolean ok) {
|
||||
if (!ok) {
|
||||
throw new AssertionError(what);
|
||||
}
|
||||
checks++;
|
||||
}
|
||||
|
||||
private static void assertEq(String what, Object want, Object got) {
|
||||
if (want == null ? got != null : !want.equals(got)) {
|
||||
throw new AssertionError(what + " = " + got + ", want " + want);
|
||||
}
|
||||
checks++;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user