IceCubesApp/Packages/Status/Sources/Status/Detail/StatusDetailView.swift

164 lines
4.8 KiB
Swift
Raw Normal View History

2023-01-17 10:36:01 +00:00
import DesignSystem
2022-12-22 09:53:36 +00:00
import Env
2023-01-17 10:36:01 +00:00
import Models
2022-12-21 17:28:21 +00:00
import Network
2023-01-17 10:36:01 +00:00
import Shimmer
import SwiftUI
import DesignSystem
2022-11-29 10:46:02 +00:00
public struct StatusDetailView: View {
2022-12-29 09:39:34 +00:00
@EnvironmentObject private var theme: Theme
2022-12-25 16:39:23 +00:00
@EnvironmentObject private var currentAccount: CurrentAccount
@EnvironmentObject private var watcher: StreamWatcher
2022-12-21 17:28:21 +00:00
@EnvironmentObject private var client: Client
@EnvironmentObject private var routerPath: RouterPath
2023-01-12 17:25:37 +00:00
@Environment(\.openURL) private var openURL
2022-12-21 17:28:21 +00:00
@StateObject private var viewModel: StatusDetailViewModel
@State private var isLoaded: Bool = false
@State private var statusHeight: CGFloat = 0
2022-11-29 10:46:02 +00:00
public init(statusId: String) {
2022-12-21 17:28:21 +00:00
_viewModel = StateObject(wrappedValue: .init(statusId: statusId))
2022-11-29 10:46:02 +00:00
}
public init(status: Status) {
_viewModel = StateObject(wrappedValue: .init(status: status))
}
public init(remoteStatusURL: URL) {
_viewModel = StateObject(wrappedValue: .init(remoteStatusURL: remoteStatusURL))
}
2022-11-29 10:46:02 +00:00
public var body: some View {
GeometryReader { reader in
ScrollViewReader { proxy in
List {
if isLoaded {
topPaddingView
}
switch viewModel.state {
case .loading:
loadingDetailView
2023-02-11 19:20:25 +00:00
case let .display(status, context, date):
if !context.ancestors.isEmpty {
ForEach(context.ancestors) { ancestor in
StatusRowView(viewModel: .init(status: ancestor, isCompact: false))
2023-01-07 17:01:06 +00:00
}
}
makeCurrentStatusView(status: status)
2023-02-11 19:20:25 +00:00
.id(date)
if !context.descendants.isEmpty {
ForEach(context.descendants) { descendant in
StatusRowView(viewModel: .init(status: descendant, isCompact: false))
}
}
if !isLoaded {
loadingContextView
}
Rectangle()
.foregroundColor(theme.secondaryBackgroundColor)
.frame(minHeight: reader.frame(in: .local).size.height - statusHeight)
.listRowSeparator(.hidden)
.listRowBackground(theme.secondaryBackgroundColor)
.listRowInsets(.init())
case .error:
errorView
2022-12-21 17:28:21 +00:00
}
}
.environment(\.defaultMinListRowHeight, 1)
.listStyle(.plain)
.scrollContentBackground(.hidden)
.background(theme.primaryBackgroundColor)
.onChange(of: viewModel.scrollToId, perform: { scrollToId in
if let scrollToId {
viewModel.scrollToId = nil
proxy.scrollTo(scrollToId, anchor: .top)
2023-01-12 17:25:37 +00:00
}
})
.task {
guard !isLoaded else { return }
viewModel.client = client
let result = await viewModel.fetch()
isLoaded = true
if !result {
if let url = viewModel.remoteStatusURL {
openURL(url)
}
DispatchQueue.main.async {
_ = routerPath.path.popLast()
}
2023-01-12 17:25:37 +00:00
}
}
2022-12-21 17:28:21 +00:00
}
2022-12-25 16:39:23 +00:00
.onChange(of: watcher.latestEvent?.id) { _ in
guard let lastEvent = watcher.latestEvent else { return }
viewModel.handleEvent(event: lastEvent, currentAccount: currentAccount.account)
}
2022-12-21 17:28:21 +00:00
}
.navigationTitle(viewModel.title)
.navigationBarTitleDisplayMode(.inline)
2022-11-29 10:46:02 +00:00
}
private func makeCurrentStatusView(status: Status) -> some View {
StatusRowView(viewModel: .init(status: status,
isCompact: false,
isFocused: true))
.overlay {
GeometryReader { reader in
VStack{}
.onAppear {
statusHeight = reader.size.height
}
}
}
.id(status.id)
}
private var errorView: some View {
ErrorView(title: "status.error.title",
message: "status.error.message",
buttonTitle: "action.retry") {
Task {
await viewModel.fetch()
}
}
.listRowBackground(theme.primaryBackgroundColor)
.listRowSeparator(.hidden)
}
private var loadingDetailView: some View {
ForEach(Status.placeholders()) { status in
StatusRowView(viewModel: .init(status: status, isCompact: false))
.redacted(reason: .placeholder)
}
}
private var loadingContextView: some View {
HStack {
Spacer()
ProgressView()
Spacer()
}
.frame(height: 50)
.listRowSeparator(.hidden)
.listRowBackground(theme.secondaryBackgroundColor)
.listRowInsets(.init())
}
private var topPaddingView: some View {
HStack { EmptyView() }
.listRowBackground(theme.primaryBackgroundColor)
.listRowSeparator(.hidden)
.listRowInsets(.init())
.frame(height: .layoutPadding)
}
2022-11-29 10:46:02 +00:00
}