Merge pull request #89 from VernissageApp/feature/improve-widget-image-fetcher

Improve widget image fetcher
This commit is contained in:
Marcin Czachurski 2023-10-11 18:57:24 +02:00 committed by GitHub
commit 598d9f77ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 109 additions and 29 deletions

View File

@ -15,4 +15,24 @@ class AttachmentDataHandler {
let context = viewContext ?? CoreDataHandler.shared.container.viewContext
return AttachmentData(context: context)
}
func getDownloadedAttachmentData(accountId: String, length: Int, viewContext: NSManagedObjectContext? = nil) -> [AttachmentData] {
let context = viewContext ?? CoreDataHandler.shared.container.viewContext
let fetchRequest = AttachmentData.fetchRequest()
fetchRequest.fetchLimit = length
let sortDescriptor = NSSortDescriptor(key: "statusRelation.id", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
let predicate1 = NSPredicate(format: "statusRelation.pixelfedAccount.id = %@", accountId)
let predicate2 = NSPredicate(format: "data != nil")
fetchRequest.predicate = NSCompoundPredicate.init(type: .and, subpredicates: [predicate1, predicate2])
do {
return try context.fetch(fetchRequest)
} catch {
CoreDataError.shared.handle(error, message: "Error during fetching attachment data (getDownloadedAttachmentData).")
return []
}
}
}

View File

