This commit is contained in:
fmodf 2024-07-14 12:08:51 +02:00
parent 14a83ca1d8
commit 9c5c54e09e
7 changed files with 47 additions and 35 deletions

View file

@ -1,10 +1,10 @@
import Foundation
enum FileAction: Stateable {
case downloadAttachmentFile(id: String, attachmentRemotePath: URL)
case attachmentFileDownloaded(id: String, localUrl: URL)
case downloadingAttachmentFileFailed(id: String, reason: String)
case downloadAttachmentFile(messageId: String, attachmentRemotePath: URL)
case attachmentFileDownloaded(messageId: String, localName: String)
case downloadingAttachmentFileFailed(messageId: String, reason: String)
case createAttachmentThumbnail(id: String, localUrl: URL)
case attachmentThumbnailCreated(id: String, thumbnailUrl: URL)
case createAttachmentThumbnail(messageId: String, localName: String)
case attachmentThumbnailCreated(messageId: String, thumbnailName: String)
}

View file

@ -59,9 +59,9 @@ extension Database {
table.column("pending", .boolean).notNull()
table.column("sentError", .boolean).notNull()
table.column("attachmentType", .integer)
table.column("attachmentLocalPath", .text)
table.column("attachmentLocalName", .text)
table.column("attachmentRemotePath", .text)
table.column("attachmentThumbnailPath", .text)
table.column("attachmentThumbnailName", .text)
table.column("attachmentDownloadFailed", .boolean).notNull().defaults(to: false)
}
}

View file

