Files
2026-04-28 22:55:10 +03:00

104 lines
3.4 KiB
Swift

import Foundation
import SwiftData
import ReadeckCore
@MainActor
public final class BookmarkSyncService {
private let client: ReadeckClientProtocol
private let context: ModelContext
public init(client: ReadeckClientProtocol, context: ModelContext) {
self.client = client
self.context = context
}
public func refresh(query: BookmarkQuery = BookmarkQuery()) async throws {
let dtos = try await client.fetchBookmarks(query)
try merge(dtos)
}
public func ensureArticleLoaded(for bookmark: Bookmark, force: Bool = false) async throws {
if !force, let html = bookmark.articleHTML, !html.isEmpty {
return
}
let html = try await client.fetchArticleHTML(id: bookmark.id)
bookmark.articleHTML = html
bookmark.articleFetchedAt = .now
try context.save()
}
public func toggleArchived(_ bookmark: Bookmark) async throws {
let updated = try await client.updateBookmark(
id: bookmark.id,
patch: BookmarkPatch(isArchived: !bookmark.isArchived)
)
apply(updated, to: bookmark)
try context.save()
}
public func toggleMarked(_ bookmark: Bookmark) async throws {
let updated = try await client.updateBookmark(
id: bookmark.id,
patch: BookmarkPatch(isMarked: !bookmark.isMarked)
)
apply(updated, to: bookmark)
try context.save()
}
public func delete(_ bookmark: Bookmark) async throws {
try await client.deleteBookmark(id: bookmark.id)
context.delete(bookmark)
try context.save()
}
private func merge(_ dtos: [BookmarkDTO]) throws {
let ids = dtos.map(\.id)
let descriptor = FetchDescriptor<Bookmark>(predicate: #Predicate { ids.contains($0.id) })
let existing = try context.fetch(descriptor)
let existingByID = Dictionary(uniqueKeysWithValues: existing.map { ($0.id, $0) })
for dto in dtos {
if let bookmark = existingByID[dto.id] {
apply(dto, to: bookmark)
} else {
context.insert(makeBookmark(from: dto))
}
}
try context.save()
}
private func makeBookmark(from dto: BookmarkDTO) -> Bookmark {
Bookmark(
id: dto.id,
url: dto.url,
title: dto.title,
siteName: dto.siteName,
author: dto.authors?.joined(separator: ", "),
summary: dto.description,
imageURL: dto.imageURL,
createdAt: dto.created,
updatedAt: dto.updated,
isArchived: dto.isArchived,
isMarked: dto.isMarked,
readProgress: dto.readProgress,
wordCount: dto.wordCount,
readingTime: dto.readingTime
)
}
private func apply(_ dto: BookmarkDTO, to bookmark: Bookmark) {
bookmark.url = dto.url
bookmark.title = dto.title
bookmark.siteName = dto.siteName
bookmark.author = dto.authors?.joined(separator: ", ")
bookmark.summary = dto.description
bookmark.imageURL = dto.imageURL
bookmark.updatedAt = dto.updated
bookmark.isArchived = dto.isArchived
bookmark.isMarked = dto.isMarked
bookmark.readProgress = dto.readProgress
bookmark.wordCount = dto.wordCount
bookmark.readingTime = dto.readingTime
}
}