From 1b303803e53dbeb24dab1a7c714d71ee14f002db Mon Sep 17 00:00:00 2001 From: Dustin Mierau Date: Wed, 17 Jan 2024 23:18:27 -0800 Subject: Added server and tracker bookmarks. Reordering in Servers window. Delete. All stored securely in Keychain. --- Hotline/Models/Bookmarks.swift | 126 +++++++++++++++++++++++++++++++++++++++++ Hotline/Models/Hotline.swift | 67 ---------------------- Hotline/Models/Tracker.swift | 7 +++ 3 files changed, 133 insertions(+), 67 deletions(-) create mode 100644 Hotline/Models/Bookmarks.swift (limited to 'Hotline/Models') diff --git a/Hotline/Models/Bookmarks.swift b/Hotline/Models/Bookmarks.swift new file mode 100644 index 0000000..3f4971c --- /dev/null +++ b/Hotline/Models/Bookmarks.swift @@ -0,0 +1,126 @@ +import SwiftUI + +extension NSNotification { + static let BookmarkAdded = Notification.Name("BookmarkAdded") + static let BookmarkRemoved = Notification.Name("BookmarkRemoved") +} + +enum BookmarkType: String, Codable { + case tracker = "tracker" + case server = "server" +} + +struct Bookmark: Codable, Equatable { + let type: BookmarkType + let name: String + let address: String + let port: Int + let login: String? + let password: String? + + init(type: BookmarkType, name: String, address: String, port: Int = HotlinePorts.DefaultServerPort, login: String? = nil, password: String? = nil) { + self.type = type + self.name = name + self.address = address + self.port = port + self.login = login + self.password = password + } +} + +@Observable final class Bookmarks { + var bookmarks: [Bookmark]? = nil + + static let DefaultBookmarks: [Bookmark] = [ + Bookmark(type: .server, name: "System 7 Today", address: "hotline.system7today.com"), + Bookmark(type: .server, name: "The Mobius Strip", address: "67.174.208.111"), + Bookmark(type: .tracker, name: "Featured Servers", address: "hltracker.com"), + ] + + init() { + self.load() + } + + func apply(_ newBookmarks: [Bookmark], save shouldSave: Bool = true) { + self.bookmarks = newBookmarks + if shouldSave { + self.save() + } + } + + func load() { + let jsonString: String? = DAKeychain.shared["Hotline Bookmarks"] + let jsonData: Data? = jsonString?.data(using: .utf8, allowLossyConversion: false) + + let decoder = JSONDecoder() + var decodedBookmarks = try? decoder.decode([Bookmark].self, from: jsonData ?? Data()) + if decodedBookmarks == nil || decodedBookmarks?.isEmpty == true { + print("Bookmarks: using default bookmarks") + decodedBookmarks = Bookmarks.DefaultBookmarks + } + else { + print("Bookmarks: using saved bookmarks") + } + + self.bookmarks = [Bookmark](decodedBookmarks!) + } + + func save() { + var bookmarksToSave = self.bookmarks + if bookmarksToSave == Bookmarks.DefaultBookmarks { + print("Bookmarks: skipping saving default bookmarks") + bookmarksToSave = [] + } + + let encoder = JSONEncoder() + encoder.outputFormatting = .prettyPrinted + if let jsonData = try? encoder.encode(bookmarksToSave) { + if let jsonString = String(data: jsonData, encoding: .utf8) { + DAKeychain.shared["Hotline Bookmarks"] = jsonString + } + } + } + + // MARK: - + + func add(_ bookmark: Bookmark, save shouldSave: Bool = true) { + self.bookmarks?.insert(bookmark, at: 0) + + if shouldSave { + self.save() + } + + DispatchQueue.main.async { + NotificationCenter.default.post(name: NSNotification.BookmarkAdded, object: nil, userInfo: ["index": 0]) + } + } + + func delete(_ bookmark: Bookmark, save shouldSave: Bool = true) -> Bool { + if let i = self.bookmarks?.firstIndex(where: { b in b.address.lowercased() == bookmark.address.lowercased() && b.port == bookmark.port }) { + self.bookmarks?.remove(at: i) + + if shouldSave { + self.save() + } + + DispatchQueue.main.async { + NotificationCenter.default.post(name: NSNotification.BookmarkRemoved, object: nil, userInfo: ["index": i]) + } + return true + } + + return false + } + + func update(_ bookmark: Bookmark, save shouldSave: Bool = true) -> Bool { + if let i = self.bookmarks?.firstIndex(where: { b in b.address.lowercased() == bookmark.address.lowercased() && b.port == bookmark.port }) { + self.bookmarks?[i] = bookmark + if shouldSave { + self.save() + } + return true + } + + return false + } +} diff --git a/Hotline/Models/Hotline.swift b/Hotline/Models/Hotline.swift index 77d4d59..6b5e3a7 100644 --- a/Hotline/Models/Hotline.swift +++ b/Hotline/Models/Hotline.swift @@ -103,73 +103,6 @@ final class Hotline: HotlineClientDelegate, HotlineFileClientDelegate { 6029, 6030, 6031, 6032, 6033, 6034, 6035 ] - static let defaultIconSet: [Int: String] = [ - 414: "๐Ÿ™‚", - 2000: "๐Ÿ“Ÿ", - 2001: "๐Ÿ’€", - 2002: "๐Ÿชฉ", - 2003: "๐Ÿ’ฅ", - 2004: "๐Ÿž", - 2014: "๐ŸŽ", - 2006: "๐Ÿ’ ", - 2007: "๐Ÿฆ ", - 2008: "๐Ÿช€", - 2009: "๐Ÿ›Ÿ", - 2010: "๐Ÿ‰", - 2011: "๐Ÿ", - 2012: "๐Ÿšฆ", - 145: "๐Ÿš”", - 2015: "๐Ÿ‘ป", - 2016: "๐Ÿ’ป", - 2017: "โ˜€๏ธ", - 2018: "โžก๏ธ", - 417: "๐Ÿงโ€โ™‚๏ธ", - 140: "๐ŸŽจ", - 141: "๐Ÿ‘ฝ", - 142: "๐Ÿš€", - 143: "๐Ÿ•ท๏ธ", - 138: "๐Ÿ˜บ", - 146: "๐ŸŒ…", - 149: "๐Ÿฎ", - 150: "๐Ÿฆ–", - 151: "๐Ÿงป", - 154: "๐Ÿ–", - 182: "โœ‹", - 207: "โš ๏ธ", - 2061: "โ˜•๏ธ", - 2063: "๐ŸŒฎ", - 2064: "๐Ÿ•", - 2065: "๐Ÿ”", - 2066: "๐ŸŒญ", - 2067: "๐Ÿญ", - 2013: "๐Ÿง", - 2037: "โš ๏ธ", - 2055: "โšก๏ธ", - 2400: "๐Ÿ‡จ๐Ÿ‡ฆ", - 2036: "โ˜ฃ๏ธ", - 4134: "๐Ÿฆˆ", - 4247: "๐Ÿ—", - 135: "โ˜ฏ๏ธ", - 137: "๐Ÿ", - 144: "๐Ÿš€", - 165: "๐ŸŽถ", - 166: "โค๏ธ", - 2549: "๐Ÿ‡ฎ๐Ÿ‡ฑ", - 2553: "๐Ÿ‡บ๐Ÿ‡ธ", - 2555: "๐Ÿ‡จ๐Ÿ‡ฆ", - 2552: "๐Ÿ‡ฎ๐Ÿ‡ณ", - 2556: "๐Ÿ‡ฆ๐Ÿ‡บ", - 2565: "๐Ÿ‡ฌ๐Ÿ‡ง", - 2567: "๐Ÿ‡ฏ๐Ÿ‡ต", - 2566: "๐Ÿ‡ซ๐Ÿ‡ท", - 2564: "๐Ÿ‡ฉ๐Ÿ‡ช", - 2563: "๐Ÿ‡ฎ๐Ÿ‡น", - 2550: "๐Ÿ‡ญ๐Ÿ‡บ", - 2551: "๐Ÿ‡ต๐Ÿ‡ฑ", - 2560: "๐Ÿ‡ช๐Ÿ‡ธ", - 2561: "๐Ÿ‡ธ๐Ÿ‡ช", - ] - var status: HotlineClientStatus = .disconnected var server: Server? { diff --git a/Hotline/Models/Tracker.swift b/Hotline/Models/Tracker.swift index e9961e9..171cb35 100644 --- a/Hotline/Models/Tracker.swift +++ b/Hotline/Models/Tracker.swift @@ -8,4 +8,11 @@ import SwiftUI self.address = address self.port = port } + + static func parseTrackerAddressAndPort(_ address: String) -> (String, Int) { + let url = URL(string: "hotlinetracker://\(address)") + let port = url?.port ?? HotlinePorts.DefaultTrackerPort + let host = url?.host(percentEncoded: false) ?? "" + return (host.lowercased().trimmingCharacters(in: .whitespacesAndNewlines), port) + } } -- cgit