aboutsummaryrefslogtreecommitdiff
path: root/Hotline/Models
diff options
context:
space:
mode:
Diffstat (limited to 'Hotline/Models')
-rw-r--r--Hotline/Models/Bookmark.swift269
-rw-r--r--Hotline/Models/BookmarksOld.swift (renamed from Hotline/Models/Bookmarks.swift)36
-rw-r--r--Hotline/Models/Server.swift6
3 files changed, 290 insertions, 21 deletions
diff --git a/Hotline/Models/Bookmark.swift b/Hotline/Models/Bookmark.swift
new file mode 100644
index 0000000..160de30
--- /dev/null
+++ b/Hotline/Models/Bookmark.swift
@@ -0,0 +1,269 @@
+import Foundation
+import SwiftData
+
+enum BookmarkType: Codable {
+ case tracker
+ case server
+ case temporary
+}
+
+@Model
+final class Bookmark {
+ var type: BookmarkType
+ var order: Int = 0
+
+ var name: String
+ var address: String
+ var port: Int
+
+ @Attribute(.allowsCloudEncryption)
+ var login: String?
+
+ @Attribute(.allowsCloudEncryption)
+ var password: String?
+
+ @Attribute(.ephemeral)
+ var expanded: Bool = false
+
+ @Attribute(.ephemeral)
+ var loading: Bool = false
+
+ @Attribute(.ephemeral)
+ var serverDescription: String? = nil
+
+ @Transient
+ var servers: [Bookmark] = []
+
+ @Transient
+ var displayAddress: String {
+ switch self.type {
+ case .tracker:
+ if self.port == HotlinePorts.DefaultTrackerPort {
+ return self.address
+ }
+ else {
+ return "\(self.address):\(String(self.port))"
+ }
+
+ case .server, .temporary:
+ if self.port == HotlinePorts.DefaultServerPort {
+ return self.address
+ }
+ else {
+ return "\(self.address):\(String(self.port))"
+ }
+ }
+
+// if let s = server {
+// if s.port == HotlinePorts.DefaultServerPort {
+// return s.address
+// }
+// else {
+// return "\(s.address):\(s.port)"
+// }
+// }
+// else if let b = bookmark {
+// if b.port == HotlinePorts.DefaultServerPort {
+// return b.address
+// }
+// else {
+// return "\(b.address):\(b.port)"
+// }
+// }
+
+// return nil
+ }
+
+ @Transient
+ var server: Server? {
+ switch self.type {
+ case .tracker:
+ 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)
+ }
+ }
+
+ static let DefaultBookmarks: [Bookmark] = [
+ Bookmark(type: .server, name: "The Mobius Strip", address: "67.174.208.111", port: HotlinePorts.DefaultServerPort),
+ Bookmark(type: .server, name: "System 7 Today", address: "hotline.system7today.com", port: HotlinePorts.DefaultServerPort),
+ 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) {
+ self.type = type
+ self.name = name
+ self.address = address
+ self.port = port
+
+ self.login = login
+ self.password = password
+ }
+
+ init(temporaryServer server: Server) {
+ self.type = .temporary
+
+ self.name = server.name ?? server.address
+ self.address = server.address
+ self.port = server.port
+
+ self.serverDescription = server.description
+ }
+
+ static func populateDefaults(force: Bool = false, context: ModelContext) {
+ if force || Bookmark.fetchCount(context: context) == 0 {
+ Bookmark.add(Bookmark.DefaultBookmarks, context: context)
+ }
+ }
+
+ static func fetchAll(context: ModelContext) -> [Bookmark] {
+ let fetchDescriptor = FetchDescriptor<Bookmark>(sortBy: [.init(\.order)])
+ do {
+ let bookmarks: [Bookmark] = try context.fetch(fetchDescriptor)
+ return bookmarks
+ }
+ catch {
+ return []
+ }
+ }
+
+ static func fetchCount(context: ModelContext) -> Int {
+ let descriptor = FetchDescriptor<Bookmark>()
+ return (try? context.fetchCount(descriptor)) ?? 0
+ }
+
+ static func deleteAll(context: ModelContext) {
+ try? context.delete(model: Bookmark.self)
+ }
+
+ static func add(_ bookmark: Bookmark, context: ModelContext) {
+ guard bookmark.type != .temporary else {
+ print("Bookmark: Attempting to add temporary bookmark to store. Aborting.")
+ return
+ }
+
+ let existingBookmarks = Bookmark.fetchAll(context: context)
+
+ // Reindex bookmarks before insert.
+ for existingBookmark in existingBookmarks {
+ existingBookmark.order += 1
+ }
+
+ // Insert new bookmark at start.
+ bookmark.order = 0
+ context.insert(bookmark)
+ }
+
+ static func add(_ bookmarks: [Bookmark], context: ModelContext) {
+ let existingBookmarks = Bookmark.fetchAll(context: context)
+
+ // Reindex bookmarks before insert.
+ for existingBookmark in existingBookmarks {
+ existingBookmark.order += bookmarks.count
+ }
+
+ // Insert new bookmarks at start.
+ var bookmarkIndex = 0
+ for newBookmark in bookmarks {
+ newBookmark.order = bookmarkIndex
+ context.insert(newBookmark)
+ bookmarkIndex += 1
+
+ print("Bookmark: added \(newBookmark.name)")
+ }
+ }
+
+ static func delete(_ bookmark: Bookmark, context: ModelContext) {
+ // Delete bookmark
+ context.delete(bookmark)
+
+ // Reindex bookmarks
+ let existingBookmarks = Bookmark.fetchAll(context: context)
+ var index = 0
+ for existingBookmark in existingBookmarks {
+ existingBookmark.order = index
+ index += 1
+ }
+ }
+
+ static func delete(at indexes: IndexSet, context: ModelContext) {
+ var existingBookmarks = Bookmark.fetchAll(context: context)
+// existingBookmarks.remove(atOffsets: indexes)
+ let bookmarksToDelete = indexes.map { existingBookmarks[$0] }
+
+ // Delete bookmark
+ for bookmark in bookmarksToDelete {
+ context.delete(bookmark)
+ }
+
+ // Reindex bookmarks
+ var index = 0
+ existingBookmarks.remove(atOffsets: indexes)
+ for existingBookmark in existingBookmarks {
+ existingBookmark.order = index
+ index += 1
+ }
+
+ do {
+ try context.save()
+ }
+ catch {
+ print("Bookmark: Failed to save bookmark deletions")
+ }
+ }
+
+ static func move(_ indexes: IndexSet, to newIndex: Int, context: ModelContext) {
+ guard Bookmark.fetchCount(context: context) >= indexes.count else {
+ print("Bookmark: Not enough bookmarks to move requested set")
+ return
+ }
+
+ // Perform move
+ var existingBookmarks = Bookmark.fetchAll(context: context)
+ existingBookmarks.move(fromOffsets: indexes, toOffset: newIndex)
+
+ // Reindex bookmarks
+ var index = 0
+ for existingBookmark in existingBookmarks {
+ existingBookmark.order = index
+ index += 1
+ }
+
+ do {
+ try context.save()
+ }
+ catch {
+ print("Bookmark: Failed to save bookmark reordering")
+ }
+ }
+
+ func fetchServers() async {
+ guard self.type == .tracker else {
+ // self.loading = false
+ return
+ }
+
+ DispatchQueue.main.sync {
+ self.loading = true
+ }
+
+ var fetchedBookmarks: [Bookmark] = []
+
+ let client = HotlineTrackerClient()
+ if let fetchedServers: [HotlineServer] = try? await client.fetchServers(address: self.address, port: self.port) {
+ for fetchedServer in fetchedServers {
+ if let serverName = fetchedServer.name {
+ let server = Server(name: serverName, description: fetchedServer.description, address: fetchedServer.address, port: Int(fetchedServer.port), users: Int(fetchedServer.users))
+ fetchedBookmarks.append(Bookmark(temporaryServer: server))
+ }
+ }
+ }
+
+ let newServers = fetchedBookmarks
+ DispatchQueue.main.sync {
+ self.servers = newServers
+ self.loading = false
+ }
+ }
+}
diff --git a/Hotline/Models/Bookmarks.swift b/Hotline/Models/BookmarksOld.swift
index 3f4971c..0a7bb16 100644
--- a/Hotline/Models/Bookmarks.swift
+++ b/Hotline/Models/BookmarksOld.swift
@@ -5,20 +5,20 @@ extension NSNotification {
static let BookmarkRemoved = Notification.Name("BookmarkRemoved")
}
-enum BookmarkType: String, Codable {
+enum BookmarkOldType: String, Codable {
case tracker = "tracker"
case server = "server"
}
-struct Bookmark: Codable, Equatable {
- let type: BookmarkType
+struct BookmarkOld: Codable, Equatable {
+ let type: BookmarkOldType
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) {
+ init(type: BookmarkOldType, name: String, address: String, port: Int = HotlinePorts.DefaultServerPort, login: String? = nil, password: String? = nil) {
self.type = type
self.name = name
self.address = address
@@ -28,20 +28,20 @@ struct Bookmark: Codable, Equatable {
}
}
-@Observable final class Bookmarks {
- var bookmarks: [Bookmark]? = nil
+@Observable final class BookmarksOld {
+ var bookmarks: [BookmarkOld]? = 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"),
+ static let DefaultBookmarks: [BookmarkOld] = [
+ BookmarkOld(type: .server, name: "System 7 Today", address: "hotline.system7today.com"),
+ BookmarkOld(type: .server, name: "The Mobius Strip", address: "67.174.208.111"),
+ BookmarkOld(type: .tracker, name: "Featured Servers", address: "hltracker.com"),
]
init() {
self.load()
}
- func apply(_ newBookmarks: [Bookmark], save shouldSave: Bool = true) {
+ func apply(_ newBookmarks: [BookmarkOld], save shouldSave: Bool = true) {
self.bookmarks = newBookmarks
if shouldSave {
self.save()
@@ -53,21 +53,21 @@ struct Bookmark: Codable, Equatable {
let jsonData: Data? = jsonString?.data(using: .utf8, allowLossyConversion: false)
let decoder = JSONDecoder()
- var decodedBookmarks = try? decoder.decode([Bookmark].self, from: jsonData ?? Data())
+ var decodedBookmarks = try? decoder.decode([BookmarkOld].self, from: jsonData ?? Data())
if decodedBookmarks == nil || decodedBookmarks?.isEmpty == true {
print("Bookmarks: using default bookmarks")
- decodedBookmarks = Bookmarks.DefaultBookmarks
+ decodedBookmarks = BookmarksOld.DefaultBookmarks
}
else {
print("Bookmarks: using saved bookmarks")
}
- self.bookmarks = [Bookmark](decodedBookmarks!)
+ self.bookmarks = [BookmarkOld](decodedBookmarks!)
}
func save() {
var bookmarksToSave = self.bookmarks
- if bookmarksToSave == Bookmarks.DefaultBookmarks {
+ if bookmarksToSave == BookmarksOld.DefaultBookmarks {
print("Bookmarks: skipping saving default bookmarks")
bookmarksToSave = []
}
@@ -83,7 +83,7 @@ struct Bookmark: Codable, Equatable {
// MARK: -
- func add(_ bookmark: Bookmark, save shouldSave: Bool = true) {
+ func add(_ bookmark: BookmarkOld, save shouldSave: Bool = true) {
self.bookmarks?.insert(bookmark, at: 0)
if shouldSave {
@@ -95,7 +95,7 @@ struct Bookmark: Codable, Equatable {
}
}
- func delete(_ bookmark: Bookmark, save shouldSave: Bool = true) -> Bool {
+ func delete(_ bookmark: BookmarkOld, 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)
@@ -112,7 +112,7 @@ struct Bookmark: Codable, Equatable {
return false
}
- func update(_ bookmark: Bookmark, save shouldSave: Bool = true) -> Bool {
+ func update(_ bookmark: BookmarkOld, 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 {
diff --git a/Hotline/Models/Server.swift b/Hotline/Models/Server.swift
index 27d45c6..e0fe38b 100644
--- a/Hotline/Models/Server.swift
+++ b/Hotline/Models/Server.swift
@@ -10,14 +10,14 @@ struct Server: Codable {
var login: String
var password: String
- init(name: String?, description: String?, address: String, port: Int = HotlinePorts.DefaultServerPort, users: Int = 0, login: String = "", password: String = "") {
+ init(name: String?, description: String?, address: String, port: Int = HotlinePorts.DefaultServerPort, users: Int = 0, login: String? = nil, password: String? = nil) {
self.name = name
self.description = description
self.address = address.lowercased()
self.port = port
self.users = users
- self.login = login
- self.password = password
+ self.login = login ?? ""
+ self.password = password ?? ""
}
init?(url: URL) {