@ -17,31 +17,54 @@ struct PhotoProvider: TimelineProvider {
func getSnapshot(in context: Context, completion: @escaping (PhotoWidgetEntry) -> Void) {
Task {
if let widgetEntry = await self.getWidgetEntries(length: 1).first {
completion(widgetEntry)
} else {
let entry = StatusFetcher.shared.placeholder()
completion(entry)
}
let widgetEntry = await self.getWidgetEntriesForSnapshot()
completion(widgetEntry)
}
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
Task {
let currentDate = Date()
let widgetEntries = await self.getWidgetEntries()
let widgetEntries = await self.getWidgetEntriesForTimeline()
let nextUpdateDate = Calendar.current.date(byAdding: .hour, value: 1, to: currentDate)!
let timeline = Timeline(entries: widgetEntries, policy: .after(nextUpdateDate))
completion(timeline)
}
}
func getWidgetEntriesForSnapshot() async -> PhotoWidgetEntry {
let entriesFromDatabase = await self.getWidgetEntriesFromDatabase(length: 1)
if let firstEntry = entriesFromDatabase.first {
return firstEntry
}
return StatusFetcher.shared.placeholder()
}
func getWidgetEntriesForTimeline() async -> [PhotoWidgetEntry] {
let entriesFromServer = await self.getWidgetEntriesFromServer(length: 3)
if entriesFromServer.isEmpty == false {
return entriesFromServer
}
let entriesFromDatabase = await self.getWidgetEntriesFromDatabase(length: 3)
if entriesFromDatabase.isEmpty == false {
return entriesFromDatabase
}
return [StatusFetcher.shared.placeholder()]
}
func getWidgetEntries(length: Int = 3) async -> [PhotoWidgetEntry] {
func getWidgetEntriesFromServer(length: Int) async -> [PhotoWidgetEntry] {
do {
return try await StatusFetcher.shared.fetchWidgetEntries(length: length)
return try await StatusFetcher.shared.fetchWidgetEntriesFromServer(length: length)
} catch {
return [StatusFetcher.shared.placeholder()]
return []
}
}
func getWidgetEntriesFromDatabase(length: Int) async -> [PhotoWidgetEntry] {
return await StatusFetcher.shared.fetchWidgetEntriesFromDatabase(length: length)
}
}

View File

@ -12,7 +12,7 @@ public class StatusFetcher {
public static let shared = StatusFetcher()
private init() { }
func fetchWidgetEntries(length: Int = 8) async throws -> [PhotoWidgetEntry] {
func fetchWidgetEntriesFromServer(length: Int) async throws -> [PhotoWidgetEntry] {
let defaultSettings = ApplicationSettingsHandler.shared.get()
guard let accountId = defaultSettings.currentAccount else {
return [self.placeholder()]
@ -67,6 +67,37 @@ public class StatusFetcher {
return widgetEntries.shuffled()
}
func fetchWidgetEntriesFromDatabase(length: Int) async -> [PhotoWidgetEntry] {
let defaultSettings = ApplicationSettingsHandler.shared.get()
guard let accountId = defaultSettings.currentAccount else {
return [self.placeholder()]
}
let attachmentDatas = AttachmentDataHandler.shared.getDownloadedAttachmentData(accountId: accountId, length: length)
var widgetEntries: [PhotoWidgetEntry] = []
for attachmentData in attachmentDatas {
guard let imageData = attachmentData.data, let uiImage = UIImage(data: imageData) else {
continue
}
let uiAvatar = await FileFetcher.shared.getImage(url: attachmentData.statusRelation?.accountAvatar)
let displayDate = Calendar.current.date(byAdding: .minute, value: widgetEntries.count * 20, to: Date())
widgetEntries.append(PhotoWidgetEntry(date: displayDate ?? Date(),
image: uiImage,
avatar: uiAvatar,
displayName: attachmentData.statusRelation?.accountDisplayName,
statusId: attachmentData.statusId))
}
if widgetEntries.isEmpty {
widgetEntries.append(self.placeholder())
}
return widgetEntries.shuffled()
}
func placeholder() -> PhotoWidgetEntry {
PhotoWidgetEntry(date: Date(), image: nil, avatar: nil, displayName: "Caroline Rick", statusId: "")

View File

@ -12,21 +12,23 @@ struct PhotoLargeWidgetView: View {
var entry: PhotoProvider.Entry
var body: some View {
if let uiImage = entry.image, let uiAvatar = entry.avatar {
self.getWidgetBody(uiImage: Image(uiImage: uiImage), uiAvatar: Image(uiImage: uiAvatar))
if let uiImage = entry.image {
self.getWidgetBody(uiImage: Image(uiImage: uiImage), uiAvatarImage: entry.avatar)
} else {
self.getWidgetBody(uiImage: Image("Placeholder"), uiAvatar: Image("Avatar"))
self.getWidgetBody(uiImage: Image("Placeholder"), uiAvatarImage: UIImage(named: "Avatar"))
.unredacted()
}
}
@ViewBuilder
private func getWidgetBody(uiImage: Image, uiAvatar: Image) -> some View {
private func getWidgetBody(uiImage: Image, uiAvatarImage: UIImage?) -> some View {
VStack {
Spacer()
HStack {
uiAvatar
.avatar(size: 24)
if let uiAvatar = uiAvatarImage {
Image(uiImage: uiAvatar)
.avatar(size: 24)
}
Text(entry.displayName ?? "")
.font(.system(size: 15))

View File

@ -12,21 +12,23 @@ struct PhotoMediumWidgetView: View {
var entry: PhotoProvider.Entry
var body: some View {
if let uiImage = entry.image, let uiAvatar = entry.avatar {
self.getWidgetBody(uiImage: Image(uiImage: uiImage), uiAvatar: Image(uiImage: uiAvatar))
if let uiImage = entry.image {
self.getWidgetBody(uiImage: Image(uiImage: uiImage), uiAvatarImage: entry.avatar)
} else {
self.getWidgetBody(uiImage: Image("Placeholder"), uiAvatar: Image("Avatar"))
self.getWidgetBody(uiImage: Image("Placeholder"), uiAvatarImage: UIImage(named: "Avatar"))
.unredacted()
}
}
@ViewBuilder
private func getWidgetBody(uiImage: Image, uiAvatar: Image) -> some View {
private func getWidgetBody(uiImage: Image, uiAvatarImage: UIImage?) -> some View {
VStack {
Spacer()
HStack {
uiAvatar
.avatar(size: 24)
if let uiAvatar = uiAvatarImage {
Image(uiImage: uiAvatar)
.avatar(size: 24)
}
Text(entry.displayName ?? "")
.font(.system(size: 15))

View File

@ -12,21 +12,23 @@ struct PhotoSmallWidgetView: View {
var entry: PhotoProvider.Entry
var body: some View {
if let uiImage = entry.image, let uiAvatar = entry.avatar {
self.getWidgetBody(uiImage: Image(uiImage: uiImage), uiAvatar: Image(uiImage: uiAvatar))
if let uiImage = entry.image {
self.getWidgetBody(uiImage: Image(uiImage: uiImage), uiAvatarImage: entry.avatar)
} else {
self.getWidgetBody(uiImage: Image("Placeholder"), uiAvatar: Image("Avatar"))
self.getWidgetBody(uiImage: Image("Placeholder"), uiAvatarImage: UIImage(named: "Avatar"))
.unredacted()
}
}
@ViewBuilder
private func getWidgetBody(uiImage: Image, uiAvatar: Image) -> some View {
private func getWidgetBody(uiImage: Image, uiAvatarImage: UIImage?) -> some View {
VStack {
Spacer()
HStack {
uiAvatar
.avatar(size: 16)
if let uiAvatar = uiAvatarImage {
Image(uiImage: uiAvatar)
.avatar(size: 16)
}
Spacer()
}