aboutsummaryrefslogtreecommitdiff
path: root/Hotline/macOS/Settings
diff options
context:
space:
mode:
authorDustin Mierau <dustin@mierau.me>2025-10-27 21:48:20 -0700
committerDustin Mierau <dustin@mierau.me>2025-10-27 21:48:20 -0700
commit4bb0ffba596e41a8309ba50d222248a0ba3eb42e (patch)
treefcd1c2b11c071a624d0b3b56ed16340272200c7d /Hotline/macOS/Settings
parent924012e6ee03fff5570e9362e29be9b04816b777 (diff)
Random project housekeeping/organizing source files.
Diffstat (limited to 'Hotline/macOS/Settings')
-rw-r--r--Hotline/macOS/Settings/GeneralSettingsView.swift67
-rw-r--r--Hotline/macOS/Settings/IconSettingsView.swift58
-rw-r--r--Hotline/macOS/Settings/SettingsView.swift31
-rw-r--r--Hotline/macOS/Settings/SoundSettingsView.swift40
4 files changed, 196 insertions, 0 deletions
diff --git a/Hotline/macOS/Settings/GeneralSettingsView.swift b/Hotline/macOS/Settings/GeneralSettingsView.swift
new file mode 100644
index 0000000..6be73aa
--- /dev/null
+++ b/Hotline/macOS/Settings/GeneralSettingsView.swift
@@ -0,0 +1,67 @@
+import SwiftUI
+
+struct GeneralSettingsView: View {
+ @State private var username: String = ""
+ @State private var usernameChanged: Bool = false
+ @State private var showClearHistoryConfirmation: Bool = false
+
+ let saveTimer = Timer.publish(every: 5, on: .main, in: .common).autoconnect()
+
+ var body: some View {
+ @Bindable var preferences = Prefs.shared
+
+ Form {
+ TextField("Your Name", text: $username, prompt: Text("guest"))
+ Toggle("Show Join/Leave in Chat", isOn: $preferences.showJoinLeaveMessages)
+ Toggle("Refuse private messages", isOn: $preferences.refusePrivateMessages)
+ Toggle("Refuse private chat", isOn: $preferences.refusePrivateChat)
+ Toggle("Automatic Response", isOn: $preferences.enableAutomaticMessage)
+ if preferences.enableAutomaticMessage {
+ TextField("", text: $preferences.automaticMessage, prompt: Text("Write a response message"))
+ .lineLimit(2)
+ .multilineTextAlignment(.leading)
+ .frame(maxWidth: .infinity)
+ .onSubmit(of: .text) {
+ preferences.username = self.username
+ }
+ }
+
+ Divider()
+
+ Button(role: .destructive) {
+ showClearHistoryConfirmation = true
+ } label: {
+ Text("Clear Chat History…")
+ }
+ }
+ .padding()
+ .frame(width: 392)
+ .confirmationDialog("Clear chat history?", isPresented: $showClearHistoryConfirmation, titleVisibility: .visible) {
+ Button("Clear Chat History", role: .destructive) {
+ Task {
+ await ChatStore.shared.clearAll()
+ }
+ }
+ Button("Cancel", role: .cancel) {}
+ } message: {
+ Text("This removes all saved chat logs across servers. Active chats will repopulate only with new messages.")
+ }
+ .onAppear {
+ self.username = preferences.username
+ self.usernameChanged = false
+ }
+ .onDisappear {
+ preferences.username = self.username
+ self.usernameChanged = false
+ }
+ .onChange(of: username) { oldValue, newValue in
+ self.usernameChanged = true
+ }
+ .onReceive(saveTimer) { _ in
+ if self.usernameChanged {
+ self.usernameChanged = false
+ preferences.username = self.username
+ }
+ }
+ }
+}
diff --git a/Hotline/macOS/Settings/IconSettingsView.swift b/Hotline/macOS/Settings/IconSettingsView.swift
new file mode 100644
index 0000000..98cbb09
--- /dev/null
+++ b/Hotline/macOS/Settings/IconSettingsView.swift
@@ -0,0 +1,58 @@
+import SwiftUI
+
+struct IconSettingsView: View {
+ @State private var hoveredUserIconID: Int = -1
+
+ var body: some View {
+ @Bindable var preferences = Prefs.shared
+
+ Form {
+ ScrollViewReader { scrollProxy in
+ ScrollView {
+ LazyVGrid(columns: [
+ GridItem(.fixed(4+32+4)),
+ GridItem(.fixed(4+32+4)),
+ GridItem(.fixed(4+32+4)),
+ GridItem(.fixed(4+32+4)),
+ GridItem(.fixed(4+32+4)),
+ GridItem(.fixed(4+32+4)),
+ GridItem(.fixed(4+32+4))
+ ], spacing: 0) {
+ ForEach(Hotline.classicIconSet, id: \.self) { iconID in
+ HStack {
+ Image("Classic/\(iconID)")
+ .resizable()
+ .interpolation(.none)
+ .scaledToFit()
+ .frame(width: 32, height: 16)
+ .help("Icon \(String(iconID))")
+ }
+ .tag(iconID)
+ .frame(width: 32, height: 32)
+ .padding(4)
+ .background(iconID == preferences.userIconID ? Color.accentColor : (iconID == hoveredUserIconID ? Color.accentColor.opacity(0.1) : Color(nsColor: .textBackgroundColor)))
+ .clipShape(RoundedRectangle(cornerRadius: 5))
+ .onTapGesture {
+ preferences.userIconID = iconID
+ }
+ .onHover { hovered in
+ if hovered {
+ self.hoveredUserIconID = iconID
+ }
+ }
+ }
+ }
+ .padding()
+ }
+ .background(Color(nsColor: .textBackgroundColor))
+ .clipShape(RoundedRectangle(cornerRadius: 6))
+ .overlay(RoundedRectangle(cornerRadius: 6).stroke(Color(nsColor: .separatorColor), lineWidth: 1))
+ .onAppear {
+ scrollProxy.scrollTo(preferences.userIconID, anchor: .center)
+ }
+ .frame(height: 355)
+ }
+ }
+ .padding()
+ }
+}
diff --git a/Hotline/macOS/Settings/SettingsView.swift b/Hotline/macOS/Settings/SettingsView.swift
new file mode 100644
index 0000000..b2a2948
--- /dev/null
+++ b/Hotline/macOS/Settings/SettingsView.swift
@@ -0,0 +1,31 @@
+import SwiftUI
+
+struct SettingsView: View {
+ private enum Tabs: Hashable {
+ case general, icon
+ }
+
+ var body: some View {
+ TabView {
+ GeneralSettingsView()
+ .tabItem {
+ Label("General", systemImage: "person.text.rectangle")
+ }
+ .tag(Tabs.general)
+ IconSettingsView()
+ .tabItem {
+ Label("Icon", systemImage: "person")
+ }
+ .tag(Tabs.icon)
+ SoundSettingsView()
+ .tabItem {
+ Label("Sound", systemImage: "speaker.wave.3")
+ }
+ .tag(Tabs.icon)
+ }
+ }
+}
+
+#Preview {
+ SettingsView()
+}
diff --git a/Hotline/macOS/Settings/SoundSettingsView.swift b/Hotline/macOS/Settings/SoundSettingsView.swift
new file mode 100644
index 0000000..b99db77
--- /dev/null
+++ b/Hotline/macOS/Settings/SoundSettingsView.swift
@@ -0,0 +1,40 @@
+import SwiftUI
+
+struct SoundSettingsView: View {
+ var body: some View {
+ @Bindable var preferences = Prefs.shared
+
+ Form {
+ Toggle("Enable Sounds", isOn: $preferences.playSounds)
+ .controlSize(.large)
+
+ Section("Sounds") {
+ Toggle("Chat", isOn: $preferences.playChatSound)
+ .disabled(!preferences.playSounds)
+
+ Toggle("File Transfers", isOn: $preferences.playFileTransferCompleteSound)
+ .disabled(!preferences.playSounds)
+
+ Toggle("Private Message", isOn: $preferences.playPrivateMessageSound)
+ .disabled(!preferences.playSounds)
+
+ Toggle("Join", isOn: $preferences.playJoinSound)
+ .disabled(!preferences.playSounds)
+
+ Toggle("Leave", isOn: $preferences.playLeaveSound)
+ .disabled(!preferences.playSounds)
+
+ Toggle("Logged in", isOn: $preferences.playLoggedInSound)
+ .disabled(!preferences.playSounds)
+
+ Toggle("Error", isOn: $preferences.playErrorSound)
+ .disabled(!preferences.playSounds)
+
+ Toggle("Chat Invitation", isOn: $preferences.playChatInvitationSound)
+ .disabled(!preferences.playSounds)
+ }
+ }
+ .formStyle(.grouped)
+ .frame(width: 392, height: 433)
+ }
+}