aboutsummaryrefslogtreecommitdiff
path: root/Hotline/Models
diff options
context:
space:
mode:
authorDustin Mierau <dustin@mierau.me>2023-12-16 09:16:15 -0800
committerDustin Mierau <dustin@mierau.me>2023-12-16 09:16:15 -0800
commit3e4fbdfcab4a4d2435aed27a16131a6ca26d7408 (patch)
treea1aec47c2de30074257dec14e3b2615a8b178033 /Hotline/Models
parenta428e3e28fb851cec65ff27f212c19bce08e5369 (diff)
Implemented basic news reading support. Various UI tweaks.
Diffstat (limited to 'Hotline/Models')
-rw-r--r--Hotline/Models/ChatMessage.swift2
-rw-r--r--Hotline/Models/Hotline.swift206
-rw-r--r--Hotline/Models/NewsCategory.swift34
-rw-r--r--Hotline/Models/NewsInfo.swift58
-rw-r--r--Hotline/Models/Server.swift4
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