Skip to content

Commit 5b66fa5

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 d58c2f9 commit 5b66fa5

9 files changed

Lines changed: 1116 additions & 321 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
@@ -45,7 +42,7 @@ struct NCUploadAssetsView: View {
4542
}) {
4643
Label(NSLocalizedString("_rename_", comment: ""), systemImage: "pencil")
4744
}
48-
if item.asset.type == .photo || item.asset.type == .livePhoto {
45+
if item.asset?.type == .photo || item.asset?.type == .livePhoto {
4946
Button(action: {
5047
if model.presentedQuickLook(index: index, fileNamePath: fileNamePath) {
5148
self.index = index
@@ -57,22 +54,22 @@ struct NCUploadAssetsView: View {
5754
}
5855
if item.data != nil {
5956
Button(action: {
60-
if let image = model.previewStore[index].asset.fullResolutionImage?.resizeImage(size: CGSize(width: 240, height: 240), isAspectRation: true) {
57+
if let image = model.previewStore[index].asset?.fullResolutionImage?.resizeImage(size: CGSize(width: 240, height: 240), isAspectRation: true) {
6158
model.previewStore[index].image = image
6259
model.previewStore[index].data = nil
63-
model.previewStore[index].assetType = model.previewStore[index].asset.type
60+
model.previewStore[index].assetType = model.previewStore[index].asset?.type ?? .photo
6461
}
6562
}) {
6663
Label(NSLocalizedString("_undo_modify_", comment: ""), systemImage: "arrow.uturn.backward.circle")
6764
}
6865
}
69-
if item.data == nil && item.asset.type == .livePhoto && item.assetType == .livePhoto {
66+
if item.data == nil && item.asset?.type == .livePhoto && item.assetType == .livePhoto {
7067
Button(action: {
7168
model.previewStore[index].assetType = .photo
7269
}) {
7370
Label(NSLocalizedString("_disable_livephoto_", comment: ""), systemImage: "livephoto.slash")
7471
}
75-
} else if item.data == nil && item.asset.type == .livePhoto && item.assetType == .photo {
72+
} else if item.data == nil && item.asset?.type == .livePhoto && item.assetType == .photo {
7673
Button(action: {
7774
model.previewStore[index].assetType = .livePhoto
7875
}) {
@@ -134,9 +131,15 @@ struct NCUploadAssetsView: View {
134131
}
135132
}
136133

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

0 commit comments

Comments
 (0)