187 lines
5.6 KiB
Swift
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)
|
|
}
|
|
}
|
|
}
|