diff options
| -rw-r--r-- | Hotline/Application-macOS.swift | 21 | ||||
| -rw-r--r-- | Hotline/Hotline.entitlements | 2 | ||||
| -rw-r--r-- | Hotline/Hotline/HotlineClient.swift | 2 | ||||
| -rw-r--r-- | Hotline/Info.plist | 2 | ||||
| -rw-r--r-- | Hotline/Models/Bookmark.swift | 9 | ||||
| -rw-r--r-- | Hotline/Models/Server.swift | 4 | ||||
| -rw-r--r-- | Hotline/Shared/NetSocket.swift | 6 | ||||
| -rw-r--r-- | Hotline/macOS/MessageBoardEditorView.swift | 2 | ||||
| -rw-r--r-- | Hotline/macOS/ServerView.swift | 40 | ||||
| -rw-r--r-- | Hotline/macOS/TrackerView.swift | 17 |
10 files changed, 70 insertions, 35 deletions
diff --git a/Hotline/Application-macOS.swift b/Hotline/Application-macOS.swift index fe3a828..36f9aa8 100644 --- a/Hotline/Application-macOS.swift +++ b/Hotline/Application-macOS.swift @@ -42,25 +42,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { } } } - -// if FileManager.default.ubiquityIdentityToken == nil { -// print("iCloud Unavailable") -// -// // We mark CloudKit has available now since we're not waiting on -// // a server sync or anything. -// ApplicationState.shared.cloudKitReady = true -// } -// else { -// print("iCloud Available") -// -// self.cloudKitObserverToken = NotificationCenter.default.addObserver(forName: NSPersistentCloudKitContainer.eventChangedNotification, object: nil, queue: OperationQueue.main) { [weak self] note in -// print("iCloud Changed!") -// ApplicationState.shared.cloudKitReady = true -// -// guard let token = self?.cloudKitObserverToken else { return } -// NotificationCenter.default.removeObserver(token) -// } -// } } func applicationWillTerminate(_ notification: Notification) { @@ -86,7 +67,7 @@ struct Application: App { let schema = Schema([ Bookmark.self ]) - let config = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false, cloudKitDatabase: .private("iCloud.co.goodmake.hotline")) + let config = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false, cloudKitDatabase: .private("iCloud.pizza.unlimited.hotline")) let modelContainer = try! ModelContainer(for: schema, configurations: [config]) // Print local SwiftData sqlite file. diff --git a/Hotline/Hotline.entitlements b/Hotline/Hotline.entitlements index e15d27d..5baba22 100644 --- a/Hotline/Hotline.entitlements +++ b/Hotline/Hotline.entitlements @@ -8,7 +8,7 @@ <string>development</string> <key>com.apple.developer.icloud-container-identifiers</key> <array> - <string>iCloud.co.goodmake.hotline</string> + <string>iCloud.pizza.unlimited.hotline</string> </array> <key>com.apple.developer.icloud-services</key> <array> diff --git a/Hotline/Hotline/HotlineClient.swift b/Hotline/Hotline/HotlineClient.swift index 434285f..063419d 100644 --- a/Hotline/Hotline/HotlineClient.swift +++ b/Hotline/Hotline/HotlineClient.swift @@ -33,7 +33,7 @@ private struct HotlineLogin { protocol HotlineClientDelegate: AnyObject { func hotlineGetUserInfo() -> (String, UInt16) - func hotlineStatusChanged(status: HotlineClientStatus) + @MainActor func hotlineStatusChanged(status: HotlineClientStatus) func hotlineReceivedAgreement(text: String) func hotlineReceivedErrorMessage(code: UInt32, message: String?) func hotlineReceivedChatMessage(message: String) diff --git a/Hotline/Info.plist b/Hotline/Info.plist index b43d991..a84b52a 100644 --- a/Hotline/Info.plist +++ b/Hotline/Info.plist @@ -5,6 +5,8 @@ <key>CFBundleDocumentTypes</key> <array> <dict> + <key>CFBundleTypeRole</key> + <string>Editor</string> <key>CFBundleTypeName</key> <string>Hotline Bookmark</string> <key>LSHandlerRank</key> diff --git a/Hotline/Models/Bookmark.swift b/Hotline/Models/Bookmark.swift index 733a6eb..b410f75 100644 --- a/Hotline/Models/Bookmark.swift +++ b/Hotline/Models/Bookmark.swift @@ -22,6 +22,9 @@ final class Bookmark { @Attribute(.allowsCloudEncryption) var password: String? + @Attribute + var autoconnect: Bool = false + @Attribute(.ephemeral) var expanded: Bool = false @@ -69,7 +72,7 @@ final class Bookmark { return nil case .server, .temporary: - return Server(name: self.name, description: self.serverDescription, address: self.address, port: self.port, login: self.login, password: self.password) + return Server(name: self.name, description: self.serverDescription, address: self.address, port: self.port, login: self.login, password: self.password, autoconnect: self.autoconnect) } } @@ -79,7 +82,7 @@ final class Bookmark { Bookmark(type: .tracker, name: "Featured Servers", address: "hltracker.com", port: HotlinePorts.DefaultTrackerPort) ] - init(type: BookmarkType, name: String, address: String, port: Int, login: String? = nil, password: String? = nil) { + init(type: BookmarkType, name: String, address: String, port: Int, login: String? = nil, password: String? = nil, autoconnect: Bool = false) { self.type = type self.name = name self.address = address @@ -87,6 +90,8 @@ final class Bookmark { self.login = login self.password = password + + self.autoconnect = autoconnect } init(temporaryServer server: Server) { diff --git a/Hotline/Models/Server.swift b/Hotline/Models/Server.swift index e0fe38b..3f4c9e8 100644 --- a/Hotline/Models/Server.swift +++ b/Hotline/Models/Server.swift @@ -9,8 +9,9 @@ struct Server: Codable { var port: Int var login: String var password: String + var autoconnect: Bool = false - init(name: String?, description: String?, address: String, port: Int = HotlinePorts.DefaultServerPort, users: Int = 0, login: String? = nil, password: String? = nil) { + init(name: String?, description: String?, address: String, port: Int = HotlinePorts.DefaultServerPort, users: Int = 0, login: String? = nil, password: String? = nil, autoconnect: Bool? = false) { self.name = name self.description = description self.address = address.lowercased() @@ -18,6 +19,7 @@ struct Server: Codable { self.users = users self.login = login ?? "" self.password = password ?? "" + self.autoconnect = autoconnect ?? false } init?(url: URL) { diff --git a/Hotline/Shared/NetSocket.swift b/Hotline/Shared/NetSocket.swift index a7d44ce..263487b 100644 --- a/Hotline/Shared/NetSocket.swift +++ b/Hotline/Shared/NetSocket.swift @@ -6,9 +6,9 @@ import Foundation protocol NetSocketDelegate: AnyObject { - func netsocketConnected(socket: NetSocket) - func netsocketDisconnected(socket: NetSocket, error: Error?) - func netsocketReceived(socket: NetSocket, bytes: [UInt8]) + @MainActor func netsocketConnected(socket: NetSocket) + @MainActor func netsocketDisconnected(socket: NetSocket, error: Error?) + @MainActor func netsocketReceived(socket: NetSocket, bytes: [UInt8]) func netsocketSent(socket: NetSocket, count: Int) } diff --git a/Hotline/macOS/MessageBoardEditorView.swift b/Hotline/macOS/MessageBoardEditorView.swift index 029616f..474384e 100644 --- a/Hotline/macOS/MessageBoardEditorView.swift +++ b/Hotline/macOS/MessageBoardEditorView.swift @@ -20,7 +20,7 @@ struct MessageBoardEditorView: View { let cleanedText = text.trimmingCharacters(in: .whitespacesAndNewlines) - await model.postToMessageBoard(text: cleanedText) + model.postToMessageBoard(text: cleanedText) let _ = await model.getMessageBoard() // let success = await model.postNewsArticle(title: title, body: text, at: path, parentID: parentID) diff --git a/Hotline/macOS/ServerView.swift b/Hotline/macOS/ServerView.swift index e9f4a96..1e53e49 100644 --- a/Hotline/macOS/ServerView.swift +++ b/Hotline/macOS/ServerView.swift @@ -143,6 +143,8 @@ struct ServerView: View { @Environment(\.scenePhase) private var scenePhase @Environment(\.modelContext) private var modelContext + @State private var reconnectTimer: Task<Void, Never>? + @State private var shouldReconnect: Bool = false @State private var model: Hotline = Hotline(trackerClient: HotlineTrackerClient(), client: HotlineClient()) @State private var state: ServerState = ServerState(selection: .chat) @State private var agreementShown: Bool = false @@ -151,6 +153,7 @@ struct ServerView: View { @State private var connectPassword: String = "" @State private var connectNameSheetPresented: Bool = false @State private var connectName: String = "" + @State private var autoconnect: Bool = false @Binding var server: Server @@ -227,6 +230,16 @@ struct ServerView: View { .onDisappear { model.disconnect() } + .onChange(of: model.status) { + if model.status == .loggedIn { + reconnectTimer?.cancel() + shouldReconnect = server.autoconnect + } + if model.status == .disconnected && shouldReconnect { + shouldReconnect = false + startReconnectTimer() + } + } .alert(model.errorMessage ?? "Server Error", isPresented: $model.errorDisplayed) { Button("OK") {} } @@ -248,6 +261,15 @@ struct ServerView: View { .focusedSceneValue(\.activeServerState, state) } + private func startReconnectTimer() { + reconnectTimer = Task { + while !Task.isCancelled { + connectToServer() + try? await Task.sleep(for: .seconds(5)) + } + } + } + var connectForm: some View { VStack(alignment: .center) { GroupBox { @@ -297,7 +319,7 @@ struct ServerView: View { Button("Connect") { Task { - await connectToServer() + connectToServer() } } @@ -335,6 +357,7 @@ struct ServerView: View { TextField("Bookmark Name", text: $connectName) .textFieldStyle(.roundedBorder) .controlSize(.large) + Toggle("Connect on Startup", isOn: $autoconnect) } .frame(width: 250) .padding() @@ -353,19 +376,24 @@ struct ServerView: View { connectNameSheetPresented = false connectName = "" Task.detached { - let (host, port) = Server.parseServerAddressAndPort(connectAddress) - let login: String? = connectLogin.isEmpty ? nil : connectLogin - let password: String? = connectPassword.isEmpty ? nil : connectPassword + let (host, port) = await Server.parseServerAddressAndPort(connectAddress) + let login: String? = await connectLogin.isEmpty ? nil : connectLogin + let password: String? = await connectPassword.isEmpty ? nil : connectPassword if !host.isEmpty { - let newBookmark = Bookmark(type: .server, name: name, address: host, port: port, login: login, password: password) - Bookmark.add(newBookmark, context: modelContext) + let newBookmark = await Bookmark(type: .server, name: name, address: host, port: port, login: login, password: password, autoconnect: autoconnect) + await MainActor.run { + Bookmark.add(newBookmark, context: modelContext) + } } } } } } } + .onAppear { + autoconnect = false + } } } diff --git a/Hotline/macOS/TrackerView.swift b/Hotline/macOS/TrackerView.swift index 0fac8b0..cdeef7f 100644 --- a/Hotline/macOS/TrackerView.swift +++ b/Hotline/macOS/TrackerView.swift @@ -24,6 +24,11 @@ struct TrackerView: View { List(selection: $selection) { ForEach(bookmarks, id: \.self) { bookmark in TrackerItemView(bookmark: bookmark) + .onAppear { + if bookmark.autoconnect, let server = bookmark.server { + openWindow(id: "server", value: server) + } + } .tag(bookmark) if bookmark.type == .tracker && bookmark.expanded { @@ -403,7 +408,19 @@ struct TrackerItemView: View { .frame(width: 11, height: 11, alignment: .center) .opacity(0.5) .padding(.leading, 3) + Image(systemName: "bolt.fill") + .resizable() + .renderingMode(.template) + .aspectRatio(contentMode: .fit) + .frame(width: 13, height: 13, alignment: .center) + .opacity(0.5) + .padding(.leading, 3) .padding(.trailing, 2) + .foregroundStyle(bookmark.autoconnect ? Color.accentColor : Color.gray) + .onTapGesture { + bookmark.autoconnect.toggle() + try? bookmark.modelContext?.save() + } Image("Server") .resizable() .scaledToFit() |