104 lines
3.4 KiB
Swift
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
|
|
}
|
|
}
|