Files
linkding-ios/Marks/Models/ServerConfig.swift
Krishna Kumar e82e69c5f3
All checks were successful
CI / build-and-deploy (push) Successful in 20s
feat(share): dup-aware interactive save card with AI tag suggestions
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>
2026-06-23 20:36:46 -05:00

46 lines
1.5 KiB
Swift

import Foundation
struct ServerConfig: Codable, Sendable {
var host: String
var port: Int?
var path: String
var token: String
var useHttps: Bool
var baseUrl: String {
var url = useHttps ? "https://" : "http://"
url += host
if let port { url += ":\(port)" }
if !path.isEmpty { url += path }
return url
}
}
extension ServerConfig {
private static let key = "serverConfig"
private static let appGroupId = "group.com.magicive.marks"
static func load() -> ServerConfig? {
guard let data = UserDefaults.standard.data(forKey: key) else { return nil }
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)
UserDefaults(suiteName: ServerConfig.appGroupId)?.set(data, forKey: ServerConfig.key)
}
func delete() {
UserDefaults.standard.removeObject(forKey: ServerConfig.key)
UserDefaults(suiteName: ServerConfig.appGroupId)?.removeObject(forKey: ServerConfig.key)
}
}