Show an avatar, description or away message being cleared
Client and channel updates were only applied when the field had a value, since ts3j reads a missing field back as an empty string. A removed avatar, an emptied description or a cleared away message arrives as the field without a value, so they stayed as they were. Such fields are now applied whenever the update carries them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -314,12 +314,12 @@ final class ConnectionEventHandler implements TS3Listener {
|
|||||||
// Both fields travel together, so an absent message here means "no message"
|
// Both fields travel together, so an absent message here means "no message"
|
||||||
// — which `has` cannot tell from "not reported".
|
// — which `has` cannot tell from "not reported".
|
||||||
c.awayMessage = c.away ? TeamspeakConnection.orEmpty(e.get("client_away_message")) : "";
|
c.awayMessage = c.away ? TeamspeakConnection.orEmpty(e.get("client_away_message")) : "";
|
||||||
} else if (has(e, "client_away_message")) {
|
} else if (reported(e, "client_away_message")) {
|
||||||
c.awayMessage = e.get("client_away_message");
|
c.awayMessage = TeamspeakConnection.orEmpty(e.get("client_away_message"));
|
||||||
}
|
}
|
||||||
if (has(e, "client_talk_power")) c.talkPower = e.getInt("client_talk_power");
|
if (has(e, "client_talk_power")) c.talkPower = e.getInt("client_talk_power");
|
||||||
if (has(e, "client_description")) {
|
if (reported(e, "client_description")) {
|
||||||
c.description = e.get("client_description");
|
c.description = TeamspeakConnection.orEmpty(e.get("client_description"));
|
||||||
conn.ui.onInfoUpdated();
|
conn.ui.onInfoUpdated();
|
||||||
}
|
}
|
||||||
// The answer to clientgetvariables.
|
// The answer to clientgetvariables.
|
||||||
@@ -330,7 +330,7 @@ final class ConnectionEventHandler implements TS3Listener {
|
|||||||
}
|
}
|
||||||
if (has(e, "client_is_channel_commander"))
|
if (has(e, "client_is_channel_commander"))
|
||||||
c.channelCommander = e.getBoolean("client_is_channel_commander");
|
c.channelCommander = e.getBoolean("client_is_channel_commander");
|
||||||
if (has(e, "client_flag_avatar")) c.avatarFlag = TeamspeakConnection.orEmpty(e.get("client_flag_avatar"));
|
if (reported(e, "client_flag_avatar")) c.avatarFlag = TeamspeakConnection.orEmpty(e.get("client_flag_avatar"));
|
||||||
announceClientUpdate(e, c, renamed, oldName);
|
announceClientUpdate(e, c, renamed, oldName);
|
||||||
conn.ui.onModelChanged();
|
conn.ui.onModelChanged();
|
||||||
}
|
}
|
||||||
@@ -398,7 +398,7 @@ final class ConnectionEventHandler implements TS3Listener {
|
|||||||
ChannelNode ch = conn.getModel().getChannel(safeInt(e, "cid"));
|
ChannelNode ch = conn.getModel().getChannel(safeInt(e, "cid"));
|
||||||
if (ch == null) return;
|
if (ch == null) return;
|
||||||
// The answer to channelgetdescription; an empty description comes as a bare key.
|
// The answer to channelgetdescription; an empty description comes as a bare key.
|
||||||
if (e.getMap().containsKey("channel_description")) {
|
if (reported(e, "channel_description")) {
|
||||||
ch.description = TeamspeakConnection.orEmpty(e.get("channel_description"));
|
ch.description = TeamspeakConnection.orEmpty(e.get("channel_description"));
|
||||||
ch.descriptionLoaded = true;
|
ch.descriptionLoaded = true;
|
||||||
conn.ui.onInfoUpdated();
|
conn.ui.onInfoUpdated();
|
||||||
@@ -425,7 +425,7 @@ final class ConnectionEventHandler implements TS3Listener {
|
|||||||
private static void applyChannelProperties(ChannelNode ch, BaseEvent e) {
|
private static void applyChannelProperties(ChannelNode ch, BaseEvent e) {
|
||||||
if (has(e, "channel_name")) ch.name = e.get("channel_name");
|
if (has(e, "channel_name")) ch.name = e.get("channel_name");
|
||||||
// An emptied topic arrives as a bare key, which has() would skip.
|
// An emptied topic arrives as a bare key, which has() would skip.
|
||||||
if (e.getMap().containsKey("channel_topic")) ch.topic = TeamspeakConnection.orEmpty(e.get("channel_topic"));
|
if (reported(e, "channel_topic")) ch.topic = TeamspeakConnection.orEmpty(e.get("channel_topic"));
|
||||||
if (has(e, "channel_icon_id")) ch.iconId = TeamspeakConnection.safeLong(e, "channel_icon_id");
|
if (has(e, "channel_icon_id")) ch.iconId = TeamspeakConnection.safeLong(e, "channel_icon_id");
|
||||||
if (has(e, "channel_flag_password")) ch.hasPassword = e.getBoolean("channel_flag_password");
|
if (has(e, "channel_flag_password")) ch.hasPassword = e.getBoolean("channel_flag_password");
|
||||||
if (has(e, "channel_flag_permanent")) ch.permanent = e.getBoolean("channel_flag_permanent");
|
if (has(e, "channel_flag_permanent")) ch.permanent = e.getBoolean("channel_flag_permanent");
|
||||||
@@ -681,6 +681,11 @@ final class ConnectionEventHandler implements TS3Listener {
|
|||||||
return value != null && !value.isEmpty();
|
return value != null && !value.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Whether the event carries a field at all, even empty: for text that can be cleared. */
|
||||||
|
private static boolean reported(BaseEvent e, String key) {
|
||||||
|
return e.getMap().containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
/** Parses a comma-separated id list (e.g. server groups "6,12,15"). */
|
/** Parses a comma-separated id list (e.g. server groups "6,12,15"). */
|
||||||
private static int[] parseIntList(String csv) {
|
private static int[] parseIntList(String csv) {
|
||||||
if (csv == null || csv.isEmpty()) return new int[0];
|
if (csv == null || csv.isEmpty()) return new int[0];
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
package com.ts3client.net;
|
package com.ts3client.net;
|
||||||
|
|
||||||
|
import com.github.manevolent.ts3j.command.MultiCommand;
|
||||||
import com.github.manevolent.ts3j.event.ClientUpdatedEvent;
|
import com.github.manevolent.ts3j.event.ClientUpdatedEvent;
|
||||||
|
import com.github.manevolent.ts3j.protocol.ProtocolRole;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pins the ts3j behaviour {@code ConnectionEventHandler.has(...)} exists for: a field
|
* Pins the ts3j behaviour {@code ConnectionEventHandler.has(...)} exists for: a field
|
||||||
@@ -22,4 +26,15 @@ class EventFieldsTest {
|
|||||||
assertEquals("", e.get("client_is_recording"));
|
assertEquals("", e.get("client_is_recording"));
|
||||||
assertEquals("", e.get("client_nickname"));
|
assertEquals("", e.get("client_nickname"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** What {@code reported(...)} relies on: a cleared field still arrives, just without a value. */
|
||||||
|
@Test
|
||||||
|
void clearedFieldsAreStillInTheMap() {
|
||||||
|
Map<String, String> fields = MultiCommand.parse(ProtocolRole.SERVER,
|
||||||
|
"notifyclientupdated clid=7 client_flag_avatar client_description=").simplifyOne().toMap();
|
||||||
|
ClientUpdatedEvent e = new ClientUpdatedEvent(fields);
|
||||||
|
assertTrue(e.getMap().containsKey("client_flag_avatar"));
|
||||||
|
assertTrue(e.getMap().containsKey("client_description"));
|
||||||
|
assertFalse(e.getMap().containsKey("client_away_message"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user