From d7261c906d6abb4fa2a1733a6b9f91ded4159057 Mon Sep 17 00:00:00 2001 From: ericek111 Date: Fri, 25 Sep 2026 10:53:25 +0000 Subject: [PATCH] Let chat logs be turned off or kept elsewhere Chat logs always went into the TeamSpeak 3 client's own folder. A new Chat page in Options can turn them off, or point them at another folder; left empty, they are still shared with the TeamSpeak 3 client. Co-Authored-By: Claude Opus 5.5 --- .../java/com/ts3client/config/Settings.java | 9 +++ .../ts3client/net/TeamspeakConnection.java | 9 ++- .../java/com/ts3client/ui/ChatLogsPanel.java | 80 +++++++++++++++++++ .../java/com/ts3client/ui/SettingsDialog.java | 4 + 4 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 ts3-client/swing/src/main/java/com/ts3client/ui/ChatLogsPanel.java diff --git a/ts3-client/core/src/main/java/com/ts3client/config/Settings.java b/ts3-client/core/src/main/java/com/ts3client/config/Settings.java index 5e5be15..65ed654 100644 --- a/ts3-client/core/src/main/java/com/ts3client/config/Settings.java +++ b/ts3-client/core/src/main/java/com/ts3client/config/Settings.java @@ -183,6 +183,11 @@ public final class Settings { /** Whose whispers are played. */ public WhisperPolicy whisperPolicy = WhisperPolicy.ALLOW_UNLESS_DENIED; + /** Keep chat logs, and show their history when a chat opens. */ + public boolean chatLogs = true; + /** Where chat logs go; empty for the TeamSpeak 3 client's own folder, so both share them. */ + public String chatLogDir = ""; + public static Settings load() { Settings s = new Settings(); try { @@ -271,6 +276,8 @@ public final class Settings { contactDefaults.load(props); colorContacts = parseB(props.getProperty("colorContacts"), colorContacts); whisperPolicy = parseEnum(WhisperPolicy.class, props.getProperty("whisperPolicy"), whisperPolicy); + chatLogs = parseB(props.getProperty("chatLogs"), chatLogs); + chatLogDir = props.getProperty("chatLogDir", chatLogDir); } private void writeToProps() { @@ -321,6 +328,8 @@ public final class Settings { contactDefaults.store(props); props.setProperty("colorContacts", Boolean.toString(colorContacts)); props.setProperty("whisperPolicy", whisperPolicy.name()); + props.setProperty("chatLogs", Boolean.toString(chatLogs)); + props.setProperty("chatLogDir", chatLogDir); } private static > E parseEnum(Class type, String v, E def) { diff --git a/ts3-client/core/src/main/java/com/ts3client/net/TeamspeakConnection.java b/ts3-client/core/src/main/java/com/ts3client/net/TeamspeakConnection.java index 67f01f1..fece234 100644 --- a/ts3-client/core/src/main/java/com/ts3client/net/TeamspeakConnection.java +++ b/ts3-client/core/src/main/java/com/ts3client/net/TeamspeakConnection.java @@ -34,14 +34,15 @@ import com.ts3client.text.TsLink; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; -import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.Executors; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -1389,14 +1390,16 @@ public final class TeamspeakConnection implements TS3Listener { } private void loadChatLogs() { + chatLogs = null; + if (!settings.chatLogs) return; try { String uniqueId = serverUniqueId(); if (uniqueId == null) { - chatLogs = null; ui.onStatus("Chat logs unavailable: the server's unique id is not known"); return; } - ChatLogStore logs = new ChatLogStore(uniqueId); + String dir = settings.chatLogDir == null ? "" : settings.chatLogDir.trim(); + ChatLogStore logs = new ChatLogStore(dir.isEmpty() ? ChatLogStore.defaultRoot() : Path.of(dir), uniqueId); chatLogs = logs; logs.begin(); List serverHistory = logs.serverHtmlHistory(); diff --git a/ts3-client/swing/src/main/java/com/ts3client/ui/ChatLogsPanel.java b/ts3-client/swing/src/main/java/com/ts3client/ui/ChatLogsPanel.java new file mode 100644 index 0000000..7e11ce2 --- /dev/null +++ b/ts3-client/swing/src/main/java/com/ts3client/ui/ChatLogsPanel.java @@ -0,0 +1,80 @@ +package com.ts3client.ui; + +import com.formdev.flatlaf.util.UIScale; +import com.ts3client.config.AppDirs; +import com.ts3client.config.Settings; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import java.awt.BorderLayout; +import java.awt.GridBagConstraints; +import java.io.File; + +/** + * Options page for chat logs: whether they are kept, and where. By default they go into the + * TeamSpeak 3 client's own folder, so both clients show the same history. + */ +final class ChatLogsPanel extends FormPanel { + + private final Settings settings; + private final JCheckBox enabled = new JCheckBox("Keep chat logs"); + private final JTextField folder = new JTextField(18); + private final JButton browse = new JButton("Browse…"); + + ChatLogsPanel(Settings settings) { + this.settings = settings; + GridBagConstraints c = gbc(); + + enabled.setSelected(settings.chatLogs); + enabled.setToolTipText("Write every chat to disk and show its history when it opens again"); + enabled.addActionListener(e -> updateEnabled()); + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 2; + add(enabled, c); + + folder.setText(settings.chatLogDir); + folder.putClientProperty("JTextField.placeholderText", "The TeamSpeak 3 client's folder"); + folder.setToolTipText("Leave empty to share the TeamSpeak 3 client's logs:
" + + AppDirs.chatLogs() + ""); + limitWidth(folder, FIELD_WIDTH); + browse.addActionListener(e -> browseForFolder()); + JPanel row = new JPanel(new BorderLayout(UIScale.scale(6), 0)); + row.add(folder, BorderLayout.CENTER); + row.add(browse, BorderLayout.EAST); + addRow(this, c, 1, new JLabel("Folder:"), row); + + c.gridy = 2; + c.gridx = 1; + c.weighty = 1; + c.anchor = GridBagConstraints.NORTHWEST; + add(new JLabel("Takes effect on the next connect."), c); + updateEnabled(); + } + + private void updateEnabled() { + folder.setEnabled(enabled.isSelected()); + browse.setEnabled(enabled.isSelected()); + } + + private void browseForFolder() { + JFileChooser chooser = new JFileChooser(); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + chooser.setDialogTitle("Folder for chat logs"); + String current = folder.getText().trim(); + chooser.setCurrentDirectory(current.isEmpty() ? AppDirs.chatLogs().toFile() : new File(current)); + if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + folder.setText(chooser.getSelectedFile().getAbsolutePath()); + } + } + + /** Copies the page into the settings; the caller saves them. */ + void apply() { + settings.chatLogs = enabled.isSelected(); + settings.chatLogDir = folder.getText().trim(); + } +} diff --git a/ts3-client/swing/src/main/java/com/ts3client/ui/SettingsDialog.java b/ts3-client/swing/src/main/java/com/ts3client/ui/SettingsDialog.java index b54203f..00b5bdb 100644 --- a/ts3-client/swing/src/main/java/com/ts3client/ui/SettingsDialog.java +++ b/ts3-client/swing/src/main/java/com/ts3client/ui/SettingsDialog.java @@ -39,6 +39,7 @@ public final class SettingsDialog extends JDialog { private final HotkeysPanel hotkeysPanel; private final ClientVersionPanel clientVersionPanel; private final ContactsPanel contactsPanel; + private final ChatLogsPanel chatLogsPanel; public SettingsDialog(Frame owner, Settings settings, AudioBackend audio, VoiceInput liveMic, VoiceOutput livePlayback, @@ -54,6 +55,7 @@ public final class SettingsDialog extends JDialog { hotkeysPanel = new HotkeysPanel(hotkeys); clientVersionPanel = new ClientVersionPanel(settings); contactsPanel = new ContactsPanel(settings); + chatLogsPanel = new ChatLogsPanel(settings); devicesPanel = new DevicesPanel(settings, audio.io(), livePlayback, this::applyLive, this::restartTest, this::setTestOutputDevice); voiceActivationPanel = new VoiceActivationPanel(settings, audio, liveMic, hotkeys, @@ -66,6 +68,7 @@ public final class SettingsDialog extends JDialog { tabs.addTab("Design", designPanel); tabs.addTab("Hotkeys", hotkeysPanel); tabs.addTab("Contacts", contactsPanel); + tabs.addTab("Chat", scrollable(chatLogsPanel)); tabs.addTab("Client Version", scrollable(clientVersionPanel)); JPanel buttons = new JPanel(new BorderLayout()); @@ -143,6 +146,7 @@ public final class SettingsDialog extends JDialog { designPanel.apply(); clientVersionPanel.apply(); contactsPanel.apply(); + chatLogsPanel.apply(); hotkeysPanel.apply(); settings.save();