feat(share): dup-aware interactive save card with AI tag suggestions
All checks were successful
CI / build-and-deploy (push) Successful in 20s
All checks were successful
CI / build-and-deploy (push) Successful in 20s
Replace the fire-and-forget share toast with an interactive SwiftUI card. On open it checks linkding's /api/bookmarks/check/ so an already-saved URL routes to an Update (no more silent duplicates) and a new URL shows a save form with title, tags, notes, and a read-later toggle. Tag suggestions are hybrid: fetch the user's existing tag vocabulary (/api/tags/) and AI tag ideas (/v1/marks/enrich via anonymous device auth), auto-fill known-vocabulary matches into the field and surface net-new ideas as tappable chips. Best-effort and non-blocking — failures never block save. Also: - Refresh bookmarks when the app returns to the foreground (scenePhase). - Render bookmark dates as a static relative label instead of the live ticking RelativeDateTime timer. - Share LinkdingAPI/Bookmark/ServerConfig/MarksAuth into the ShareExtension target via project.yml (durable across xcodegen regen) and regenerate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -61,6 +61,24 @@ struct BookmarkResponse: Codable, Sendable {
|
||||
let results: [Bookmark]
|
||||
}
|
||||
|
||||
/// Response of linkding's `GET /api/bookmarks/check/?url=` — tells us whether the
|
||||
/// URL is already saved, plus scraped metadata and suggested tags for new saves.
|
||||
struct BookmarkCheck: Codable, Sendable {
|
||||
let bookmark: Bookmark?
|
||||
let metadata: Metadata?
|
||||
let autoTags: [String]?
|
||||
|
||||
struct Metadata: Codable, Sendable {
|
||||
let title: String?
|
||||
let description: String?
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case bookmark, metadata
|
||||
case autoTags = "auto_tags"
|
||||
}
|
||||
}
|
||||
|
||||
struct BookmarkCreate: Codable, Sendable {
|
||||
let url: String
|
||||
let title: String
|
||||
@@ -90,6 +108,14 @@ struct BookmarkUpdate: Codable, Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
struct LinkdingTag: Codable, Sendable {
|
||||
let name: String
|
||||
}
|
||||
|
||||
struct TagResponse: Codable, Sendable {
|
||||
let results: [LinkdingTag]
|
||||
}
|
||||
|
||||
struct SmartCollection: Identifiable, Sendable {
|
||||
let id = UUID()
|
||||
let name: String
|
||||
|
||||
@@ -25,6 +25,13 @@ extension ServerConfig {
|
||||
return try? JSONDecoder().decode(ServerConfig.self, from: data)
|
||||
}
|
||||
|
||||
/// Loads config from the shared app group — used by extensions, which have no
|
||||
/// access to the main app's `UserDefaults.standard`.
|
||||
static func loadShared() -> ServerConfig? {
|
||||
guard let data = UserDefaults(suiteName: appGroupId)?.data(forKey: key) else { return nil }
|
||||
return try? JSONDecoder().decode(ServerConfig.self, from: data)
|
||||
}
|
||||
|
||||
func save() {
|
||||
guard let data = try? JSONEncoder().encode(self) else { return }
|
||||
UserDefaults.standard.set(data, forKey: ServerConfig.key)
|
||||
|
||||
Reference in New Issue
Block a user