- Add new logo and splash screen assets with multiple resolutions - Implement paginated label selection with TabView - Create UnifiedLabelChip component for consistent label display - Add manual tag entry functionality with validation - Refactor BookmarkLabelsViewModel with dependency injection - Update launch screen configuration and color sets - Add new localization strings for improved UX - Improve ShareBookmarkView with better label selection UI
81 lines
3.1 KiB
Swift
81 lines
3.1 KiB
Swift
import Foundation
|
|
import SwiftUI
|
|
import UniformTypeIdentifiers
|
|
|
|
class ShareBookmarkViewModel: ObservableObject {
|
|
@Published var url: String?
|
|
@Published var title: String = ""
|
|
@Published var labels: [BookmarkLabelDto] = []
|
|
@Published var selectedLabels: Set<String> = []
|
|
@Published var statusMessage: (text: String, isError: Bool, emoji: String)? = nil
|
|
@Published var isSaving: Bool = false
|
|
private weak var extensionContext: NSExtensionContext?
|
|
|
|
init(extensionContext: NSExtensionContext?) {
|
|
self.extensionContext = extensionContext
|
|
extractSharedContent()
|
|
}
|
|
|
|
func onAppear() {
|
|
loadLabels()
|
|
}
|
|
|
|
private func extractSharedContent() {
|
|
guard let extensionContext = extensionContext else { return }
|
|
for item in extensionContext.inputItems {
|
|
guard let inputItem = item as? NSExtensionItem else { continue }
|
|
for attachment in inputItem.attachments ?? [] {
|
|
if attachment.hasItemConformingToTypeIdentifier(UTType.url.identifier) {
|
|
attachment.loadItem(forTypeIdentifier: UTType.url.identifier, options: nil) { [weak self] (url, error) in
|
|
DispatchQueue.main.async {
|
|
if let url = url as? URL {
|
|
self?.url = url.absoluteString
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if attachment.hasItemConformingToTypeIdentifier(UTType.plainText.identifier) {
|
|
attachment.loadItem(forTypeIdentifier: UTType.plainText.identifier, options: nil) { [weak self] (text, error) in
|
|
DispatchQueue.main.async {
|
|
if let text = text as? String, let url = URL(string: text) {
|
|
self?.url = url.absoluteString
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func loadLabels() {
|
|
Task {
|
|
let loaded = await SimpleAPI.getBookmarkLabels { [weak self] message, error in
|
|
self?.statusMessage = (message, error, error ? "❌" : "✅")
|
|
} ?? []
|
|
let sorted = loaded.sorted { $0.count > $1.count }
|
|
await MainActor.run {
|
|
self.labels = Array(sorted)
|
|
}
|
|
}
|
|
}
|
|
|
|
func save() {
|
|
guard let url = url, !url.isEmpty else {
|
|
statusMessage = ("No URL found.", true, "❌")
|
|
return
|
|
}
|
|
isSaving = true
|
|
Task {
|
|
await SimpleAPI.addBookmark(title: title, url: url, labels: Array(selectedLabels)) { [weak self] message, error in
|
|
self?.statusMessage = (message, error, error ? "❌" : "✅")
|
|
self?.isSaving = false
|
|
if !error {
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
|
self?.extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|