Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ struct FileAttachmentProcessor: AttachmentProcessor {
throw AttachmentError.fileCreationFailed
}

return .localBinaryFile(fileData)
return .localBinaryFile(ChatLocalBinaryFile(data: fileData))
}
}

Expand All @@ -50,12 +50,12 @@ struct CameraMediaProcessor: AttachmentProcessor {
guard let fileData = try? fileActionsService.createFileData(image: image, type: type) else {
throw AttachmentError.fileCreationFailed
}
return .localBinaryFile(fileData)
return .localBinaryFile(ChatLocalBinaryFile(data: fileData))
case .video(let file):
guard let fileData = try? fileActionsService.createFileData(fileUrl: file) else {
throw AttachmentError.fileCreationFailed
}
return .localBinaryFile(fileData)
return .localBinaryFile(ChatLocalBinaryFile(data: fileData))
}
}
}
Expand All @@ -68,7 +68,7 @@ struct PhotosPickerProcessor: AsyncAttachmentProcessor {

func process(_ input: PhotosPickerItem, spaceId: String) async throws -> ChatLinkedObject {
let data = try await fileActionsService.createFileData(photoItem: input)
return .localPhotosFile(ChatLocalPhotosFile(data: data, photosPickerItemHash: input.hashValue))
return .localPhotosFile(ChatLocalPhotosFile(data: ChatLocalBinaryFile(data: data), photosPickerItemHash: input.hashValue))
}
}

