Files
linkding-ios/Marks/Views/AddBookmarkView.swift
2026-07-02 01:11:03 -05:00

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
}
}
}
}