mirror of
https://github.com/warmbly/warmbly.git
synced 2026-09-06 08:01:24 +00:00
86 lines
3.2 KiB
Swift
86 lines
3.2 KiB
Swift
import SwiftUI
|
|
|
|
/// Autosaved compose drafts (`GET /v1/unibox/drafts`): tap to resume a draft
|
|
/// in the composer, swipe to delete. Pushed from the unibox drawer.
|
|
struct ComposeDraftsView: View {
|
|
@Environment(AppEnvironment.self) private var env
|
|
|
|
/// Opens the tapped draft in the compose sheet (owned by the unibox root).
|
|
var onOpen: (ComposeDraft) -> Void
|
|
|
|
@State private var store = ComposeDraftsStore()
|
|
|
|
var body: some View {
|
|
Group {
|
|
if store.isLoading, !store.hasLoaded {
|
|
ProgressView()
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
} else if let error = store.errorMessage {
|
|
EmptyStateView(title: "Couldn't load drafts", message: error)
|
|
} else if store.drafts.isEmpty {
|
|
EmptyStateView(
|
|
title: "No drafts",
|
|
message: "Emails you start composing are saved here automatically."
|
|
)
|
|
} else {
|
|
List {
|
|
ForEach(store.drafts) { draft in
|
|
Button {
|
|
onOpen(draft)
|
|
} label: {
|
|
row(draft)
|
|
}
|
|
.swipeActions(edge: .trailing) {
|
|
Button(role: .destructive) {
|
|
Task { await store.delete(env.api, id: draft.id) }
|
|
} label: {
|
|
Label("Delete", systemImage: "trash")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.listStyle(.plain)
|
|
}
|
|
}
|
|
.navigationTitle("Drafts")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.task { await store.load(env.api) }
|
|
.refreshable { await store.load(env.api) }
|
|
}
|
|
|
|
private func row(_ draft: ComposeDraft) -> some View {
|
|
VStack(alignment: .leading, spacing: 3) {
|
|
HStack(spacing: 8) {
|
|
Text(recipientsLine(draft))
|
|
.font(.body.weight(.medium))
|
|
.foregroundStyle(.primary)
|
|
.lineLimit(1)
|
|
Spacer(minLength: 8)
|
|
Text(UniboxFormat.listTime(draft.updatedAt))
|
|
.font(.caption)
|
|
.monospacedDigit()
|
|
.foregroundStyle(.tertiary)
|
|
}
|
|
if let subject = draft.subject, !subject.isEmpty {
|
|
Text(subject)
|
|
.font(.footnote.weight(.medium))
|
|
.foregroundStyle(.secondary)
|
|
.lineLimit(1)
|
|
}
|
|
if let snippet = draft.body?.trimmingCharacters(in: .whitespacesAndNewlines), !snippet.isEmpty {
|
|
Text(snippet)
|
|
.font(.footnote)
|
|
.foregroundStyle(.tertiary)
|
|
.lineLimit(2)
|
|
}
|
|
}
|
|
.padding(.vertical, 3)
|
|
}
|
|
|
|
private func recipientsLine(_ draft: ComposeDraft) -> String {
|
|
let recipients = (draft.to ?? []).map(UniboxAddress.display)
|
|
guard !recipients.isEmpty else { return "No recipient" }
|
|
return recipients.joined(separator: ", ")
|
|
}
|
|
}
|