mirror of
https://github.com/wu736139669/hapi.git
synced 2026-10-09 19:29:41 +00:00
78 lines
3.3 KiB
Swift
78 lines
3.3 KiB
Swift
import HapiClient
|
|
import HapiProtocol
|
|
import HapiUI
|
|
import SwiftUI
|
|
|
|
/// One recent draft while browsing; just a header while typing.
|
|
struct ScratchlistDrawerView: View {
|
|
@State private var model: ScratchlistScreenModel
|
|
let interactor: ChatInteractor
|
|
let keyboardFocused: Bool
|
|
let onOpen: (ScratchlistEntry?, Bool) -> Void
|
|
@Environment(\.dynamicTypeSize) private var dynamicTypeSize
|
|
|
|
init(store: any SessionScratchlistStoring, sessionId: String, interactor: ChatInteractor,
|
|
keyboardFocused: Bool, onOpen: @escaping (ScratchlistEntry?, Bool) -> Void) {
|
|
_model = State(initialValue: ScratchlistScreenModel(sessionId: sessionId, store: store))
|
|
self.interactor = interactor
|
|
self.keyboardFocused = keyboardFocused
|
|
self.onOpen = onOpen
|
|
}
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 0) {
|
|
header
|
|
if model.state.loadFailed || model.state.refreshFailed {
|
|
ScratchlistErrorBanner(message: String(localized: model.state.loadFailed
|
|
? "Couldn't load the scratchlist" : "Couldn't refresh — showing saved drafts")) { model.retry() }
|
|
}
|
|
if !keyboardFocused, !dynamicTypeSize.isAccessibilitySize, let entry = model.state.entries.first {
|
|
ScratchlistEntryRow(entry: entry, interactor: interactor,
|
|
onOpen: { onOpen(entry, false) }, onEdit: { onOpen(entry, true) },
|
|
onDelete: { model.deleteEntry(entry.entryId) }, compact: true)
|
|
.padding(.bottom, 8)
|
|
.accessibilityIdentifier("scratchlist.recent")
|
|
}
|
|
Divider()
|
|
}
|
|
.padding(.horizontal, 12)
|
|
.padding(.top, 4)
|
|
.accessibilityIdentifier("scratchlist.drawer")
|
|
.onAppear { model.start() }
|
|
.onDisappear { model.stop() }
|
|
}
|
|
|
|
private var header: some View {
|
|
HStack(spacing: 0) {
|
|
Button { onOpen(nil, false) } label: {
|
|
HStack(spacing: 6) {
|
|
Text("Scratchlist").font(.subheadline.weight(.semibold))
|
|
if model.isLoading {
|
|
ProgressView().controlSize(.small)
|
|
} else if model.state.loaded {
|
|
Text(verbatim: "· \(model.state.entries.count)")
|
|
.font(.subheadline).foregroundStyle(.secondary)
|
|
}
|
|
Image(systemName: "chevron.right")
|
|
.font(.caption.weight(.semibold)).foregroundStyle(.secondary)
|
|
Spacer(minLength: 0)
|
|
}
|
|
.frame(minHeight: 44)
|
|
.contentShape(Rectangle())
|
|
}
|
|
.buttonStyle(.plain)
|
|
.accessibilityHint("View all")
|
|
.accessibilityIdentifier("scratchlist.open")
|
|
Button { interactor.setComposerDestination(.chat) } label: {
|
|
Image(systemName: "xmark")
|
|
.font(.system(size: 15, weight: .medium)).foregroundStyle(.secondary)
|
|
.frame(width: 44, height: 44).contentShape(Rectangle())
|
|
}
|
|
.buttonStyle(.plain)
|
|
.accessibilityLabel("Back to chat")
|
|
.accessibilityIdentifier("scratchlist.close")
|
|
.disabled(interactor.scratchlistBusy || interactor.isSending)
|
|
}
|
|
}
|
|
}
|