Impressia/Vernissage/Services/TimelineService.swift

117 lines
4.7 KiB
Swift
Raw Normal View History

2023-01-03 14:09:22 +01:00
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the MIT License.
//
import Foundation
import CoreData
import MastodonSwift
public class TimelineService {
public static let shared = TimelineService()
2023-01-05 11:55:20 +01:00
private init() { }
2023-01-03 14:09:22 +01:00
public func onBottomOfList(for accountData: AccountData) async throws {
// Load data from API and operate on CoreData on background context.
let backgroundContext = CoreDataHandler.shared.newBackgroundContext()
// Get maximimum downloaded stauts id.
2023-01-05 11:55:20 +01:00
let oldestStatus = StatusDataHandler.shared.getMinimumtatus(viewContext: backgroundContext)
2023-01-03 14:09:22 +01:00
guard let oldestStatus = oldestStatus else {
return
}
try await self.loadData(for: accountData, on: backgroundContext, maxId: oldestStatus.id)
}
public func onTopOfList(for accountData: AccountData) async throws {
// Load data from API and operate on CoreData on background context.
let backgroundContext = CoreDataHandler.shared.newBackgroundContext()
// Get maximimum downloaded stauts id.
2023-01-05 11:55:20 +01:00
let newestStatus = StatusDataHandler.shared.getMaximumStatus(viewContext: backgroundContext)
2023-01-03 20:42:20 +01:00
try await self.loadData(for: accountData, on: backgroundContext, minId: newestStatus?.id)
2023-01-03 14:09:22 +01:00
}
2023-01-05 11:55:20 +01:00
public func getStatus(withId statusId: String, and accountData: AccountData?) async throws -> Status? {
guard let accessToken = accountData?.accessToken, let serverUrl = accountData?.serverUrl else {
2023-01-04 17:56:01 +01:00
return nil
}
2023-01-05 11:55:20 +01:00
let client = MastodonClient(baseURL: serverUrl).getAuthenticated(token: accessToken)
2023-01-04 17:56:01 +01:00
return try await client.read(statusId: statusId)
}
2023-01-03 14:09:22 +01:00
public func getComments(for statusId: String, and accountData: AccountData) async throws -> Context {
let client = MastodonClient(baseURL: accountData.serverUrl).getAuthenticated(token: accountData.accessToken ?? "")
return try await client.getContext(for: statusId)
}
private func loadData(for accountData: AccountData, on backgroundContext: NSManagedObjectContext, minId: String? = nil, maxId: String? = nil) async throws {
guard let accessToken = accountData.accessToken else {
return
}
2023-01-04 17:56:01 +01:00
2023-01-03 14:09:22 +01:00
// Retrieve statuses from API.
let client = MastodonClient(baseURL: accountData.serverUrl).getAuthenticated(token: accessToken)
let statuses = try await client.getHomeTimeline(maxId: maxId, minId: minId, limit: 40)
2023-01-05 11:55:20 +01:00
2023-01-04 17:56:01 +01:00
// Save status data in database.
2023-01-03 14:09:22 +01:00
for status in statuses {
2023-01-05 11:55:20 +01:00
let statusData = StatusDataHandler.shared.createStatusDataEntity(viewContext: backgroundContext)
try await self.copy(from: status, to: statusData, on: backgroundContext)
2023-01-04 17:56:01 +01:00
}
try backgroundContext.save()
}
2023-01-05 11:55:20 +01:00
public func updateStatus(_ statusData: StatusData, basedOn status: Status) async throws -> StatusData? {
// Load data from API and operate on CoreData on background context.
let backgroundContext = CoreDataHandler.shared.newBackgroundContext()
2023-01-04 17:56:01 +01:00
2023-01-05 11:55:20 +01:00
// Update status data in database.
try await self.copy(from: status, to: statusData, on: backgroundContext)
try backgroundContext.save()
return statusData
}
private func copy(from status: Status, to statusData: StatusData, on backgroundContext: NSManagedObjectContext) async throws {
statusData.copyFrom(status)
2023-01-04 17:56:01 +01:00
for attachment in status.mediaAttachments {
let imageData = try await self.fetchImage(attachment: attachment)
2023-01-03 14:09:22 +01:00
2023-01-04 17:56:01 +01:00
guard let imageData = imageData else {
continue
}
2023-01-03 14:09:22 +01:00
2023-01-04 17:56:01 +01:00
// Save attachment in database.
let attachmentData = statusData.attachments().first { item in item.id == attachment.id }
2023-01-05 11:55:20 +01:00
?? AttachmentDataHandler.shared.createAttachmnentDataEntity(viewContext: backgroundContext)
2023-01-04 17:56:01 +01:00
2023-01-05 11:55:20 +01:00
attachmentData.copyFrom(attachment)
2023-01-04 17:56:01 +01:00
attachmentData.statusId = statusData.id
attachmentData.data = imageData
2023-01-05 11:55:20 +01:00
// TODO: read exif information
2023-01-04 17:56:01 +01:00
if attachmentData.isInserted {
attachmentData.statusRelation = statusData
statusData.addToAttachmentRelation(attachmentData)
2023-01-03 14:09:22 +01:00
}
}
}
private func fetchImage(attachment: Attachment) async throws -> Data? {
guard let data = try await RemoteFileService.shared.fetchData(url: attachment.url) else {
return nil
}
return data
}
}