diff options
| author | Dustin Mierau <dustin@mierau.me> | 2024-05-14 13:35:26 -0700 |
|---|---|---|
| committer | Dustin Mierau <dustin@mierau.me> | 2024-05-14 13:35:26 -0700 |
| commit | 87e979fb48da4a46e64544cea3e7b7d5fc32caa1 (patch) | |
| tree | 4c10cdba5a88544543bf45ae454f643decf1358c /Hotline/Models | |
| parent | e9fcf8c3b14e77479a6fb471bb51cd82a008147c (diff) | |
Newsgroup posting now works. Added reload button to news. More work on server and user icons.
Diffstat (limited to 'Hotline/Models')
| -rw-r--r-- | Hotline/Models/Hotline.swift | 220 | ||||
| -rw-r--r-- | Hotline/Models/NewsInfo.swift | 44 |
2 files changed, 176 insertions, 88 deletions
diff --git a/Hotline/Models/Hotline.swift b/Hotline/Models/Hotline.swift index 5dc61ab..4c9defa 100644 --- a/Hotline/Models/Hotline.swift +++ b/Hotline/Models/Hotline.swift @@ -131,6 +131,7 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { var files: [FileInfo] = [] var filesLoaded: Bool = false var news: [NewsInfo] = [] + private var newsLookup: [String:NewsInfo] = [:] var newsLoaded: Bool = false var instantMessages: [UInt16:[InstantMessage]] = [:] var transfers: [TransferInfo] = [] @@ -307,88 +308,110 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { } - @MainActor func getNewsList(at path: [String] = []) async -> [NewsInfo] { + @MainActor func getNewsList(at path: [String] = []) async { return await withCheckedContinuation { [weak self] continuation in - var requestCategories = true + let parentNewsGroup = self?.findNews(in: self?.news ?? [], at: path) - 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) { [weak self] categories in -// let parentNews = self?.findNews(in: self?.news ?? [], at: path) + // Send a categories request for bundle paths or root (empty path) + if path.isEmpty || parentNewsGroup?.type == .bundle { + print("Hotline: Requesting categories at: /\(path.joined(separator: "/"))") + + self?.client.sendGetNewsCategories(path: path) { @MainActor [weak self] categories in + // Create info for each category returned. + var newCategoryInfos: [NewsInfo] = [] - var newCategories: [NewsInfo] = [] + // Transform hotline categories into NewsInfo objects. for category in categories { - newCategories.append(NewsInfo(hotlineNewsCategory: category)) - } - - DispatchQueue.main.async { - if let parent = existingNewsItem { - parent.children = newCategories - } - else if path.isEmpty { - self?.newsLoaded = true - self?.news = newCategories + var newsCategoryInfo = NewsInfo(hotlineNewsCategory: category) + + if let lookupPath = newsCategoryInfo.lookupPath { + // Merge returned category info with existing category info. + if let existingCategoryInfo = self?.newsLookup[lookupPath] { + print("Hotline: Merging category into existing category at \(lookupPath)") + + existingCategoryInfo.count = newsCategoryInfo.count + existingCategoryInfo.name = newsCategoryInfo.name + existingCategoryInfo.path = newsCategoryInfo.path + existingCategoryInfo.categoryID = newsCategoryInfo.categoryID + newsCategoryInfo = existingCategoryInfo + } + else { + print("Hotline: New category added at \(lookupPath)") + self?.newsLookup[lookupPath] = newsCategoryInfo + } } - continuation.resume(returning: newCategories) + newCategoryInfos.append(newsCategoryInfo) + } + + if let parent = parentNewsGroup { + parent.children = newCategoryInfos + } + else if path.isEmpty { + self?.newsLoaded = true + self?.news = newCategoryInfos } + + continuation.resume() } } else { - self?.client.sendGetNewsArticles(path: path) { [weak self] articles in -// let parentNews = self?.findNews(in: self?.news ?? [], at: path) - print("GENERATING NEWS") + print("Hotline: Requesting articles at: /\(path.joined(separator: "/"))") + + self?.client.sendGetNewsArticles(path: path) { @MainActor [weak self] articles in + print("Hotline: Organizing news at \(path.joined(separator: "/"))") - var newArticles: [NewsInfo] = [] - for article in articles { - newArticles.append(NewsInfo(hotlineNewsArticle: article)) - } + // Create info for each article returned. + var newArticleInfos: [NewsInfo] = [] - let organizedNewsArticles: [NewsInfo] = self?.organizeNewsList(newArticles) ?? [] - - DispatchQueue.main.async { - if let parent = existingNewsItem { - print("UNDER PARENT:", parent.name) - parent.children = organizedNewsArticles - - print(parent.children) - } - else if path.isEmpty { - self?.news = organizedNewsArticles + for article in articles { + var newsArticleInfo = NewsInfo(hotlineNewsArticle: article) + + if let lookupPath = newsArticleInfo.lookupPath { + // Merge returned category info with existing category info. + if let existingArticleInfo = self?.newsLookup[lookupPath] { + print("Hotline: Merging article into existing article at \(lookupPath)") + + existingArticleInfo.count = newsArticleInfo.count + existingArticleInfo.name = newsArticleInfo.name + existingArticleInfo.path = newsArticleInfo.path + existingArticleInfo.articleUsername = newsArticleInfo.articleUsername + existingArticleInfo.articleDate = newsArticleInfo.articleDate + existingArticleInfo.articleFlavors = newsArticleInfo.articleFlavors + existingArticleInfo.articleID = newsArticleInfo.articleID + newsArticleInfo = existingArticleInfo + } + else { + print("Hotline: New article added at \(lookupPath)") + self?.newsLookup[lookupPath] = newsArticleInfo + } } - continuation.resume(returning: organizedNewsArticles) + newArticleInfos.append(newsArticleInfo) + } + + let organizedNewsArticles: [NewsInfo] = self?.organizeNewsArticles(newArticleInfos) ?? [] + if let parent = parentNewsGroup { + parent.children = organizedNewsArticles } + + continuation.resume() } } } } - func organizeNewsList(_ news: [NewsInfo]) -> [NewsInfo] { - var articleMap: [UInt: NewsInfo] = [:] - - // Create lookup table of each news item. - for article in news { - if let articleID = article.articleID { - articleMap[articleID] = article - } - } - + func organizeNewsArticles(_ flatArticles: [NewsInfo]) -> [NewsInfo] { // Place articles under their parent. var organized: [NewsInfo] = [] - for article in news { - if let parentID = article.parentID, - parentID != 0, - let parentArticle = articleMap[parentID] { - article.expanded = true - parentArticle.children.append(article) + for article in flatArticles { + if let parentLookupPath = article.parentArticleLookupPath, + let parentArticle = self.newsLookup[parentLookupPath] { +// article.expanded = true + if parentArticle.children.firstIndex(of: article) == nil { + article.expanded = true + parentArticle.children.append(article) + } } else { organized.append(article) @@ -398,30 +421,51 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { return organized } - @MainActor func getNewsCategories(at path: [String] = []) async -> [NewsInfo] { + @MainActor func postNewsArticle(title: String, body: String, at path: [String], parentID: UInt32 = 0) async -> Bool { return await withCheckedContinuation { [weak self] continuation in - self?.client.sendGetNewsCategories(path: path) { [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)) - } - - DispatchQueue.main.async { - if let parent = parentNews { - parent.children = newCategories - } - else if path.isEmpty { - self?.news = newCategories - } - - continuation.resume(returning: newCategories) - } + guard let client = self?.client else { + continuation.resume(returning: false) + return } + + client.postNewsArticle(title: title, text: body, path: path, parentID: parentID, callback: { success in + print("Hotline: News article posted? \(success)") + continuation.resume(returning: success) + }) } } +// @MainActor func getNewsCategories(at path: [String] = []) async -> [NewsInfo] { +// return await withCheckedContinuation { [weak self] continuation in +// guard let client = self?.client else { +// continuation.resume(returning: []) +// return +// } +// +// client.sendGetNewsCategories(path: path) { [weak self] categories in +// let parentNews = self?.findNews(in: self?.news ?? [], at: path) +// +// var newCategories: [NewsInfo] = [] +// for category in categories { +// let categoryInfo: NewsInfo = NewsInfo(hotlineNewsCategory: category) +// newCategories.append(categoryInfo) +// self?.newsLookup[categoryInfo.path.joined(separator: "/")] = categoryInfo +// } +// +// DispatchQueue.main.async { +// if let parent = parentNews { +// parent.children = newCategories +// } +// else if path.isEmpty { +// 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) { articles in @@ -953,9 +997,7 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { } private func findNews(in newsToSearch: [NewsInfo], at path: [String]) -> NewsInfo? { - guard !path.isEmpty, !newsToSearch.isEmpty else { return nil } - - let currentName = path[0] + guard !path.isEmpty, !newsToSearch.isEmpty, let currentName = path.first else { return nil } for news in newsToSearch { if news.name == currentName { @@ -971,4 +1013,18 @@ class Hotline: Equatable, HotlineClientDelegate, HotlineFileClientDelegate { return nil } + + private func findNewsArticle(id articleID: UInt32, at path: [String]) -> NewsInfo? { + guard let parent = self.findNews(in: self.news, at: path), !parent.children.isEmpty else { + return nil + } + + return parent.children.first { child in + guard let childArticleID = child.articleID else { + return false + } + + return child.type == .article && child.articleID == childArticleID + } + } } diff --git a/Hotline/Models/NewsInfo.swift b/Hotline/Models/NewsInfo.swift index eecd550..6ee28b8 100644 --- a/Hotline/Models/NewsInfo.swift +++ b/Hotline/Models/NewsInfo.swift @@ -9,15 +9,15 @@ enum NewsInfoType { @Observable class NewsInfo: Identifiable, Hashable { let id: UUID = UUID() - let name: String - let count: UInt + var name: String + var count: UInt let type: NewsInfoType - let categoryID: UUID? - let articleID: UInt? - let parentID: UInt? + var categoryID: UUID? + var articleID: UInt? + var parentID: UInt? - let path: [String] + var path: [String] var expanded: Bool = false var children: [NewsInfo] = [] @@ -30,6 +30,38 @@ enum NewsInfoType { self.type == .bundle || self.type == .category || self.children.count > 0 } + var lookupPath: String? { + switch self.type { + case .bundle, .category: + return "/\(self.path.joined(separator: "/"))" + case .article: + guard let aid = self.articleID else { + return nil + } +// if let pid = self.parentID, pid != 0 { +// return "/\(self.path.joined(separator: "/"))/\(pid)/\(aid)" +// } + return "/\(self.path.joined(separator: "/"))/\(aid)" + } + } + + var parentArticleLookupPath: String? { + switch self.type { + case .bundle, .category: +// if self.path.count <= 1 { +// return "/" +// } +// let parentPath = self.path[0..<self.path.count-1] +// return "/\(parentPath.joined(separator: "/"))" + return nil + case .article: + guard let pid = self.parentID, pid != 0 else { + return nil + } + return "/\(self.path.joined(separator: "/"))/\(pid)" + } + } + init(hotlineNewsCategory: HotlineNewsCategory) { self.categoryID = hotlineNewsCategory.id self.articleID = nil |