Create SidebarTree. Comment-out SidebarViewController for now.

This commit is contained in:
Brent Simmons
2025-02-07 22:56:55 -08:00
parent 9301bb9961
commit e417f782f9
2 changed files with 469 additions and 133 deletions

View File

@@ -0,0 +1,259 @@
//
// SidebarTree.swift
// NetNewsWire-iOS
//
// Created by Brent Simmons on 2/7/25.
// Copyright © 2025 Ranchero Software. All rights reserved.
//
import Foundation
import Account
typealias SectionID = Int
typealias ItemID = Int
protocol Section: Identifiable {}
protocol Item: Identifiable {}
@MainActor protocol SidebarContainer: Identifiable {
var isExpanded: Bool { get set }
var items: [any Item] { get }
func updateItems()
}
extension SidebarContainer {
func createFeedsDictionary() -> [String: SidebarFeed] {
var d = [String: SidebarFeed]() // feedID: SidebarFeed
for item in items where item is SidebarFeed {
let sidebarFeed = item as! SidebarFeed
d[sidebarFeed.feedID] = sidebarFeed
}
return d
}
func createFoldersDictionary() -> [Int: SidebarFolder] {
var d = [Int: SidebarFolder]()
for item in items where item is SidebarFolder {
let sidebarFolder = item as! SidebarFolder
d[sidebarFolder.folderID] = sidebarFolder
}
return d
}
}
// MARK: - SidebarSmartFeedsFolder
@MainActor final class SidebarSmartFeedsFolder: Section, SidebarContainer {
let id = createID()
var isExpanded = true
let items: [any Item] = [
SidebarSmartFeed(SmartFeedsController.shared.todayFeed),
SidebarSmartFeed(SmartFeedsController.shared.unreadFeed),
SidebarSmartFeed(SmartFeedsController.shared.starredFeed)
]
func updateItems() {
}
}
// MARK: - SidebarSmartFeed
@MainActor final class SidebarSmartFeed: Item {
let id = createID()
let smartFeed: any PseudoFeed
init(_ smartFeed: any PseudoFeed) {
self.smartFeed = smartFeed
}
}
// MARK: - SidebarAccount
@MainActor final class SidebarAccount: Section, SidebarContainer {
let id = createID()
let accountID: String
weak var account: Account?
var isExpanded = true
var items = [any Item]() { // top-level feeds and folders
didSet {
feedsDictionary = createFeedsDictionary()
foldersDictionary = createFoldersDictionary()
}
}
private var feedsDictionary = [String: SidebarFeed]()
private var foldersDictionary = [Int: SidebarFolder]()
init(_ account: Account) {
self.accountID = account.accountID
self.account = account
updateItems()
}
func updateItems() {
guard let account else {
items = [any Item]()
return
}
var sidebarFeeds = [SidebarFeed]()
for feed in account.topLevelFeeds {
if let existingFeed = feedsDictionary[feed.feedID] {
sidebarFeeds.append(existingFeed)
} else {
sidebarFeeds.append(SidebarFeed(feed))
}
}
var sidebarFolders = [SidebarFolder]()
if let folders = account.folders {
for folder in folders {
if let existingFolder = foldersDictionary[folder.folderID] {
sidebarFolders.append(existingFolder)
} else {
sidebarFolders.append(SidebarFolder(folder))
}
}
}
for folder in sidebarFolders {
folder.updateItems()
}
// TODO: sort feeds
items = sidebarFeeds + sidebarFolders
}
}
// MARK: - SidebarFolder
@MainActor final class SidebarFolder: Item, SidebarContainer {
let id = createID()
let folderID: Int
weak var folder: Folder?
var isExpanded = false
var items = [any Item]() { // SidebarFeed
didSet {
feedsDictionary = createFeedsDictionary()
}
}
private var feedsDictionary = [String: SidebarFeed]()
init(_ folder: Folder) {
self.folderID = folder.folderID
self.folder = folder
updateItems()
}
func updateItems() {
guard let folder else {
items = [any Item]()
return
}
var sidebarFeeds = [any Item]()
for feed in folder.topLevelFeeds {
if let existingFeed = feedsDictionary[feed.feedID] {
sidebarFeeds.append(existingFeed)
} else {
sidebarFeeds.append(SidebarFeed(feed))
}
}
// TODO: sort feeds
items = sidebarFeeds
}
}
// MARK: - SidebarFeed
@MainActor final class SidebarFeed: Item {
let id = createID()
let feedID: String
weak var feed: Feed?
init(_ feed: Feed) {
self.feedID = feed.feedID
self.feed = feed
}
}
// MARK: - SidebarTree
@MainActor final class SidebarTree {
var sections = [any Section]()
private lazy var sidebarSmartFeedsFolder = SidebarSmartFeedsFolder()
func rebuildTree() {
rebuildSections()
for section in sections where section is SidebarAccount {
let sidebarAccount = section as! SidebarAccount
sidebarAccount.updateItems()
}
}
}
private extension SidebarTree {
func rebuildSections() {
var updatedSections = [any Section]()
updatedSections.append(sidebarSmartFeedsFolder)
for account in AccountManager.shared.activeAccounts {
if let existingSection = existingSection(for: account) {
updatedSections.append(existingSection)
} else {
updatedSections.append(SidebarAccount(account))
}
}
// TODO: sort accounts
sections = updatedSections
}
func existingSection(for account: Account) -> (any Section)? {
// Linear search through array because its just a few items.
let accountID = account.accountID
for sidebarSection in sections where sidebarSection is SidebarAccount {
let sidebarAccount = sidebarSection as! SidebarAccount
if sidebarAccount.accountID == accountID {
return sidebarAccount
}
}
return nil
}
}
// MARK: - IDs
@MainActor private var autoIncrementingID = 0
@MainActor private func createID() -> Int {
defer { autoIncrementingID += 1 }
return autoIncrementingID
}