Implement basic tap-to-collapse

This commit is contained in:
fwcd 2024-08-03 03:31:23 +02:00
parent 2d251a5b6d
commit ec5bc5c1bb
6 changed files with 56 additions and 3 deletions

View file

@ -113,12 +113,15 @@ public struct StatusDetailView: View {
.navigationBarTitleDisplayMode(.inline)
}
@ViewBuilder
private func makeStatusesListView(statuses: [Status]) -> some View {
ForEach(statuses) { status in
let collapsedIds = viewModel.hierarchyCollapseState.implicitlyCollapsedStatusIds(for: statuses)
ForEach(statuses.filter { !collapsedIds.contains($0.id) }) { status in
let (indentationLevel, extraInsets) = viewModel.getIndentationLevel(id: status.id, maxIndent: userPreferences.getRealMaxIndent())
let viewModel: StatusRowViewModel = .init(status: status,
client: client,
routerPath: routerPath,
hierarchyCollapseState: viewModel.hierarchyCollapseState,
scrollToId: $viewModel.scrollToId)
let isFocused = self.viewModel.statusId == status.id

View file

@ -11,6 +11,8 @@ import SwiftUI
var client: Client?
var routerPath: RouterPath?
let hierarchyCollapseState = StatusHierarchyCollapseState()
enum State {
case loading, display(statuses: [Status]), error(error: Error)

View file

@ -0,0 +1,24 @@
import Observation
import Models
@MainActor
@Observable public class StatusHierarchyCollapseState {
public var explicitlyCollapsedStatusIds: Set<String>
public init(explicitlyCollapsedStatusIds: Set<String> = []) {
self.explicitlyCollapsedStatusIds = explicitlyCollapsedStatusIds
}
public func implicitlyCollapsedStatusIds(for statuses: [Status]) -> Set<String> {
let childs: [String: [String]] = Dictionary(
grouping: statuses.filter { $0.inReplyToId != nil },
by: { $0.inReplyToId! }
).mapValues { $0.map(\.id) }
func descendants(for id: String) -> [String] {
(childs[id] ?? []).flatMap { [$0] + descendants(for: $0) }
}
return Set(explicitlyCollapsedStatusIds.flatMap(descendants(for:)))
}
}

View file

@ -29,7 +29,9 @@ public struct StatusesListView<Fetcher>: View where Fetcher: StatusesFetcher {
switch fetcher.statusesState {
case .loading:
ForEach(Status.placeholders()) { status in
StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath),
StatusRowView(viewModel: .init(status: status,
client: client,
routerPath: routerPath),
context: .timeline)
.redacted(reason: .placeholder)
.allowsHitTesting(false)

View file

@ -348,7 +348,11 @@ public struct StatusRowView: View {
private func handleTap() {
guard !isFocused else { return }
viewModel.navigateToDetail()
if indentationLevel > 0, viewModel.hierarchyCollapseState != nil {
viewModel.isHierarchyExplicitlyCollapsed.toggle()
} else {
viewModel.navigateToDetail()
}
}
}

View file

@ -18,6 +18,8 @@ import SwiftUI
let client: Client
let routerPath: RouterPath
let hierarchyCollapseState: StatusHierarchyCollapseState?
let userFollowedTag: HTMLString.Link?
@ -67,6 +69,20 @@ import SwiftUI
}
}
}
// toggled on tap, collapses the post with its hierarchy of replies
var isHierarchyExplicitlyCollapsed: Bool {
get {
hierarchyCollapseState?.explicitlyCollapsedStatusIds.contains(status.id) ?? false
}
set {
if newValue {
hierarchyCollapseState?.explicitlyCollapsedStatusIds.insert(status.id)
} else {
hierarchyCollapseState?.explicitlyCollapsedStatusIds.remove(status.id)
}
}
}
// used by the button to expand a collapsed post
var isCollapsed: Bool = true {
@ -162,6 +178,7 @@ import SwiftUI
public init(status: Status,
client: Client,
routerPath: RouterPath,
hierarchyCollapseState: StatusHierarchyCollapseState? = nil,
isRemote: Bool = false,
showActions: Bool = true,
textDisabled: Bool = false,
@ -171,6 +188,7 @@ import SwiftUI
finalStatus = status.reblog ?? status
self.client = client
self.routerPath = routerPath
self.hierarchyCollapseState = hierarchyCollapseState
self.isRemote = isRemote
self.showActions = showActions
self.textDisabled = textDisabled