Skip to content

Commit 7ded060

Browse files
committed
feat(camera): add in-app camera with direct upload to Nextcloud
Introduces a custom AVFoundation camera that captures photos and videos and uploads them directly to Nextcloud without saving to the device camera roll by default. - Custom camera UI: shutter, flash, flip, mode selector (photo/video) - Pinch-to-zoom from 0.5x (ultra-wide) to 10x with live zoom label - Virtual multi-camera support (triple/dual-wide) for sub-1x zoom - Recording timer with red dot, screen stays on during video recording - App backgrounding gracefully stops recording in progress - Review screen after capture: retake or use, inline video playback - "Save to camera roll" opt-in toggle per session (off by default) - Global default for toggle in Settings -> Advanced - Filenames follow Nextcloud conventions via createFileName(), including "Maintain original filename" mode (IMG_XXXX.JPG) - Videos saved as .mov (QuickTime) matching Apple native camera format - Restored live photo upload logic: livePhotoFile and nativeFormat were inadvertently removed in a previous refactor - Fix: PHAuthorizationStatus.limited now correctly treated as authorized - Fix: NCViewerQuickLookView crash when asset is nil after model change Signed-off-by: Rasmus Wøldike <rswoldike@gmail.com>
1 parent b6ca6da commit 7ded060

10 files changed

Lines changed: 1116 additions & 356 deletions

File tree

iOSClient/Main/Create/Upload Assets/NCUploadAssetsModel.swift

Lines changed: 277 additions & 150 deletions
Large diffs are not rendered by default.

iOSClient/Main/Create/Upload Assets/NCUploadAssetsView.swift

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
//
2-
// NCUploadAssetsView.swift
3-
// Nextcloud
4-
//
5-
// Created by Marino Faggiana on 03/06/24.
6-
// Copyright © 2024 Marino Faggiana. All rights reserved.
7-
//
1+
// SPDX-FileCopyrightText: Nextcloud GmbH
2+
// SPDX-FileCopyrightText: 2024 Marino Faggiana
3+
// SPDX-FileCopyrightText: 2026 Rasmus Wøldike
4+
// SPDX-License-Identifier: GPL-3.0-or-later
85

96
import SwiftUI
107
import NextcloudKit
@@ -46,7 +43,7 @@ struct NCUploadAssetsView: View {
4643
}) {
4744
Label(NSLocalizedString("_rename_", comment: ""), systemImage: "pencil")
4845
}
49-
if item.asset.type == .photo || item.asset.type == .livePhoto {
46+
if item.asset?.type == .photo || item.asset?.type == .livePhoto {
5047
Button(action: {
5148
if model.presentedQuickLook(index: index, fileNamePath: fileNamePath) {
5249
self.index = index
@@ -58,22 +55,22 @@ struct NCUploadAssetsView: View {
5855
}
5956
if item.data != nil {
6057
Button(action: {
61-
if let image = model.previewStore[index].asset.fullResolutionImage?.resizeImage(size: CGSize(width: 240, height: 240), isAspectRation: true) {
58+
if let image = model.previewStore[index].asset?.fullResolutionImage?.resizeImage(size: CGSize(width: 240, height: 240), isAspectRation: true) {
6259
model.previewStore[index].image = image
6360
model.previewStore[index].data = nil
64-
model.previewStore[index].assetType = model.previewStore[index].asset.type
61+
model.previewStore[index].assetType = model.previewStore[index].asset?.type ?? .photo
6562
}
6663
}) {
6764
Label(NSLocalizedString("_undo_modify_", comment: ""), systemImage: "arrow.uturn.backward.circle")
6865
}
6966
}
70-
if item.data == nil && item.asset.type == .livePhoto && item.assetType == .livePhoto {
67+
if item.data == nil && item.asset?.type == .livePhoto && item.assetType == .livePhoto {
7168
Button(action: {
7269
model.previewStore[index].assetType = .photo
7370
}) {
7471
Label(NSLocalizedString("_disable_livephoto_", comment: ""), systemImage: "livephoto.slash")
7572
}
76-
} else if item.data == nil && item.asset.type == .livePhoto && item.assetType == .photo {
73+
} else if item.data == nil && item.asset?.type == .livePhoto && item.assetType == .photo {
7774
Button(action: {
7875
model.previewStore[index].assetType = .livePhoto
7976
}) {
@@ -135,9 +132,15 @@ struct NCUploadAssetsView: View {
135132
}
136133
}
137134

135+
if !model.tempAssets.isEmpty {
136+
Section {
137+
Toggle(NSLocalizedString("_save_to_camera_roll_", comment: ""), isOn: $model.saveToCameraRoll)
138+
.font(.body)
139+
.tint(Color(NCBrandColor.shared.getElement(account: model.session.account)))
140+
}
141+
}
142+
138143
Section {
139-
// Auto upload requires creating folders and subfolders which are difficult to manage offline
140-
//
141144
if NCNetworking.shared.isOnline {
142145
Toggle(isOn: $model.useAutoUploadFolder, label: {
143146
Text(NSLocalizedString("_use_folder_auto_upload_", comment: ""))
@@ -260,12 +263,12 @@ struct NCUploadAssetsView: View {
260263
.frame(width: 80, height: 80, alignment: .center)
261264
.cornerRadius(10)
262265
} else {
263-
Color(.lightGray) // Placeholder
266+
Color(.lightGray)
264267
.frame(width: 80, height: 80)
265268
.cornerRadius(10)
266269
.onAppear {
267270
DispatchQueue.main.async {
268-
if let asset = item.asset.phAsset,
271+
if let asset = item.asset?.phAsset,
269272
let image = model.lowResolutionImage(asset: asset) {
270273
model.previewStore[index].image = image
271274
}
@@ -295,7 +298,3 @@ struct NCUploadAssetsView: View {
295298
}
296299
}
297300
}
298-
299-
#Preview {
300-
NCUploadAssetsView(model: NCUploadAssetsModel(assets: [], serverUrl: "/", controller: nil))
301-
}

0 commit comments

Comments
 (0)