Files

187 lines
5.6 KiB
Swift

import SwiftUI
struct ShareExtensionView: View {
@ObservedObject var viewModel: ShareViewModel
var onCancel: () -> Void
var onComplete: () -> Void
var onOpenURL: (URL) -> Void
// MARK: - Body
var body: some View {
NavigationStack {
Group {
if !viewModel.isConfigured {
notConfiguredView
} else if viewModel.isSaved {
successView
} else {
formView
}
}
.navigationTitle("Save to BookStax")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancel", action: onCancel)
}
}
}
.task {
guard viewModel.isConfigured, !viewModel.isSaved else { return }
await viewModel.loadShelves()
}
.alert(
"Error",
isPresented: Binding(
get: { viewModel.errorMessage != nil },
set: { if !$0 { viewModel.errorMessage = nil } }
),
actions: {
Button("OK") { viewModel.errorMessage = nil }
},
message: {
Text(viewModel.errorMessage ?? "")
}
)
}
// MARK: - Not configured
private var notConfiguredView: some View {
VStack(spacing: 20) {
Image(systemName: "exclamationmark.triangle.fill")
.font(.system(size: 48))
.foregroundStyle(.orange)
Text("BookStax Not Configured")
.font(.headline)
Text("Please open BookStax and sign in to your BookStack server.")
.multilineTextAlignment(.center)
.foregroundStyle(.secondary)
.padding(.horizontal)
Button("Close", action: onCancel)
.buttonStyle(.borderedProminent)
}
.padding()
}
// MARK: - Success
private var successView: some View {
VStack(spacing: 24) {
Image(systemName: "checkmark.circle.fill")
.font(.system(size: 64))
.foregroundStyle(.green)
VStack(spacing: 8) {
Text("Page saved!")
.font(.headline)
Text(viewModel.pageTitle)
.font(.subheadline)
.foregroundStyle(.secondary)
.multilineTextAlignment(.center)
}
VStack(spacing: 12) {
if let url = URL(string: viewModel.serverURL), !viewModel.serverURL.isEmpty {
Button {
onOpenURL(url)
onComplete()
} label: {
Label("Open BookStax", systemImage: "safari")
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
}
Button("Done", action: onComplete)
.buttonStyle(.bordered)
}
.padding(.horizontal)
}
.padding()
.task {
try? await Task.sleep(for: .milliseconds(1500))
onComplete()
}
}
// MARK: - Form
private var formView: some View {
Form {
Section("Selected Text") {
Text(viewModel.sharedText)
.lineLimit(4)
.font(.footnote)
.foregroundStyle(.secondary)
}
Section("Page Title") {
TextField("Page title", text: $viewModel.pageTitle)
.autocorrectionDisabled()
}
Section("Location") {
NavigationLink {
ShelfPickerView(viewModel: viewModel)
} label: {
LabeledRow(label: "Shelf", value: viewModel.selectedShelf?.name)
}
NavigationLink {
BookPickerView(viewModel: viewModel)
} label: {
LabeledRow(label: "Book", value: viewModel.selectedBook?.name)
}
.disabled(viewModel.selectedShelf == nil)
NavigationLink {
ChapterPickerView(viewModel: viewModel)
} label: {
LabeledRow(label: "Chapter", value: viewModel.selectedChapter?.name,
placeholder: "Optional")
}
.disabled(viewModel.selectedBook == nil)
}
Section {
Button {
Task { await viewModel.savePage() }
} label: {
HStack {
Spacer()
if viewModel.isLoading {
ProgressView()
} else {
Text("Save")
.fontWeight(.semibold)
}
Spacer()
}
}
.disabled(viewModel.isSaveDisabled)
}
}
}
}
// MARK: - Helper
private struct LabeledRow: View {
let label: String
let value: String?
var placeholder: String = "Select"
var body: some View {
HStack {
Text(LocalizedStringKey(label))
Spacer()
Text(value.map { LocalizedStringKey($0) } ?? LocalizedStringKey(placeholder))
.foregroundStyle(value == nil ? .secondary : .primary)
}
}
}