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 <noreply@anthropic.com>
This commit is contained in:
@@ -183,6 +183,11 @@ public final class Settings {
|
|||||||
/** Whose whispers are played. */
|
/** Whose whispers are played. */
|
||||||
public WhisperPolicy whisperPolicy = WhisperPolicy.ALLOW_UNLESS_DENIED;
|
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() {
|
public static Settings load() {
|
||||||
Settings s = new Settings();
|
Settings s = new Settings();
|
||||||
try {
|
try {
|
||||||
@@ -271,6 +276,8 @@ public final class Settings {
|
|||||||
contactDefaults.load(props);
|
contactDefaults.load(props);
|
||||||
colorContacts = parseB(props.getProperty("colorContacts"), colorContacts);
|
colorContacts = parseB(props.getProperty("colorContacts"), colorContacts);
|
||||||
whisperPolicy = parseEnum(WhisperPolicy.class, props.getProperty("whisperPolicy"), whisperPolicy);
|
whisperPolicy = parseEnum(WhisperPolicy.class, props.getProperty("whisperPolicy"), whisperPolicy);
|
||||||
|
chatLogs = parseB(props.getProperty("chatLogs"), chatLogs);
|
||||||
|
chatLogDir = props.getProperty("chatLogDir", chatLogDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeToProps() {
|
private void writeToProps() {
|
||||||
@@ -321,6 +328,8 @@ public final class Settings {
|
|||||||
contactDefaults.store(props);
|
contactDefaults.store(props);
|
||||||
props.setProperty("colorContacts", Boolean.toString(colorContacts));
|
props.setProperty("colorContacts", Boolean.toString(colorContacts));
|
||||||
props.setProperty("whisperPolicy", whisperPolicy.name());
|
props.setProperty("whisperPolicy", whisperPolicy.name());
|
||||||
|
props.setProperty("chatLogs", Boolean.toString(chatLogs));
|
||||||
|
props.setProperty("chatLogDir", chatLogDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <E extends Enum<E>> E parseEnum(Class<E> type, String v, E def) {
|
private static <E extends Enum<E>> E parseEnum(Class<E> type, String v, E def) {
|
||||||
|
|||||||
@@ -34,14 +34,15 @@ import com.ts3client.text.TsLink;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@@ -1389,14 +1390,16 @@ public final class TeamspeakConnection implements TS3Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void loadChatLogs() {
|
private void loadChatLogs() {
|
||||||
|
chatLogs = null;
|
||||||
|
if (!settings.chatLogs) return;
|
||||||
try {
|
try {
|
||||||
String uniqueId = serverUniqueId();
|
String uniqueId = serverUniqueId();
|
||||||
if (uniqueId == null) {
|
if (uniqueId == null) {
|
||||||
chatLogs = null;
|
|
||||||
ui.onStatus("Chat logs unavailable: the server's unique id is not known");
|
ui.onStatus("Chat logs unavailable: the server's unique id is not known");
|
||||||
return;
|
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;
|
chatLogs = logs;
|
||||||
logs.begin();
|
logs.begin();
|
||||||
List<String> serverHistory = logs.serverHtmlHistory();
|
List<String> serverHistory = logs.serverHtmlHistory();
|
||||||
|
|||||||
@@ -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("<html>Leave empty to share the TeamSpeak 3 client's logs:<br>"
|
||||||
|
+ AppDirs.chatLogs() + "</html>");
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@ public final class SettingsDialog extends JDialog {
|
|||||||
private final HotkeysPanel hotkeysPanel;
|
private final HotkeysPanel hotkeysPanel;
|
||||||
private final ClientVersionPanel clientVersionPanel;
|
private final ClientVersionPanel clientVersionPanel;
|
||||||
private final ContactsPanel contactsPanel;
|
private final ContactsPanel contactsPanel;
|
||||||
|
private final ChatLogsPanel chatLogsPanel;
|
||||||
|
|
||||||
public SettingsDialog(Frame owner, Settings settings, AudioBackend audio,
|
public SettingsDialog(Frame owner, Settings settings, AudioBackend audio,
|
||||||
VoiceInput liveMic, VoiceOutput livePlayback,
|
VoiceInput liveMic, VoiceOutput livePlayback,
|
||||||
@@ -54,6 +55,7 @@ public final class SettingsDialog extends JDialog {
|
|||||||
hotkeysPanel = new HotkeysPanel(hotkeys);
|
hotkeysPanel = new HotkeysPanel(hotkeys);
|
||||||
clientVersionPanel = new ClientVersionPanel(settings);
|
clientVersionPanel = new ClientVersionPanel(settings);
|
||||||
contactsPanel = new ContactsPanel(settings);
|
contactsPanel = new ContactsPanel(settings);
|
||||||
|
chatLogsPanel = new ChatLogsPanel(settings);
|
||||||
devicesPanel = new DevicesPanel(settings, audio.io(), livePlayback,
|
devicesPanel = new DevicesPanel(settings, audio.io(), livePlayback,
|
||||||
this::applyLive, this::restartTest, this::setTestOutputDevice);
|
this::applyLive, this::restartTest, this::setTestOutputDevice);
|
||||||
voiceActivationPanel = new VoiceActivationPanel(settings, audio, liveMic, hotkeys,
|
voiceActivationPanel = new VoiceActivationPanel(settings, audio, liveMic, hotkeys,
|
||||||
@@ -66,6 +68,7 @@ public final class SettingsDialog extends JDialog {
|
|||||||
tabs.addTab("Design", designPanel);
|
tabs.addTab("Design", designPanel);
|
||||||
tabs.addTab("Hotkeys", hotkeysPanel);
|
tabs.addTab("Hotkeys", hotkeysPanel);
|
||||||
tabs.addTab("Contacts", contactsPanel);
|
tabs.addTab("Contacts", contactsPanel);
|
||||||
|
tabs.addTab("Chat", scrollable(chatLogsPanel));
|
||||||
tabs.addTab("Client Version", scrollable(clientVersionPanel));
|
tabs.addTab("Client Version", scrollable(clientVersionPanel));
|
||||||
|
|
||||||
JPanel buttons = new JPanel(new BorderLayout());
|
JPanel buttons = new JPanel(new BorderLayout());
|
||||||
@@ -143,6 +146,7 @@ public final class SettingsDialog extends JDialog {
|
|||||||
designPanel.apply();
|
designPanel.apply();
|
||||||
clientVersionPanel.apply();
|
clientVersionPanel.apply();
|
||||||
contactsPanel.apply();
|
contactsPanel.apply();
|
||||||
|
chatLogsPanel.apply();
|
||||||
hotkeysPanel.apply();
|
hotkeysPanel.apply();
|
||||||
settings.save();
|
settings.save();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user