mv-experiment #1
|
@ -37,6 +37,13 @@ enum MessageContentType: Codable & Equatable, DatabaseValueConvertible {
|
|||
case typing
|
||||
case invite
|
||||
case attachment(Attachment)
|
||||
|
||||
var isAttachment: Bool {
|
||||
if case .attachment = self {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
enum MessageStatus: Int, Codable, DatabaseValueConvertible {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import Combine
|
||||
import Foundation
|
||||
import GRDB
|
||||
import Photos
|
||||
import SwiftUI
|
||||
|
||||
|
@ -12,11 +13,13 @@ final class AttachmentsStore: ObservableObject {
|
|||
private let client: Client
|
||||
private let roster: Roster
|
||||
|
||||
private var messagesCancellable: AnyCancellable?
|
||||
private var processing: Set<String> = []
|
||||
|
||||
init(roster: Roster, client: Client) {
|
||||
self.client = client
|
||||
self.roster = roster
|
||||
subscribe()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,38 +194,54 @@ extension AttachmentsStore {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Uploadings/Downloadings
|
||||
extension AttachmentsStore {
|
||||
func processAttachment(_ message: Message) {
|
||||
// Prevent multiple processing
|
||||
if processing.contains(message.id) {
|
||||
return
|
||||
}
|
||||
|
||||
// Process in background
|
||||
Task(priority: .background) {
|
||||
// Do needed processing
|
||||
if case .attachment(let attachment) = message.contentType {
|
||||
if attachment.localPath != nil, attachment.remotePath == nil {
|
||||
// Uploading
|
||||
processing.insert(message.id)
|
||||
await uploadAttachment(message)
|
||||
processing.remove(message.id)
|
||||
} else if attachment.localPath == nil, attachment.remotePath != nil {
|
||||
// Downloading
|
||||
processing.insert(message.id)
|
||||
await downloadAttachment(message)
|
||||
processing.remove(message.id)
|
||||
} else if attachment.localPath != nil, attachment.remotePath != nil, attachment.thumbnailName == nil, attachment.type == .image {
|
||||
// Generate thumbnail
|
||||
processing.insert(message.id)
|
||||
await generateThumbnail(message)
|
||||
processing.remove(message.id)
|
||||
// MARK: - Processing attachments
|
||||
private extension AttachmentsStore {
|
||||
func subscribe() {
|
||||
messagesCancellable = ValueObservation.tracking(Message
|
||||
.filter(
|
||||
(Column("to") == roster.bareJid && Column("from") == roster.contactBareJid) ||
|
||||
(Column("from") == roster.bareJid && Column("to") == roster.contactBareJid)
|
||||
)
|
||||
.order(Column("date").desc)
|
||||
.fetchAll
|
||||
)
|
||||
.publisher(in: Database.shared.dbQueue, scheduling: .immediate)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { _ in
|
||||
} receiveValue: { [weak self] messages in
|
||||
let forProcessing = messages
|
||||
// .filter { $0.status == .pending }
|
||||
.filter { self?.processing.contains($0.id) == false }
|
||||
.filter { $0.contentType.isAttachment }
|
||||
for message in forProcessing {
|
||||
if case .attachment(let attachment) = message.contentType {
|
||||
if attachment.localPath != nil, attachment.remotePath == nil {
|
||||
// Uploading
|
||||
self?.processing.insert(message.id)
|
||||
Task(priority: .background) {
|
||||
await self?.uploadAttachment(message)
|
||||
}
|
||||
} else if attachment.localPath == nil, attachment.remotePath != nil {
|
||||
// Downloading
|
||||
self?.processing.insert(message.id)
|
||||
Task(priority: .background) {
|
||||
await self?.downloadAttachment(message)
|
||||
}
|
||||
} else if attachment.localPath != nil, attachment.remotePath != nil, attachment.thumbnailName == nil, attachment.type == .image {
|
||||
// Generate thumbnail
|
||||
self?.processing.insert(message.id)
|
||||
Task(priority: .background) {
|
||||
await self?.generateThumbnail(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Uploadings/Downloadings
|
||||
extension AttachmentsStore {
|
||||
private func uploadAttachment(_ message: Message) async {
|
||||
do {
|
||||
try await message.setStatus(.pending)
|
||||
|
@ -246,8 +265,10 @@ extension AttachmentsStore {
|
|||
message.oobUrl = remotePath
|
||||
try await message.save()
|
||||
try await client.sendMessage(message)
|
||||
processing.remove(message.id)
|
||||
try await message.setStatus(.sent)
|
||||
} catch {
|
||||
processing.remove(message.id)
|
||||
try? await message.setStatus(.error)
|
||||
}
|
||||
}
|
||||
|
@ -276,6 +297,7 @@ extension AttachmentsStore {
|
|||
remotePath: remotePath
|
||||
)
|
||||
)
|
||||
processing.remove(message.id)
|
||||
try await message.save()
|
||||
} catch {
|
||||
logIt(.error, "Can't download attachment: \(error)")
|
||||
|
@ -324,6 +346,7 @@ extension AttachmentsStore {
|
|||
remotePath: attachment.remotePath
|
||||
)
|
||||
)
|
||||
processing.remove(message.id)
|
||||
try? await message.save()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,15 +150,12 @@ private struct AttachmentView: View {
|
|||
ProgressView()
|
||||
.scaleEffect(1.5)
|
||||
.progressViewStyle(CircularProgressViewStyle(tint: .Material.Elements.active))
|
||||
let imageName = progressImageName(attachment.type ?? .file)
|
||||
let imageName = progressImageName(attachment.type)
|
||||
Image(systemName: imageName)
|
||||
.font(.body1)
|
||||
.foregroundColor(.Material.Elements.active)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
attachments.processAttachment(message)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private var failed: some View {
|
||||
|
@ -178,7 +175,9 @@ private struct AttachmentView: View {
|
|||
}
|
||||
}
|
||||
.onTapGesture {
|
||||
attachments.processAttachment(message)
|
||||
Task {
|
||||
try? await message.setStatus(.pending)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue