forked from shadowfacts/Tusker
102 lines
3.5 KiB
Swift
102 lines
3.5 KiB
Swift
//
|
|
// ConversationTree.swift
|
|
// Tusker
|
|
//
|
|
// Created by Shadowfacts on 2/4/23.
|
|
// Copyright © 2023 Shadowfacts. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import Pachyderm
|
|
|
|
class ConversationNode {
|
|
let status: StatusMO
|
|
var children: [ConversationNode]
|
|
|
|
init(status: StatusMO) {
|
|
self.status = status
|
|
self.children = []
|
|
}
|
|
}
|
|
struct ConversationTree {
|
|
let ancestors: [ConversationNode]
|
|
let mainStatus: ConversationNode
|
|
var descendants: [ConversationNode] {
|
|
mainStatus.children
|
|
}
|
|
|
|
init(ancestors: [ConversationNode], mainStatus: ConversationNode) {
|
|
self.ancestors = ancestors
|
|
self.mainStatus = mainStatus
|
|
}
|
|
|
|
static func build(for mainStatus: StatusMO, ancestors: [StatusMO], descendants: [StatusMO]) -> ConversationTree {
|
|
let mainStatusNode = ConversationNode(status: mainStatus)
|
|
let ancestors = buildAncestorNodes(mainStatusNode: mainStatusNode, ancestors: ancestors)
|
|
buildDescendantNodes(mainStatusNode: mainStatusNode, descendants: descendants)
|
|
return ConversationTree(ancestors: ancestors, mainStatus: mainStatusNode)
|
|
}
|
|
|
|
private static func buildAncestorNodes(mainStatusNode: ConversationNode, ancestors: [StatusMO]) -> [ConversationNode] {
|
|
var statuses = ancestors
|
|
var parents = [ConversationNode]()
|
|
|
|
var parentID: String? = mainStatusNode.status.inReplyToID
|
|
|
|
while let currentParentID = parentID,
|
|
let parentIndex = statuses.firstIndex(where: { $0.id == currentParentID }) {
|
|
let parentStatus = statuses.remove(at: parentIndex)
|
|
|
|
let node = ConversationNode(status: parentStatus)
|
|
parents.insert(node, at: 0)
|
|
|
|
parentID = parentStatus.inReplyToID
|
|
}
|
|
|
|
// once the parents list is built and in-order, then we walk through and set each node's children
|
|
for (index, node) in parents.enumerated() {
|
|
if index == parents.count - 1 {
|
|
// the last parent is the direct parent of the main status
|
|
node.children = [mainStatusNode]
|
|
} else {
|
|
// otherwise, it's the parent of the status that comes immediately after it in the parents list
|
|
node.children = [parents[index + 1]]
|
|
}
|
|
}
|
|
|
|
return parents
|
|
}
|
|
|
|
// doesn't return anything, since we're modifying the main status node in-place
|
|
private static func buildDescendantNodes(mainStatusNode: ConversationNode, descendants: [StatusMO]) {
|
|
var descendants = descendants
|
|
|
|
func removeAllInReplyTo(id: String) -> [StatusMO] {
|
|
let statuses = descendants.filter { $0.inReplyToID == id }
|
|
descendants.removeAll { $0.inReplyToID == id }
|
|
return statuses
|
|
}
|
|
|
|
var nodes: [String: ConversationNode] = [
|
|
mainStatusNode.status.id: mainStatusNode
|
|
]
|
|
|
|
var idsToCheck = [mainStatusNode.status.id]
|
|
|
|
while !idsToCheck.isEmpty {
|
|
let inReplyToID = idsToCheck.removeFirst()
|
|
let nodeForID = nodes[inReplyToID]!
|
|
|
|
let inReply = removeAllInReplyTo(id: inReplyToID)
|
|
for reply in inReply {
|
|
idsToCheck.append(reply.id)
|
|
|
|
let replyNode = ConversationNode(status: reply)
|
|
nodes[reply.id] = replyNode
|
|
|
|
nodeForID.children.append(replyNode)
|
|
}
|
|
}
|
|
}
|
|
}
|