146 lines
4.8 KiB
Swift
146 lines
4.8 KiB
Swift
import SwiftUI
|
|
|
|
struct AddBookmarkView: View {
|
|
@Bindable var viewModel: BookmarksViewModel
|
|
@Environment(\.dismiss) private var dismiss
|
|
|
|
@State private var url = ""
|
|
@State private var title = ""
|
|
@State private var description = ""
|
|
@State private var tagsText = ""
|
|
@State private var importText = ""
|
|
@State private var isSaving = false
|
|
@State private var error: String?
|
|
@State private var importMessage: String?
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
Form {
|
|
Section {
|
|
TextEditor(text: $importText)
|
|
.frame(minHeight: 96)
|
|
.textInputAutocapitalization(.never)
|
|
.autocorrectionDisabled()
|
|
|
|
HStack {
|
|
Button {
|
|
extractImportText()
|
|
} label: {
|
|
Label("Extract Link", systemImage: "link.badge.plus")
|
|
}
|
|
.disabled(importText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
|
|
|
|
Spacer()
|
|
|
|
if let importMessage {
|
|
Text(importMessage)
|
|
.font(.caption)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
}
|
|
} header: {
|
|
Text("Import Text")
|
|
} footer: {
|
|
Text("Paste an email, newsletter, or message and Marks will pull out the first web link.")
|
|
}
|
|
|
|
Section {
|
|
TextField("https://", text: $url)
|
|
.keyboardType(.URL)
|
|
.textInputAutocapitalization(.never)
|
|
.autocorrectionDisabled()
|
|
} header: {
|
|
Text("URL")
|
|
}
|
|
|
|
Section {
|
|
TextField("Optional", text: $title)
|
|
} header: {
|
|
Text("Title")
|
|
}
|
|
|
|
Section {
|
|
TextField("Optional", text: $description, axis: .vertical)
|
|
.lineLimit(2...5)
|
|
} header: {
|
|
Text("Notes")
|
|
}
|
|
|
|
Section {
|
|
TextField("comma separated", text: $tagsText)
|
|
.textInputAutocapitalization(.never)
|
|
.autocorrectionDisabled()
|
|
} header: {
|
|
Text("Tags")
|
|
} footer: {
|
|
Text("Separate tags with commas")
|
|
}
|
|
|
|
if let error {
|
|
Section {
|
|
Text(error)
|
|
.foregroundStyle(.red)
|
|
.font(.footnote)
|
|
}
|
|
}
|
|
}
|
|
.navigationTitle("Add Bookmark")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar {
|
|
ToolbarItem(placement: .cancellationAction) {
|
|
Button("Cancel") { dismiss() }
|
|
}
|
|
ToolbarItem(placement: .confirmationAction) {
|
|
Button("Save") { save() }
|
|
.disabled(url.trimmingCharacters(in: .whitespaces).isEmpty || isSaving)
|
|
.overlay {
|
|
if isSaving { ProgressView().scaleEffect(0.7) }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private func extractImportText() {
|
|
guard let payload = IngestPayloadParser.parse(item: importText, typeIdentifier: "public.plain-text") else {
|
|
importMessage = "No link found"
|
|
return
|
|
}
|
|
|
|
url = payload.url
|
|
if let extractedTitle = payload.title, title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
title = extractedTitle
|
|
}
|
|
if description.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
description = payload.notes
|
|
}
|
|
importMessage = "Link extracted"
|
|
error = nil
|
|
}
|
|
|
|
private func save() {
|
|
let urlString = url.normalizedURL
|
|
guard URL(string: urlString) != nil else {
|
|
error = "Invalid URL"
|
|
return
|
|
}
|
|
let tags = tagsText.parsedTags
|
|
isSaving = true
|
|
error = nil
|
|
Task {
|
|
do {
|
|
try await viewModel.addBookmark(
|
|
url: urlString,
|
|
title: title.trimmingCharacters(in: .whitespaces),
|
|
description: description.trimmingCharacters(in: .whitespacesAndNewlines),
|
|
tags: tags
|
|
)
|
|
dismiss()
|
|
} catch {
|
|
self.error = error.localizedDescription
|
|
isSaving = false
|
|
}
|
|
}
|
|
}
|
|
}
|