Unit tests, Lokalisierung, ShareExtension
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user