@ -14,19 +14,18 @@ final class FileProcessing {
return subdirectoryURL
}
func createThumbnail(localUrl: URL) -> URL? {
let fileExtension = localUrl.pathExtension
let fileNameWithoutExtension = localUrl.deletingPathExtension().lastPathComponent
let thumbnailFileName = fileNameWithoutExtension + "_thumb." + fileExtension
func createThumbnail(localName: String) -> String? {
let thumbnailFileName = "thumb_\(localName)"
let thumbnailUrl = FileProcessing.fileFolder.appendingPathComponent(thumbnailFileName)
let localUrl = FileProcessing.fileFolder.appendingPathComponent(localName)
// check if thumbnail already exists
if FileManager.default.fileExists(atPath: thumbnailUrl.path) {
return thumbnailUrl
return thumbnailFileName
}
// create thumbnail if not exists
switch localUrl.lastPathComponent.attachmentType {
switch localName.attachmentType {
case .image:
guard let image = UIImage(contentsOfFile: localUrl.path) else { return nil }
let targetSize = CGSize(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
@ -34,7 +33,7 @@ final class FileProcessing {
guard let data = thumbnail.pngData() else { return nil }
do {
try data.write(to: thumbnailUrl)
return thumbnailUrl
return thumbnailFileName
} catch {
return nil
}

View file

@ -35,7 +35,7 @@ final class DatabaseMiddleware {
.store(in: &cancellables)
}
// swiftlint:disable:next function_body_length
// swiftlint:disable:next function_body_length cyclomatic_complexity
func middleware(state _: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
switch action {
// MARK: Accounts
@ -309,7 +309,7 @@ final class DatabaseMiddleware {
}
.eraseToAnyPublisher()
case .fileAction(.attachmentFileDownloaded(let id, let localUrl)):
case .fileAction(.attachmentFileDownloaded(let id, let localName)):
return Future<AppAction, Never> { promise in
Task(priority: .background) { [weak self] in
guard let database = self?.database else {
@ -321,7 +321,7 @@ final class DatabaseMiddleware {
_ = try database._db.write { db in
try Message
.filter(Column("id") == id)
.updateAll(db, Column("attachmentLocalPath").set(to: localUrl), Column("attachmentDownloadFailed").set(to: false))
.updateAll(db, Column("attachmentLocalName").set(to: localName), Column("attachmentDownloadFailed").set(to: false))
}
promise(.success(.empty))
} catch {
@ -332,7 +332,7 @@ final class DatabaseMiddleware {
}
.eraseToAnyPublisher()
case .fileAction(.attachmentThumbnailCreated(let id, let thumbnailUrl)):
case .fileAction(.attachmentThumbnailCreated(let id, let thumbnailName)):
return Future<AppAction, Never> { promise in
Task(priority: .background) { [weak self] in
guard let database = self?.database else {
@ -344,7 +344,7 @@ final class DatabaseMiddleware {
_ = try database._db.write { db in
try Message
.filter(Column("id") == id)
.updateAll(db, Column("attachmentThumbnailPath").set(to: thumbnailUrl))
.updateAll(db, Column("attachmentThumbnailName").set(to: thumbnailName))
}
promise(.success(.empty))
} catch {

View file

@ -21,7 +21,7 @@ final class FileMiddleware {
wSelf.downloadingMessageIDs.insert(message.id)
DispatchQueue.main.async {
// swiftlint:disable:next force_unwrapping
store.dispatch(.fileAction(.downloadAttachmentFile(id: message.id, attachmentRemotePath: message.attachmentRemotePath!)))
store.dispatch(.fileAction(.downloadAttachmentFile(messageId: message.id, attachmentRemotePath: message.attachmentRemotePath!)))
}
}
promise(.success(.empty))
@ -29,31 +29,32 @@ final class FileMiddleware {
case .fileAction(.downloadAttachmentFile(let id, let attachmentRemotePath)):
return Future { promise in
let localUrl = FileProcessing.fileFolder.appendingPathComponent(id).appendingPathExtension(attachmentRemotePath.pathExtension)
let localName = "\(id)_\(UUID().uuidString)\(attachmentRemotePath.lastPathComponent)"
let localUrl = FileProcessing.fileFolder.appendingPathComponent(localName)
DownloadManager.shared.enqueueDownload(from: attachmentRemotePath, to: localUrl) { error in
DispatchQueue.main.async {
if let error {
store.dispatch(.fileAction(.downloadingAttachmentFileFailed(id: id, reason: error.localizedDescription)))
store.dispatch(.fileAction(.downloadingAttachmentFileFailed(messageId: id, reason: error.localizedDescription)))
} else {
store.dispatch(.fileAction(.attachmentFileDownloaded(id: id, localUrl: localUrl)))
store.dispatch(.fileAction(.attachmentFileDownloaded(messageId: id, localName: localName)))
}
}
}
promise(.success(.empty))
}.eraseToAnyPublisher()
case .fileAction(.attachmentFileDownloaded(let id, let localUrl)):
case .fileAction(.attachmentFileDownloaded(let id, let localName)):
return Future { [weak self] promise in
self?.downloadingMessageIDs.remove(id)
promise(.success(.fileAction(.createAttachmentThumbnail(id: id, localUrl: localUrl))))
promise(.success(.fileAction(.createAttachmentThumbnail(messageId: id, localName: localName))))
}
.eraseToAnyPublisher()
case .fileAction(.createAttachmentThumbnail(let id, let localUrl)):
case .fileAction(.createAttachmentThumbnail(let id, let localName)):
return Future { [weak self] promise in
if let thumbnailUrl = FileProcessing.shared.createThumbnail(localUrl: localUrl) {
if let thumbnailName = FileProcessing.shared.createThumbnail(localName: localName) {
self?.downloadingMessageIDs.remove(id)
promise(.success(.fileAction(.attachmentThumbnailCreated(id: id, thumbnailUrl: thumbnailUrl))))
promise(.success(.fileAction(.attachmentThumbnailCreated(messageId: id, thumbnailName: thumbnailName))))
} else {
self?.downloadingMessageIDs.remove(id)
promise(.success(.empty))

View file

@ -42,9 +42,9 @@ struct Message: DBStorable, Equatable {
let sentError: Bool
var attachmentType: MessageAttachmentType?
var attachmentLocalPath: URL?
var attachmentLocalName: String?
var attachmentRemotePath: URL?
var attachmentThumbnailPath: URL?
var attachmentThumbnailName: String?
var attachmentDownloadFailed: Bool = false
}
@ -96,9 +96,9 @@ extension Message {
pending: false,
sentError: false,
attachmentType: nil,
attachmentLocalPath: nil,
attachmentLocalName: nil,
attachmentRemotePath: nil,
attachmentThumbnailPath: nil,
attachmentThumbnailName: nil,
attachmentDownloadFailed: false
)
if let oob = martinMessage.oob {
@ -108,3 +108,15 @@ extension Message {
return msg
}
}
extension Message {
var attachmentLocalPath: URL? {
guard let attachmentLocalName = attachmentLocalName else { return nil }
return FileProcessing.fileFolder.appendingPathComponent(attachmentLocalName)
}
var attachmentThumbnailPath: URL? {
guard let attachmentThumbnailName = attachmentThumbnailName else { return nil }
return FileProcessing.fileFolder.appendingPathComponent(attachmentThumbnailName)
}
}

View file

@ -88,8 +88,6 @@ private struct AttachmentView: View {
if let file = message.attachmentLocalPath {
VideoPlayerView(url: file)
.frame(width: Const.attachmentPreviewSize, height: Const.attachmentPreviewSize)
.cornerRadius(Const.attachmentPreviewSize / 10)
.overlay(RoundedRectangle(cornerRadius: Const.attachmentPreviewSize / 10).stroke(Color.Material.Shape.separator, lineWidth: 1))
} else {
placeholder
}
@ -135,7 +133,7 @@ private struct AttachmentView: View {
}
.onTapGesture {
if let url = message.attachmentRemotePath {
store.dispatch(.fileAction(.downloadAttachmentFile(id: message.id, attachmentRemotePath: url)))
store.dispatch(.fileAction(.downloadAttachmentFile(messageId: message.id, attachmentRemotePath: url)))
}
}
}
@ -160,12 +158,14 @@ private struct AttachmentView: View {
}
}
// TODO: Make video player better!
private struct VideoPlayerView: UIViewControllerRepresentable {
let url: URL
func makeUIViewController(context _: Context) -> AVPlayerViewController {
let controller = AVPlayerViewController()
controller.player = AVPlayer(url: url)
controller.allowsPictureInPicturePlayback = true
return controller
}