diff options
| author | Dustin Mierau <dustin@mierau.me> | 2023-12-16 09:16:15 -0800 |
|---|---|---|
| committer | Dustin Mierau <dustin@mierau.me> | 2023-12-16 09:16:15 -0800 |
| commit | 3e4fbdfcab4a4d2435aed27a16131a6ca26d7408 (patch) | |
| tree | a1aec47c2de30074257dec14e3b2615a8b178033 /Hotline/Models | |
| parent | a428e3e28fb851cec65ff27f212c19bce08e5369 (diff) | |
Implemented basic news reading support. Various UI tweaks.
Diffstat (limited to 'Hotline/Models')
| -rw-r--r-- | Hotline/Models/ChatMessage.swift | 2 | ||||
| -rw-r--r-- | Hotline/Models/Hotline.swift | 206 | ||||
| -rw-r--r-- | Hotline/Models/NewsCategory.swift | 34 | ||||
| -rw-r--r-- | Hotline/Models/NewsInfo.swift | 58 | ||||
| -rw-r--r-- | Hotline/Models/Server.swift | 4 |
5 files changed, 254 insertions, 50 deletions
diff --git a/Hotline/Models/ChatMessage.swift b/Hotline/Models/ChatMessage.swift index 8a57d61..e585cdf 100644 --- a/Hotline/Models/ChatMessage.swift +++ b/Hotline/Models/ChatMessage.swift @@ -15,7 +15,7 @@ struct ChatMessage: Identifiable { let date: Date let username: String? - static let parser = /^\s*([^\:]+)\:\s*(.+)/ + static let parser = /^\s*([^\:]+):\s*([\s\S]+)$/ init(text: String, type: ChatMessageType, date: Date) { self.type = type diff --git a/Hotline/Models/Hotline.swift b/Hotline/Models/Hotline.swift index 92366fa..c7474b2 100644 --- a/Hotline/Models/Hotline.swift +++ b/Hotline/Models/Hotline.swift @@ -6,17 +6,31 @@ import SwiftUI var status: HotlineClientStatus = .disconnected - var server: Server? = nil - var serverVersion: UInt16? = nil - var serverName: String? = nil + var server: Server? { + didSet { + self.updateServerTitle() + } + } + var serverVersion: UInt16? { + didSet { + self.updateServerTitle() + } + } + var serverName: String? { + didSet { + self.updateServerTitle() + } + } + var serverTitle: String = "Server" var username: String = "bolt" var iconID: UInt = 128 + var access: HotlineUserAccessOptions? var users: [User] = [] var chat: [ChatMessage] = [] var messageBoard: [String] = [] var files: [FileInfo] = [] - var news: [NewsCategory] = [] + var news: [NewsInfo] = [] // MARK: - @@ -28,8 +42,8 @@ import SwiftUI // MARK: - - @MainActor func getServers(address: String, port: Int = Tracker.defaultPort) async -> [Server] { - let fetchedServers: [HotlineServer] = await self.trackerClient.fetchServers(address: address, port: port) + @MainActor func getServerList(tracker: String, port: Int = Tracker.defaultPort) async -> [Server] { + let fetchedServers: [HotlineServer] = await self.trackerClient.fetchServers(address: tracker, port: port) var servers: [Server] = [] @@ -48,6 +62,7 @@ import SwiftUI @MainActor func login(server: Server, login: String, password: String, username: String, iconID: UInt) async -> Bool { self.server = server + self.serverName = server.name self.username = username self.iconID = iconID @@ -107,24 +122,164 @@ import SwiftUI } } - @MainActor func getNewsCategories() async -> [NewsCategory] { + @MainActor func getNewsArticle(id articleID: UInt, at path: [String], flavor: String) async -> String? { + return await withCheckedContinuation { [weak self] continuation in + self?.client.sendGetNewsArticle(id: UInt32(articleID), path: path, flavor: flavor, sent: { success in + if !success { + continuation.resume(returning: nil) + return + } + + print("GET NEWS CATS FROM \(path)") + }, reply: { articleText in +// let parentNews = self?.findNews(in: self?.news ?? [], at: path) + +// var newCategories: [NewsInfo] = [] +// for category in categories { +// newCategories.append(NewsInfo(hotlineNewsCategory: category)) +// } +// +// if let parent = existingNewsItem { +// parent.children = newCategories +// } +// else if path.isEmpty { +// self?.news = newCategories +// } + + continuation.resume(returning: articleText) + }) + } + + } + + @MainActor func getNewsList(at path: [String] = []) async -> [NewsInfo] { return await withCheckedContinuation { [weak self] continuation in - self?.client.sendGetNewsCategories(sent: { success in + var requestCategories = true + + let existingNewsItem = self?.findNews(in: self?.news ?? [], at: path) + + if existingNewsItem != nil { + if existingNewsItem!.type != .bundle { + requestCategories = false + } + } + + if requestCategories { + self?.client.sendGetNewsCategories(path: path, sent: { success in + if !success { + continuation.resume(returning: []) + return + } + + print("GET NEWS CATS FROM \(path)") + }, reply: { [weak self] categories in +// let parentNews = self?.findNews(in: self?.news ?? [], at: path) + + var newCategories: [NewsInfo] = [] + for category in categories { + newCategories.append(NewsInfo(hotlineNewsCategory: category)) + } + + if let parent = existingNewsItem { + parent.children = newCategories + } + else if path.isEmpty { + self?.news = newCategories + } + + continuation.resume(returning: newCategories) + }) + } + else { + self?.client.sendGetNewsArticles(path: path, sent: { success in + if !success { + continuation.resume(returning: []) + return + } + + print("GET NEWS ARTICLES FROM \(path)") + }, reply: { [weak self] articles in +// let parentNews = self?.findNews(in: self?.news ?? [], at: path) + print("GENERATING NEWS") + + var newArticles: [NewsInfo] = [] + for article in articles { + newArticles.append(NewsInfo(hotlineNewsArticle: article)) + } + + if let parent = existingNewsItem { + print("UNDER PARENT:", parent.name) + parent.children = newArticles + + print(parent.children) + } + else if path.isEmpty { + self?.news = newArticles + } + + continuation.resume(returning: newArticles) + }) + } + } + } + + @MainActor func getNewsCategories(at path: [String] = []) async -> [NewsInfo] { + return await withCheckedContinuation { [weak self] continuation in + self?.client.sendGetNewsCategories(path: path, sent: { success in if !success { continuation.resume(returning: []) return } + + print("GET NEWS CATS FROM \(path)") }, reply: { [weak self] categories in - var newCategories: [NewsCategory] = [] + let parentNews = self?.findNews(in: self?.news ?? [], at: path) + + var newCategories: [NewsInfo] = [] for category in categories { - newCategories.append(NewsCategory(hotlineNewsCategory: category)) + newCategories.append(NewsInfo(hotlineNewsCategory: category)) + } + + if let parent = parentNews { + parent.children = newCategories + } + else if path.isEmpty { + self?.news = newCategories } - self?.news = newCategories continuation.resume(returning: newCategories) }) } } + + @MainActor func getArticles(at path: [String]) async -> [NewsInfo] { + return await withCheckedContinuation { [weak self] continuation in + self?.client.sendGetNewsArticles(path: path, sent: { success in + if !success { + continuation.resume(returning: []) + return + } + }, reply: { [weak self] articles in + print("ARTICLES?", articles) + + continuation.resume(returning: []) + }) +// self?.client.sendGetNewsCategories(sent: { success in +// if !success { +// continuation.resume(returning: []) +// return +// } +// }, reply: { [weak self] categories in +// var newCategories: [NewsCategory] = [] +// for category in categories { +// newCategories.append(NewsCategory(hotlineNewsCategory: category)) +// } +// self?.news = newCategories +// +// continuation.resume(returning: newCategories) +// }) + } + } // @MainActor func updateUsers() async -> [User] { @@ -143,6 +298,7 @@ import SwiftUI if status == .disconnected { self.serverVersion = nil self.serverName = nil + self.access = nil self.users = [] self.chat = [] self.messageBoard = [] @@ -210,6 +366,8 @@ import SwiftUI func hotlineReceivedUserAccess(options: HotlineUserAccessOptions) { print("Hotline: got access options") print(options, options.contains(.canSendChat), options.contains(.canBroadcast)) + + self.access = options } func hotlineReceivedError(message: String) { @@ -218,6 +376,10 @@ import SwiftUI // MARK: - Utilities + func updateServerTitle() { + self.serverTitle = self.serverName ?? self.server?.name ?? server?.address ?? "Server" + } + private func addOrUpdateHotlineUser(_ user: HotlineUser) { if let i = self.users.firstIndex(where: { $0.id == user.id }) { print("Hotline: updating user \(self.users[i].name)") @@ -233,8 +395,6 @@ import SwiftUI private func findFile(in filesToSearch: [FileInfo], at path: [String]) -> FileInfo? { guard !path.isEmpty, !filesToSearch.isEmpty else { return nil } - // var stack: [([HotlineFile], [String])] = [(self.files!, path)] - let currentName = path[0] for file in filesToSearch { @@ -251,4 +411,24 @@ import SwiftUI return nil } + + private func findNews(in newsToSearch: [NewsInfo], at path: [String]) -> NewsInfo? { + guard !path.isEmpty, !newsToSearch.isEmpty else { return nil } + + let currentName = path[0] + + for news in newsToSearch { + if news.name == currentName { + if path.count == 1 { + return news + } + else if !news.children.isEmpty { + let remainingPath = Array(path[1...]) + return self.findNews(in: news.children, at: remainingPath) + } + } + } + + return nil + } } diff --git a/Hotline/Models/NewsCategory.swift b/Hotline/Models/NewsCategory.swift deleted file mode 100644 index 0ba87a7..0000000 --- a/Hotline/Models/NewsCategory.swift +++ /dev/null @@ -1,34 +0,0 @@ -import SwiftUI - -enum NewsCategoryType { - case bundle - case category -} - -@Observable class NewsCategory: Identifiable, Hashable { - let id: UUID = UUID() - - let name: String - let count: UInt16 - let type: NewsCategoryType - - init(hotlineNewsCategory: HotlineNewsCategory) { - self.name = hotlineNewsCategory.name - self.count = hotlineNewsCategory.count - - if hotlineNewsCategory.type == 2 { - self.type = .bundle - } - else { - self.type = .category - } - } - - func hash(into hasher: inout Hasher) { - hasher.combine(self.id) - } - - static func == (lhs: NewsCategory, rhs: NewsCategory) -> Bool { - return lhs.id == rhs.id - } -} diff --git a/Hotline/Models/NewsInfo.swift b/Hotline/Models/NewsInfo.swift new file mode 100644 index 0000000..ad6bfcd --- /dev/null +++ b/Hotline/Models/NewsInfo.swift @@ -0,0 +1,58 @@ +import SwiftUI + +enum NewsInfoType { + case bundle + case category + case article +} + +@Observable class NewsInfo: Identifiable, Hashable { + let id: UUID = UUID() + + let name: String + let count: UInt + let type: NewsInfoType + + let categoryID: UUID? + let articleID: UInt? + + let path: [String] + var children: [NewsInfo] = [] + + var articleFlavors: [String]? + + init(hotlineNewsCategory: HotlineNewsCategory) { + self.categoryID = hotlineNewsCategory.id + self.articleID = nil + self.name = hotlineNewsCategory.name + self.count = UInt(hotlineNewsCategory.count) + self.path = hotlineNewsCategory.path + + if hotlineNewsCategory.type == 2 { + self.type = .bundle + } + else { + self.type = .category + } + } + + init(hotlineNewsArticle: HotlineNewsArticle) { + self.articleID = UInt(hotlineNewsArticle.id) + self.categoryID = nil + self.name = hotlineNewsArticle.title + self.count = 0 +// self.count = UInt(hotlineNewsArticle.count) + self.path = hotlineNewsArticle.path + self.type = .article + + self.articleFlavors = hotlineNewsArticle.flavors.map { $0.0 } + } + + func hash(into hasher: inout Hasher) { + hasher.combine(self.id) + } + + static func == (lhs: NewsInfo, rhs: NewsInfo) -> Bool { + return lhs.id == rhs.id + } +} diff --git a/Hotline/Models/Server.swift b/Hotline/Models/Server.swift index b159a1a..8b5ea56 100644 --- a/Hotline/Models/Server.swift +++ b/Hotline/Models/Server.swift @@ -4,13 +4,13 @@ import SwiftUI static let defaultPort: Int = 5500 let id: UUID = UUID() - let name: String + let name: String? let description: String? let users: Int let address: String let port: Int - init(name: String, description: String?, address: String, port: Int, users: Int = 0) { + init(name: String?, description: String?, address: String, port: Int, users: Int = 0) { self.name = name self.description = description self.address = address |