mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-26 02:01:02 +00:00
Status detail + context
This commit is contained in:
parent
c2a2fe1f86
commit
07188a6818
5 changed files with 108 additions and 4 deletions
6
Packages/Models/Sources/Models/StatusContext.swift
Normal file
6
Packages/Models/Sources/Models/StatusContext.swift
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public struct StatusContext: Decodable {
|
||||||
|
public let ancestors: [Status]
|
||||||
|
public let descendants: [Status]
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public enum Statuses: Endpoint {
|
public enum Statuses: Endpoint {
|
||||||
|
case status(id: String)
|
||||||
|
case context(id: String)
|
||||||
case favourite(id: String)
|
case favourite(id: String)
|
||||||
case unfavourite(id: String)
|
case unfavourite(id: String)
|
||||||
case reblog(id: String)
|
case reblog(id: String)
|
||||||
|
@ -8,6 +10,10 @@ public enum Statuses: Endpoint {
|
||||||
|
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
|
case .status(let id):
|
||||||
|
return "statuses/\(id)"
|
||||||
|
case .context(let id):
|
||||||
|
return "statuses/\(id)/context"
|
||||||
case .favourite(let id):
|
case .favourite(let id):
|
||||||
return "statuses/\(id)/favourite"
|
return "statuses/\(id)/favourite"
|
||||||
case .unfavourite(let id):
|
case .unfavourite(let id):
|
||||||
|
|
|
@ -24,6 +24,7 @@ public struct NotificationsListView: View {
|
||||||
.padding(.top, DS.Constants.layoutPadding)
|
.padding(.top, DS.Constants.layoutPadding)
|
||||||
}
|
}
|
||||||
.task {
|
.task {
|
||||||
|
viewModel.client = client
|
||||||
await viewModel.fetchNotifications()
|
await viewModel.fetchNotifications()
|
||||||
}
|
}
|
||||||
.refreshable {
|
.refreshable {
|
||||||
|
|
|
@ -1,13 +1,68 @@
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import Models
|
||||||
|
import Shimmer
|
||||||
|
import Routeur
|
||||||
|
import Network
|
||||||
|
import DesignSystem
|
||||||
|
|
||||||
public struct StatusDetailView: View {
|
public struct StatusDetailView: View {
|
||||||
private let statusId: String
|
@EnvironmentObject private var client: Client
|
||||||
|
@EnvironmentObject private var routeurPath: RouterPath
|
||||||
|
@StateObject private var viewModel: StatusDetailViewModel
|
||||||
|
@State private var isLoaded: Bool = false
|
||||||
|
|
||||||
public init(statusId: String) {
|
public init(statusId: String) {
|
||||||
self.statusId = statusId
|
_viewModel = StateObject(wrappedValue: .init(statusId: statusId))
|
||||||
}
|
}
|
||||||
|
|
||||||
public var body: some View {
|
public var body: some View {
|
||||||
Text("Status id \(statusId)")
|
ScrollViewReader { proxy in
|
||||||
|
ScrollView {
|
||||||
|
LazyVStack {
|
||||||
|
switch viewModel.state {
|
||||||
|
case .loading:
|
||||||
|
ForEach(Status.placeholders()) { status in
|
||||||
|
StatusRowView(viewModel: .init(status: status, isEmbed: false))
|
||||||
|
.redacted(reason: .placeholder)
|
||||||
|
.shimmering()
|
||||||
|
}
|
||||||
|
case let.display(status, context):
|
||||||
|
if !context.ancestors.isEmpty {
|
||||||
|
ForEach(context.ancestors) { ancestor in
|
||||||
|
StatusRowView(viewModel: .init(status: ancestor, isEmbed: false))
|
||||||
|
Divider()
|
||||||
|
.padding(.vertical, DS.Constants.dividerPadding)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StatusRowView(viewModel: .init(status: status, isEmbed: false))
|
||||||
|
.id(status.id)
|
||||||
|
Divider()
|
||||||
|
.padding(.vertical, DS.Constants.dividerPadding)
|
||||||
|
if !context.descendants.isEmpty {
|
||||||
|
ForEach(context.descendants) { descendant in
|
||||||
|
StatusRowView(viewModel: .init(status: descendant, isEmbed: false))
|
||||||
|
Divider()
|
||||||
|
.padding(.vertical, DS.Constants.dividerPadding)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case let .error(error):
|
||||||
|
Text(error.localizedDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal, DS.Constants.layoutPadding)
|
||||||
|
}
|
||||||
|
.task {
|
||||||
|
guard !isLoaded else { return }
|
||||||
|
isLoaded = true
|
||||||
|
viewModel.client = client
|
||||||
|
await viewModel.fetchStatusDetail()
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
proxy.scrollTo(viewModel.statusId, anchor: .center)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle(viewModel.title)
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
import Models
|
||||||
|
import Network
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
class StatusDetailViewModel: ObservableObject {
|
||||||
|
public let statusId: String
|
||||||
|
|
||||||
|
var client: Client?
|
||||||
|
|
||||||
|
enum State {
|
||||||
|
case loading, display(status: Status, context: StatusContext), error(error: Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Published var state: State = .loading
|
||||||
|
@Published var title: String = ""
|
||||||
|
|
||||||
|
init(statusId: String) {
|
||||||
|
state = .loading
|
||||||
|
self.statusId = statusId
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchStatusDetail() async {
|
||||||
|
guard let client else { return }
|
||||||
|
do {
|
||||||
|
state = .loading
|
||||||
|
let status: Status = try await client.get(endpoint: Statuses.status(id: statusId))
|
||||||
|
let context: StatusContext = try await client.get(endpoint: Statuses.context(id: statusId))
|
||||||
|
state = .display(status: status, context: context)
|
||||||
|
title = "Post from \(status.account.displayName)"
|
||||||
|
} catch {
|
||||||
|
state = .error(error: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue