Add "Change Description" to the client menu

Offered for our own client as well as others, with TS3's edit icon. The
description is read back from the server before prompting, since the
model only carries one for clients whose info has been looked at.

Note that client_description is not a clientupdate property even for
ourselves — the server only takes it through clientedit — and that our
own change is not echoed back to us, so the local entry is updated here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-19 18:04:35 +00:00
parent bcc034e975
commit cb05b097a6
5 changed files with 76 additions and 0 deletions

View File

@@ -292,6 +292,10 @@ final class ConnectionEventHandler implements TS3Listener {
c.awayMessage = e.get("client_away_message");
}
if (has(e, "client_talk_power")) c.talkPower = e.getInt("client_talk_power");
if (has(e, "client_description")) {
c.description = e.get("client_description");
conn.ui.onInfoUpdated();
}
if (has(e, "client_is_channel_commander"))
c.channelCommander = e.getBoolean("client_is_channel_commander");
announceClientUpdate(e, c, renamed, oldName);

View File

@@ -887,6 +887,45 @@ public final class TeamspeakConnection implements TS3Listener {
}, "ts3j-clientinfo").start();
}
/**
* Reads a client's description straight from the server, so an editor opens on the
* live text instead of whatever an earlier info request cached.
*/
public void requestClientDescription(int clientId, BiConsumer<String, String> callback) {
run("ts3j-client-description", callback, () -> {
Client c = client.getClientInfo(clientId);
String description = c == null ? "" : orEmpty(c.get("client_description"));
ClientEntry e = model.getClient(clientId);
if (e != null) e.description = description;
return description;
});
}
/**
* Changes a client's description. Unlike the nickname or the away state, this is not
* a {@code clientupdate} property even for ourselves: the server only takes it
* through {@code clientedit}, gated by {@code b_client_modify_own_description}.
*
* @param callback given {@code null} on success, or the failure message
*/
public void setClientDescription(int clientId, String description, Consumer<String> callback) {
boolean self = clientId == selfClientId;
run("ts3j-client-description-set", (ignored, error) -> callback.accept(error), () -> {
SingleCommand cmd = new SingleCommand("clientedit", ProtocolRole.CLIENT);
cmd.add(new CommandSingleParameter("clid", Integer.toString(clientId)));
cmd.add(new CommandSingleParameter("client_description", description));
client.executeCommand(cmd).complete();
// Only the other clients are told about our own update, so the local entry
// (and the info panel showing it) has to be caught up here.
if (self) {
ClientEntry e = model.getClient(clientId);
if (e != null) e.description = description;
ui.onInfoUpdated();
}
return null;
});
}
// ---- channel administration ----
/**