Expand All @@ -82,7 +82,7 @@ struct PasteBufferProcessor: AsyncAttachmentProcessor {
guard let fileData = try? await fileActionsService.createFileData(source: .itemProvider(input)) else {
throw AttachmentError.fileCreationFailed
}
return .localBinaryFile(fileData)
return .localBinaryFile(ChatLocalBinaryFile(data: fileData))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ protocol ChatAttachmentHandlerProtocol: ObservableObject {

func addUploadedObject(_ details: MessageAttachmentDetails) throws
func removeLinkedObject(_ linkedObject: ChatLinkedObject)
func clearAll()
func clearState()
func canAddOneAttachment() -> Bool
func setPhotosItems(_ items: [PhotosPickerItem]) throws
func getPhotosItems() -> [PhotosPickerItem]
Expand Down Expand Up @@ -54,7 +54,7 @@ final class ChatAttachmentHandler: ChatAttachmentHandlerProtocol {

// MARK: - State

private let state = ChatAttachmentState()
private let state: ChatAttachmentState

var linkedObjectsPublisher: AnyPublisher<[ChatLinkedObject], Never> {
state.linkedObjectsPublisher
Expand Down Expand Up @@ -91,6 +91,7 @@ final class ChatAttachmentHandler: ChatAttachmentHandlerProtocol {

init(spaceId: String) {
self.spaceId = spaceId
self.state = ChatAttachmentState(spaceId: spaceId)
}

// MARK: - Public Methods
Expand All @@ -111,11 +112,8 @@ final class ChatAttachmentHandler: ChatAttachmentHandlerProtocol {
AnytypeAnalytics.instance().logDetachItemChat()
}

func clearAll() {
state.clearAllLinkedObjects()
state.clearPhotosItems()
state.updatePhotosItemsTask()
state.cancelAllLinkPreviewTasks()
func clearState() {
state.clearState()
}

func canAddOneAttachment() -> Bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,23 @@ import _PhotosUI_SwiftUI

@MainActor
final class ChatAttachmentState {

nonisolated init() { }

@Injected(\.fileActionsService)
private var fileActionsService: any FileActionsServiceProtocol

private let linkedObjectsSubject = CurrentValueSubject<[ChatLinkedObject], Never>([])
private let attachmentsDownloadingSubject = CurrentValueSubject<Bool, Never>(false)
private let photosItemsTaskSubject = CurrentValueSubject<UUID, Never>(UUID())
private var linkPreviewTasks: [URL: AnyCancellable] = [:]
private var preloadTasks: [Int: Task<Void, Never>] = [:]
private var photosItems: [PhotosPickerItem] = []

let spaceId: String

nonisolated init(spaceId: String) {
self.spaceId = spaceId
}

var linkedObjectsPublisher: AnyPublisher<[ChatLinkedObject], Never> {
linkedObjectsSubject.eraseToAnyPublisher()
}
Expand All @@ -36,29 +44,6 @@ final class ChatAttachmentState {
linkedObjectsSubject.send(objects)
}

func addLinkedObject(_ object: ChatLinkedObject) {
var current = linkedObjectsSubject.value
current.append(object)
linkedObjectsSubject.send(current)
}

func removeLinkedObject(with id: Int) {
var current = linkedObjectsSubject.value
current.removeAll { $0.id == id }
linkedObjectsSubject.send(current)
}

func updateLinkedObject(at index: Int, with object: ChatLinkedObject) {
var current = linkedObjectsSubject.value
guard index < current.count else { return }
current[index] = object
linkedObjectsSubject.send(current)
}

func clearAllLinkedObjects() {
linkedObjectsSubject.send([])
}

func setAttachmentsDownloading(_ downloading: Bool) {
attachmentsDownloadingSubject.send(downloading)
}
Expand All @@ -75,11 +60,6 @@ final class ChatAttachmentState {
linkPreviewTasks[url] = nil
}

func cancelAllLinkPreviewTasks() {
linkPreviewTasks.values.forEach { $0.cancel() }
linkPreviewTasks.removeAll()
}

func hasLinkPreviewTask(for url: URL) -> Bool {
return linkPreviewTasks[url] != nil
}
Expand All @@ -96,7 +76,109 @@ final class ChatAttachmentState {
photosItems.removeAll(where: predicate)
}

func clearPhotosItems() {
func clearState() {
preloadTasks.values.forEach { $0.cancel() }
preloadTasks.removeAll()

discardPreloadedFiles(linkedObjects.compactMap { $0.preloadFileId })

linkedObjectsSubject.send([])

linkPreviewTasks.values.forEach { $0.cancel() }
linkPreviewTasks.removeAll()

photosItems = []

updatePhotosItemsTask()
}

func addLinkedObject(_ linkedObject: ChatLinkedObject) {
storeLinkedObject(linkedObject)
startPreload(linkedObject: linkedObject)
}

func updateLinkedObject(at index: Int, with linkedObject: ChatLinkedObject) {
updateLinkedObjectStorage(at: index, with: linkedObject)
startPreload(linkedObject: linkedObject)
}

func removeLinkedObject(with id: Int) {
if let objectToRemove = linkedObjects.first(where: { $0.id == id }) {
discardPreloadedFile(from: objectToRemove)
}
removeLinkedObjectFromStorage(with: id)
}

private func storeLinkedObject(_ object: ChatLinkedObject) {
var current = linkedObjectsSubject.value
current.append(object)
linkedObjectsSubject.send(current)
}

private func updateLinkedObjectStorage(at index: Int, with object: ChatLinkedObject) {
var current = linkedObjectsSubject.value
guard index < current.count else { return }
current[index] = object
linkedObjectsSubject.send(current)
}

private func removeLinkedObjectFromStorage(with id: Int) {
var current = linkedObjectsSubject.value
current.removeAll { $0.id == id }
linkedObjectsSubject.send(current)
}

private func startPreload(linkedObject: ChatLinkedObject) {
guard let data = linkedObject.fileData else { return }

let task = Task { [weak self, fileActionsService, spaceId] in
if let preloadFileId = try? await fileActionsService.preloadFileObject(spaceId: spaceId, data: data, origin: .none) {
self?.updatePreloadFileId(for: linkedObject.id, preloadFileId: preloadFileId)
}
self?.removePreloadTask(objectId: linkedObject.id)
}

addPreloadTask(objectId: linkedObject.id, task: task)
}

private func updatePreloadFileId(for objectId: Int, preloadFileId: String) {
var linkedObjects = linkedObjectsSubject.value
guard let index = linkedObjects.firstIndex(where: { $0.id == objectId }) else { return }

switch linkedObjects[index] {
case .localPhotosFile(var file):
file.data?.preloadFileId = preloadFileId
linkedObjects[index] = .localPhotosFile(file)
case .localBinaryFile(var file):
file.preloadFileId = preloadFileId
linkedObjects[index] = .localBinaryFile(file)
default:
return
}

linkedObjectsSubject.send(linkedObjects)
}

private func addPreloadTask(objectId: Int, task: Task<Void, Never>) {
removePreloadTask(objectId: objectId)
preloadTasks[objectId] = task
}

private func removePreloadTask(objectId: Int) {
preloadTasks[objectId]?.cancel()
preloadTasks[objectId] = nil
}

private func discardPreloadedFile(from linkedObject: ChatLinkedObject) {
guard let preloadFileId = linkedObject.preloadFileId else { return }
discardPreloadedFiles([preloadFileId])
}

private func discardPreloadedFiles(_ preloadFileIds: [String]) {
for preloadFileId in preloadFileIds {
Task {
try await fileActionsService.discardPreloadFile(fileId: preloadFileId, spaceId: spaceId)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -659,7 +659,7 @@ final class ChatViewModel: ObservableObject, MessageModuleOutput, ChatActionProv

private func clearInput() {
message = NSAttributedString()
attachmentHandler.clearAll()
attachmentHandler.clearState()
replyToMessage = nil
editMessage = nil
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@ import Foundation
import Services

struct ChatLocalPhotosFile: Equatable {
let data: FileData?
var data: ChatLocalBinaryFile?
let photosPickerItemHash: Int
}

struct ChatLocalBinaryFile: Equatable {
let data: FileData
var preloadFileId: String?
}

enum ChatLinkedObject: Identifiable, Equatable {
case uploadedObject(MessageAttachmentDetails)
case localPhotosFile(ChatLocalPhotosFile)
case localBinaryFile(FileData)
case localBinaryFile(ChatLocalBinaryFile)
case localBookmark(ChatLocalBookmark)

var id: Int {
Expand All @@ -19,7 +24,7 @@ enum ChatLinkedObject: Identifiable, Equatable {
case .localPhotosFile(let file):
return file.photosPickerItemHash
case .localBinaryFile(let file):
return file.path.hashValue
return file.data.path.hashValue
case .localBookmark(let bookmark):
return bookmark.hashValue
}
Expand Down Expand Up @@ -51,4 +56,27 @@ enum ChatLinkedObject: Identifiable, Equatable {
return nil
}
}

var preloadFileId: String? {
switch self {
case .localPhotosFile(let file):
return file.data?.preloadFileId
case .localBinaryFile(let file):
return file.preloadFileId
default:
return nil
}
}

var fileData: FileData? {
switch self {
case .localPhotosFile(let file):
return file.data?.data
case .localBinaryFile(let file):
return file.data
case .localBookmark, .uploadedObject:
return nil
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import UIKit

struct ChatInputLocalFile: View {

let fileData: FileData
let fileData: ChatLocalBinaryFile
let onTapObject: () -> Void
let onTapRemove: () -> Void

Expand All @@ -16,15 +16,15 @@ struct ChatInputLocalFile: View {

@ViewBuilder
private var content: some View {
switch fileData.chatViewType {
switch fileData.data.chatViewType {
case .image:
ChatInputLocalImageView(contentsOfFile: fileData.path, onTapRemove: onTapRemove)
.id(fileData.path)
ChatInputLocalImageView(contentsOfFile: fileData.data.path, onTapRemove: onTapRemove)
.id(fileData.data.path)
case .file:
ChatInputLocalBinaryFileView(path: fileData.path, type: fileData.type, sizeInBytes: fileData.sizeInBytes, onTapRemove: onTapRemove)
ChatInputLocalBinaryFileView(path: fileData.data.path, type: fileData.data.type, sizeInBytes: fileData.data.sizeInBytes, onTapRemove: onTapRemove)
case .video:
ChatInputVideoView(url: URL(fileURLWithPath: fileData.path), onTapRemove: onTapRemove)
.id(fileData.path)
ChatInputVideoView(url: URL(fileURLWithPath: fileData.data.path), onTapRemove: onTapRemove)
.id(fileData.data.path)
}
}
}
Loading
Loading