2023-01-17 10:36:01 +00:00
|
|
|
import DesignSystem
|
2023-02-04 16:17:38 +00:00
|
|
|
import Env
|
2022-12-19 06:17:01 +00:00
|
|
|
import Models
|
2023-02-18 06:26:48 +00:00
|
|
|
import Network
|
2022-12-19 06:17:01 +00:00
|
|
|
import Shimmer
|
2023-01-17 10:36:01 +00:00
|
|
|
import SwiftUI
|
2022-12-19 06:17:01 +00:00
|
|
|
|
2023-09-18 19:03:52 +00:00
|
|
|
@MainActor
|
2022-12-19 06:17:01 +00:00
|
|
|
public struct StatusesListView<Fetcher>: View where Fetcher: StatusesFetcher {
|
2023-09-18 19:03:52 +00:00
|
|
|
@Environment(Theme.self) private var theme
|
2023-03-13 12:38:28 +00:00
|
|
|
|
2023-09-18 05:01:23 +00:00
|
|
|
@State private var fetcher: Fetcher
|
2023-07-03 05:40:49 +00:00
|
|
|
// Whether this status is on a remote local timeline (many actions are unavailable if so)
|
2023-01-06 11:14:05 +00:00
|
|
|
private let isRemote: Bool
|
2023-02-15 07:46:14 +00:00
|
|
|
private let routerPath: RouterPath
|
|
|
|
private let client: Client
|
2023-03-13 12:38:28 +00:00
|
|
|
|
2023-03-01 18:27:56 +00:00
|
|
|
public init(fetcher: Fetcher,
|
|
|
|
client: Client,
|
|
|
|
routerPath: RouterPath,
|
2023-03-13 12:38:28 +00:00
|
|
|
isRemote: Bool = false)
|
|
|
|
{
|
2023-09-18 05:01:23 +00:00
|
|
|
_fetcher = .init(initialValue: fetcher)
|
2023-01-06 11:14:05 +00:00
|
|
|
self.isRemote = isRemote
|
2023-02-15 07:46:14 +00:00
|
|
|
self.client = client
|
|
|
|
self.routerPath = routerPath
|
2022-12-19 06:17:01 +00:00
|
|
|
}
|
2023-03-13 12:38:28 +00:00
|
|
|
|
2022-12-19 06:17:01 +00:00
|
|
|
public var body: some View {
|
2023-01-30 20:41:42 +00:00
|
|
|
switch fetcher.statusesState {
|
|
|
|
case .loading:
|
|
|
|
ForEach(Status.placeholders()) { status in
|
2023-09-18 05:01:23 +00:00
|
|
|
StatusRowView(viewModel: .init(status: status, client: client, routerPath: routerPath))
|
2023-01-30 20:41:42 +00:00
|
|
|
.redacted(reason: .placeholder)
|
2023-09-18 16:55:11 +00:00
|
|
|
.allowsHitTesting(false)
|
2023-01-30 20:41:42 +00:00
|
|
|
}
|
|
|
|
case .error:
|
|
|
|
ErrorView(title: "status.error.title",
|
|
|
|
message: "status.error.loading.message",
|
2023-03-13 12:38:28 +00:00
|
|
|
buttonTitle: "action.retry")
|
|
|
|
{
|
2023-01-30 20:41:42 +00:00
|
|
|
Task {
|
2024-01-04 11:56:46 +00:00
|
|
|
await fetcher.fetchNewestStatuses(pullToRefresh: false)
|
2023-01-07 17:01:06 +00:00
|
|
|
}
|
2023-01-30 20:41:42 +00:00
|
|
|
}
|
2023-03-13 12:38:28 +00:00
|
|
|
.listRowBackground(theme.primaryBackgroundColor)
|
|
|
|
.listRowSeparator(.hidden)
|
|
|
|
|
2023-01-30 20:41:42 +00:00
|
|
|
case let .display(statuses, nextPageState):
|
2024-01-02 13:06:53 +00:00
|
|
|
ForEach(statuses, id: \.id) { status in
|
2024-01-01 20:29:03 +00:00
|
|
|
StatusRowView(viewModel: StatusRowViewModel(status: status,
|
|
|
|
client: client,
|
|
|
|
routerPath: routerPath,
|
|
|
|
isRemote: isRemote))
|
|
|
|
.onAppear {
|
|
|
|
fetcher.statusDidAppear(status: status)
|
|
|
|
}
|
|
|
|
.onDisappear {
|
|
|
|
fetcher.statusDidDisappear(status: status)
|
|
|
|
}
|
2023-01-30 20:41:42 +00:00
|
|
|
}
|
|
|
|
switch nextPageState {
|
|
|
|
case .hasNextPage:
|
|
|
|
loadingRow
|
2024-01-08 17:22:10 +00:00
|
|
|
.id(UUID().uuidString)
|
2023-01-30 20:41:42 +00:00
|
|
|
.onAppear {
|
|
|
|
Task {
|
|
|
|
await fetcher.fetchNextPage()
|
2022-12-19 06:17:01 +00:00
|
|
|
}
|
2023-01-30 20:41:42 +00:00
|
|
|
}
|
|
|
|
case .loadingNextPage:
|
|
|
|
loadingRow
|
2024-01-08 17:22:10 +00:00
|
|
|
.id(UUID().uuidString)
|
2023-01-30 20:41:42 +00:00
|
|
|
case .none:
|
|
|
|
EmptyView()
|
2022-12-19 06:17:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-03-13 12:38:28 +00:00
|
|
|
|
2022-12-19 06:17:01 +00:00
|
|
|
private var loadingRow: some View {
|
|
|
|
HStack {
|
|
|
|
Spacer()
|
|
|
|
ProgressView()
|
|
|
|
Spacer()
|
|
|
|
}
|
2023-01-03 17:22:08 +00:00
|
|
|
.padding(.horizontal, .layoutPadding)
|
2023-01-30 20:41:42 +00:00
|
|
|
.listRowBackground(theme.primaryBackgroundColor)
|
2022-12-19 06:17:01 +00:00
|
|
|
}
|
|
|
|
}
|