Let admins delete other clients' avatars

With b_client_avatar_delete_other, the client's context menu and the
menu on their avatar offer "Delete Avatar", as in TeamSpeak 3. The
server takes the deletion as ftdeletefile on /avatar_<unique id> (not
the stored file's name, which it refuses with "convert error") and
clears the client's avatar flag for everyone. Our own avatar's file is
now deleted the same way, which only such clients are allowed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-01 16:27:41 +00:00
parent b1890cbf79
commit bda114d2c0
9 changed files with 80 additions and 1 deletions

View File

@@ -1,5 +1,6 @@
package com.ts3client.ui;
import com.ts3client.net.ClientEntry;
import com.ts3client.net.TeamspeakConnection;
import com.ts3client.net.avatar.Avatar;
@@ -60,6 +61,12 @@ final class AvatarActions {
conn.removeAvatar(error -> fail("Error deleting avatar: ", error));
}
/** Deletes another client's avatar for everyone. */
void deleteOf(ClientEntry client) {
if (!conn.isConnected()) return;
conn.removeAvatarOf(client, error -> fail("Error deleting avatar: ", error));
}
/** Shows {@code error}, if there is one; callable from any thread. */
private void fail(String what, String error) {
if (error == null) return;

View File

@@ -74,6 +74,11 @@ final class ClientMenu {
JMenuItem description = new JMenuItem("Change Description", Icons.of("EDIT"));
description.addActionListener(a -> actions.changeClientDescription(client));
menu.add(description);
if (!self && !client.avatarFlag.isEmpty() && actions.canDeleteOtherAvatars()) {
JMenuItem avatar = new JMenuItem("Delete Avatar", Icons.of("DELETE_AVATAR_OTHER"));
avatar.addActionListener(a -> actions.deleteClientAvatar(client));
menu.add(avatar);
}
JMenuItem info = new JMenuItem("Connection Info", Icons.of("INFO"));
info.addActionListener(a -> actions.showConnectionInfo(client));
menu.add(info);

View File

@@ -71,6 +71,11 @@ public final class InfoPanel extends JPanel {
/** Whether we may upload an avatar. */
boolean canSet();
/** Whether we may delete other clients' avatars. */
boolean canDeleteOthers();
void deleteOf(ClientEntry client);
/** Asks for an image to upload as our avatar. */
void set();
@@ -314,6 +319,10 @@ public final class InfoPanel extends JPanel {
menu.add(item("Save…", null, () -> saveAvatar(avatar, name)));
if (avatarHandler != null && !shownClient.self) {
if (avatarHandler.canSet()) menu.add(item("Steal for avatar", null, () -> avatarHandler.steal(avatar)));
ClientEntry client = shownClient;
if (avatarHandler.canDeleteOthers()) {
menu.add(item("Delete avatar", "DELETE_AVATAR_OTHER", () -> avatarHandler.deleteOf(client)));
}
} else if (avatarHandler != null) {
if (avatarHandler.canSet()) menu.add(item("Set avatar…", "UPLOAD_AVATAR", avatarHandler::set));
menu.add(item("Delete avatar", "DELETE_AVATAR", avatarHandler::delete));

View File

@@ -6,6 +6,7 @@ import com.ts3client.config.IdentityStore;
import com.ts3client.config.Settings;
import com.ts3client.contacts.ContactStore;
import com.ts3client.net.ChannelNode;
import com.ts3client.net.ClientEntry;
import com.ts3client.net.ConnectionListener.ChatScope;
import com.ts3client.net.PictureDownloads;
import com.ts3client.net.TeamspeakConnection;
@@ -115,6 +116,16 @@ final class ServerTab {
return session.canSetAvatar();
}
@Override
public boolean canDeleteOthers() {
return session.canDeleteOtherAvatars();
}
@Override
public void deleteOf(ClientEntry client) {
avatar.deleteOf(client);
}
@Override
public void set() {
avatar.choose();

View File

@@ -215,6 +215,16 @@ final class ServerTabTreeActions implements ServerTreePanel.Actions {
* Reads the description from the server before prompting, since the model only
* carries one for clients whose info has been looked at.
*/
@Override
public boolean canDeleteOtherAvatars() {
return tab.session().canDeleteOtherAvatars();
}
@Override
public void deleteClientAvatar(ClientEntry client) {
tab.avatar().deleteOf(client);
}
@Override
public void changeClientDescription(ClientEntry client) {
if (!conn.isConnected()) return;

View File

@@ -118,6 +118,11 @@ public final class ServerTreePanel extends JPanel {
/** Edits a client's description — our own included. */
void changeClientDescription(ClientEntry client);
/** Whether we may delete other clients' avatars. */
boolean canDeleteOtherAvatars();
void deleteClientAvatar(ClientEntry client);
/** A new nickname for ourselves, typed into the tree row. */
void renameSelf(String nickname);