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>
46 lines
1.5 KiB
Swift
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)
|
|
}
|
|
}
|