// // NCUploadAssets.swift // Nextcloud // // Created by Marino Faggiana on 04/01/23. // Copyright © 2023 Marino Faggiana. All rights reserved. // // Author Marino Faggiana // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // import SwiftUI import NextcloudKit import TLPhotoPicker import Mantis import Photos import QuickLook class NCHostingUploadAssetsView: NSObject { func makeShipDetailsUI(assets: [TLPHAsset], serverUrl: String, userBaseUrl: NCUserBaseUrl) -> UIViewController { let uploadAssets = NCUploadAssets(assets: assets, serverUrl: serverUrl, userBaseUrl: userBaseUrl ) let details = UploadAssetsView(uploadAssets: uploadAssets) return UIHostingController(rootView: details) } } // MARK: - Class struct PreviewStore { var id: String var asset: TLPHAsset var assetType: TLPHAsset.AssetType var data: Data? var fileName: String var image: UIImage } class NCUploadAssets: NSObject, ObservableObject, NCCreateFormUploadConflictDelegate { @Published var serverUrl: String @Published var assets: [TLPHAsset] @Published var userBaseUrl: NCUserBaseUrl @Published var dismiss = false @Published var isUseAutoUploadFolder: Bool = false @Published var isUseAutoUploadSubFolder: Bool = false @Published var previewStore: [PreviewStore] = [] @Published var showHUD: Bool = false @Published var uploadInProgress: Bool = false var metadatasNOConflict: [tableMetadata] = [] var metadatasUploadInConflict: [tableMetadata] = [] var timer: Timer? init(assets: [TLPHAsset], serverUrl: String, userBaseUrl: NCUserBaseUrl) { self.assets = assets self.serverUrl = serverUrl self.userBaseUrl = userBaseUrl } func loadImages() { var previewStore: [PreviewStore] = [] DispatchQueue.global().async { for asset in self.assets { guard let image = asset.fullResolutionImage?.resizeImage(size: CGSize(width: 300, height: 300), isAspectRation: true), let localIdentifier = asset.phAsset?.localIdentifier else { continue } previewStore.append(PreviewStore(id: localIdentifier, asset: asset, assetType: asset.type, fileName: "", image: image)) } DispatchQueue.main.async { self.previewStore = previewStore } } } func startTimer(navigationItem: UINavigationItem) { self.timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { _ in guard let buttonDone = navigationItem.leftBarButtonItems?.first, let buttonCrop = navigationItem.leftBarButtonItems?.last else { return } buttonCrop.isEnabled = true buttonDone.isEnabled = true if let markup = navigationItem.rightBarButtonItems?.first(where: { $0.accessibilityIdentifier == "QLOverlayMarkupButtonAccessibilityIdentifier" }) { if let originalButton = markup.value(forKey: "originalButton") as AnyObject? { if let symbolImageName = originalButton.value(forKey: "symbolImageName") as? String { if symbolImageName == "pencil.tip.crop.circle.on" { buttonCrop.isEnabled = false buttonDone.isEnabled = false } } } } }) } func stopTimer() { self.timer?.invalidate() } func dismissCreateFormUploadConflict(metadatas: [tableMetadata]?) { guard let metadatas = metadatas else { self.showHUD = false self.uploadInProgress.toggle() return } func createProcessUploads() { if !self.dismiss { NCNetworkingProcessUpload.shared.createProcessUploads(metadatas: metadatas, completion: { _ in self.dismiss = true }) } } if isUseAutoUploadFolder { DispatchQueue.global().async { let assets = self.assets.compactMap { $0.phAsset } let result = NCNetworking.shared.createFolder(assets: assets, selector: NCGlobal.shared.selectorUploadFile, useSubFolder: self.isUseAutoUploadSubFolder, account: self.userBaseUrl.account, urlBase: self.userBaseUrl.urlBase, userId: self.userBaseUrl.userId, withPush: false) DispatchQueue.main.async { self.showHUD = false self.uploadInProgress.toggle() if result { createProcessUploads() } else { let error = NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_error_createsubfolders_upload_") NCContentPresenter.shared.showError(error: error) } } } } else { createProcessUploads() } } } // MARK: - View struct UploadAssetsView: View { @State private var fileName: String = CCUtility.getFileNameMask(NCGlobal.shared.keyFileNameMask) @State private var isMaintainOriginalFilename: Bool = CCUtility.getOriginalFileName(NCGlobal.shared.keyFileNameOriginal) @State private var isAddFilenametype: Bool = CCUtility.getFileNameType(NCGlobal.shared.keyFileNameType) @State private var isPresentedSelect = false @State private var isPresentedUploadConflict = false @State private var isPresentedQuickLook = false @State private var isPresentedAlert = false @State private var fileNamePath = NSTemporaryDirectory() + "Photo.jpg" @State private var renameFileName: String = "" @State private var renameIndex: Int = 0 @State private var metadata: tableMetadata? @State private var index: Int = 0 var gridItems: [GridItem] = [GridItem()] @ObservedObject var uploadAssets: NCUploadAssets @Environment(\.presentationMode) var presentationMode init(uploadAssets: NCUploadAssets) { self.uploadAssets = uploadAssets uploadAssets.loadImages() } func getTextServerUrl(_ serverUrl: String) -> String { if let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", uploadAssets.userBaseUrl.account, serverUrl)), let metadata = NCManageDatabase.shared.getMetadataFromOcId(directory.ocId) { return (metadata.fileNameView) } else { return (serverUrl as NSString).lastPathComponent } } private func setFileNameMaskForPreview(fileName: String?) -> String { guard let asset = uploadAssets.assets.first?.phAsset else { return "" } var preview: String = "" let creationDate = asset.creationDate ?? Date() CCUtility.setOriginalFileName(isMaintainOriginalFilename, key: NCGlobal.shared.keyFileNameOriginal) CCUtility.setFileNameType(isAddFilenametype, key: NCGlobal.shared.keyFileNameType) CCUtility.setFileNameMask(fileName, key: NCGlobal.shared.keyFileNameMask) preview = CCUtility.createFileName( getOriginalFilenameForPreview() as String, fileDate: creationDate, fileType: asset.mediaType, keyFileName: fileName.isEmptyOrNil ? nil : NCGlobal.shared.keyFileNameMask, keyFileNameType: NCGlobal.shared.keyFileNameType, keyFileNameOriginal: NCGlobal.shared.keyFileNameOriginal, forcedNewFileName: false ) let trimmedPreview = preview.trimmingCharacters(in: .whitespacesAndNewlines) return String(format: NSLocalizedString("_preview_filename_", comment: ""), "MM, MMM, DD, YY, YYYY, HH, hh, mm, ss, ampm") + ":" + "\n\n" + (trimmedPreview as NSString).deletingPathExtension } private func save(completion: @escaping (_ metadatasNOConflict: [tableMetadata], _ metadatasUploadInConflict: [tableMetadata]) -> Void) { var metadatasNOConflict: [tableMetadata] = [] var metadatasUploadInConflict: [tableMetadata] = [] let autoUploadPath = NCManageDatabase.shared.getAccountAutoUploadPath(urlBase: uploadAssets.userBaseUrl.urlBase, userId: uploadAssets.userBaseUrl.userId, account: uploadAssets.userBaseUrl.account) var serverUrl = uploadAssets.isUseAutoUploadFolder ? autoUploadPath : uploadAssets.serverUrl let autoUploadSubfolderGranularity = NCManageDatabase.shared.getAccountAutoUploadSubfolderGranularity() for tlAsset in uploadAssets.assets { guard let asset = tlAsset.phAsset, let previewStore = uploadAssets.previewStore.first(where: { $0.id == asset.localIdentifier }) else { continue } let assetFileName = asset.originalFilename var livePhoto: Bool = false let creationDate = asset.creationDate ?? Date() let ext = assetFileName.pathExtension.lowercased() let fileName = previewStore.fileName.isEmpty ? CCUtility.createFileName(assetFileName as String, fileDate: creationDate, fileType: asset.mediaType, keyFileName: NCGlobal.shared.keyFileNameMask, keyFileNameType: NCGlobal.shared.keyFileNameType, keyFileNameOriginal: NCGlobal.shared.keyFileNameOriginal, forcedNewFileName: false)! : (previewStore.fileName + "." + ext) if previewStore.assetType == .livePhoto && CCUtility.getLivePhoto() && previewStore.data == nil { livePhoto = true } // Auto upload with subfolder if uploadAssets.isUseAutoUploadSubFolder { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy" let yearString = dateFormatter.string(from: creationDate) dateFormatter.dateFormat = "MM" let monthString = dateFormatter.string(from: creationDate) dateFormatter.dateFormat = "dd" let dayString = dateFormatter.string(from: creationDate) if autoUploadSubfolderGranularity == 0 { serverUrl = autoUploadPath + "/" + yearString } else if autoUploadSubfolderGranularity == 2 { serverUrl = autoUploadPath + "/" + yearString + "/" + monthString + "/" + dayString } else { // Month Granularity is default serverUrl = autoUploadPath + "/" + yearString + "/" + monthString } } // Check if is in upload let isRecordInSessions = NCManageDatabase.shared.getAdvancedMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@ AND session != ''", uploadAssets.userBaseUrl.account, serverUrl, fileName), sorted: "fileName", ascending: false) if !isRecordInSessions.isEmpty { continue } let metadata = NCManageDatabase.shared.createMetadata(account: uploadAssets.userBaseUrl.account, user: uploadAssets.userBaseUrl.user, userId: uploadAssets.userBaseUrl.userId, fileName: fileName, fileNameView: fileName, ocId: NSUUID().uuidString, serverUrl: serverUrl, urlBase: uploadAssets.userBaseUrl.urlBase, url: "", contentType: "", isLivePhoto: livePhoto) metadata.assetLocalIdentifier = asset.localIdentifier metadata.session = NCNetworking.shared.sessionIdentifierBackground metadata.sessionSelector = NCGlobal.shared.selectorUploadFile metadata.status = NCGlobal.shared.metadataStatusWaitUpload // Modified if let previewStore = uploadAssets.previewStore.first(where: { $0.id == asset.localIdentifier }), let data = previewStore.data { if metadata.contentType == "image/heic" { let fileNameNoExtension = (fileName as NSString).deletingPathExtension metadata.contentType = "image/jpeg" metadata.fileName = fileNameNoExtension + ".jpg" metadata.fileNameView = fileNameNoExtension + ".jpg" } let fileNamePath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)! do { try data.write(to: URL(fileURLWithPath: fileNamePath)) metadata.isExtractFile = true metadata.size = NCUtilityFileSystem.shared.getFileSize(filePath: fileNamePath) metadata.creationDate = asset.creationDate as? NSDate ?? (Date() as NSDate) metadata.date = asset.modificationDate as? NSDate ?? (Date() as NSDate) } catch { } } if let result = NCManageDatabase.shared.getMetadataConflict(account: uploadAssets.userBaseUrl.account, serverUrl: serverUrl, fileNameView: fileName) { metadata.fileName = result.fileName metadatasUploadInConflict.append(metadata) } else { metadatasNOConflict.append(metadata) } } completion(metadatasNOConflict, metadatasUploadInConflict) } private func presentedQuickLook(index: Int) { var image: UIImage? if let imageData = uploadAssets.previewStore[index].data { image = UIImage(data: imageData) } else if let imageFullResolution = uploadAssets.previewStore[index].asset.fullResolutionImage?.fixedOrientation() { image = imageFullResolution } if let image = image { if let data = image.jpegData(compressionQuality: 1) { do { try data.write(to: URL(fileURLWithPath: fileNamePath)) self.index = index isPresentedQuickLook = true } catch { } } } } private func deleteAsset(index: Int) { uploadAssets.assets.remove(at: index) uploadAssets.previewStore.remove(at: index) if uploadAssets.previewStore.isEmpty { uploadAssets.dismiss = true } } private func getOriginalFilenameForPreview() -> NSString { CCUtility.setOriginalFileName(isMaintainOriginalFilename, key: NCGlobal.shared.keyFileNameOriginal) if let asset = uploadAssets.assets.first?.phAsset { return asset.originalFilename } else { return "" } } var body: some View { NavigationView { ZStack(alignment: .top) { List { Section(footer: Text(NSLocalizedString("_modify_image_desc_", comment: ""))) { ScrollView(.horizontal) { LazyHGrid(rows: gridItems, alignment: .center, spacing: 10) { ForEach(0..