Skip to content

Commit e89218c

Browse files
Move role picker before scanner and complete localization (#95)
1 parent 689eff7 commit e89218c

29 files changed

Lines changed: 2445 additions & 182 deletions

RemoteCam/AppDelegate.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import Photos
1212

1313
@UIApplicationMain
1414
class AppDelegate: UIResponder, UIApplicationDelegate {
15-
1615
var window: UIWindow?
1716

1817
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {

RemoteCam/CameraViewController.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,30 @@ public class CameraViewController: UIViewController,
148148
navigationController?.navigationBar.scrollEdgeAppearance = appearance
149149
navigationController?.navigationBar.tintColor = .white
150150

151+
navigationItem.rightBarButtonItem = UIBarButtonItem(
152+
image: UIImage(systemName: "questionmark.circle"),
153+
style: .plain,
154+
target: self,
155+
action: #selector(showHelpModal)
156+
)
157+
151158
orientation = getOrientation()
152159
}
153160

161+
@objc private func showHelpModal() {
162+
let helpView = RemoteShutterHelpView(onDismiss: { [weak self] in
163+
self?.dismiss(animated: true)
164+
})
165+
let hostingController = UIHostingController(rootView: helpView)
166+
hostingController.modalPresentationStyle = .pageSheet
167+
if let sheet = hostingController.sheetPresentationController {
168+
sheet.detents = [.large()]
169+
sheet.prefersGrabberVisible = true
170+
sheet.preferredCornerRadius = 20
171+
}
172+
present(hostingController, animated: true)
173+
}
174+
154175
override public func viewDidAppear(_ animated: Bool) {
155176
super.viewDidAppear(animated)
156177
checkPermissionsAndSetupCamera()

RemoteCam/DeviceScannerView.swift

Lines changed: 202 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ struct DeviceScannerView: View {
1717
ZStack {
1818
AppTheme.backgroundGradient
1919

20-
if viewModel.hasPeers {
20+
if viewModel.role == .camera {
21+
cameraWaitingState
22+
} else if viewModel.hasPeers {
2123
peerList
2224
} else {
2325
emptyState
@@ -80,75 +82,159 @@ struct DeviceScannerView: View {
8082
}
8183
}
8284

83-
// MARK: - Empty State
85+
// MARK: - Camera Waiting State
8486

85-
private var emptyState: some View {
86-
VStack(spacing: 24) {
87-
Spacer()
87+
private var cameraWaitingState: some View {
88+
ScrollView {
89+
VStack(spacing: 20) {
90+
// Camera icon
91+
ZStack {
92+
Circle()
93+
.fill(AppTheme.accent.opacity(0.12))
94+
.frame(width: 100, height: 100)
95+
96+
Circle()
97+
.strokeBorder(AppTheme.accent.opacity(0.25), lineWidth: 1.5)
98+
.frame(width: 100, height: 100)
99+
100+
Image(systemName: "camera.fill")
101+
.font(.system(size: 40, weight: .medium))
102+
.foregroundColor(AppTheme.accent)
103+
}
104+
.padding(.top, 24)
105+
106+
VStack(spacing: 8) {
107+
Text(NSLocalizedString("Camera Mode", comment: ""))
108+
.font(.title2)
109+
.fontWeight(.bold)
110+
.foregroundColor(.primary)
111+
112+
Text(NSLocalizedString("On another device, open Remote Shutter and select Remote. This camera will appear in their device list.", comment: ""))
113+
.font(.subheadline)
114+
.foregroundColor(.secondary)
115+
.multilineTextAlignment(.center)
116+
.lineLimit(nil)
117+
.fixedSize(horizontal: false, vertical: true)
118+
}
119+
.padding(.horizontal, 20)
88120

89-
// QR code
90-
VStack(spacing: 16) {
91-
if let qrImage = qrCodeImage {
92-
Image(uiImage: qrImage)
93-
.interpolation(.none)
94-
.resizable()
95-
.aspectRatio(contentMode: .fit)
96-
.frame(width: 160, height: 160)
97-
.padding(12)
98-
.background(.ultraThinMaterial)
99-
.clipShape(RoundedRectangle(cornerRadius: 18))
100-
.overlay(
101-
RoundedRectangle(cornerRadius: 18)
102-
.strokeBorder(AppTheme.glassBorder, lineWidth: 0.5)
103-
)
104-
} else {
105-
Image("AppLogo")
106-
.resizable()
107-
.aspectRatio(contentMode: .fit)
108-
.frame(width: 80, height: 80)
109-
.clipShape(RoundedRectangle(cornerRadius: 18))
110-
.shadow(color: .black.opacity(0.1), radius: 8, y: 4)
121+
// Status
122+
statusBadge
123+
124+
// Actions
125+
VStack(spacing: 12) {
126+
if !viewModel.hasLocalNetworkAccess {
127+
settingsButton
128+
} else if viewModel.isScanning {
129+
cameraAdvertisingIndicator
130+
goOfflineButton
131+
} else {
132+
goOnlineButton
133+
}
134+
135+
shareButton
111136
}
137+
.padding(.horizontal, 20)
112138

113-
Text(NSLocalizedString("You need at least 2 devices running Remote Shutter", comment: ""))
114-
.font(.subheadline)
115-
.foregroundColor(.secondary)
116-
.multilineTextAlignment(.center)
117-
.padding(.horizontal, 40)
139+
// QR code + tip
140+
qrCodeSection
118141
}
142+
}
143+
}
119144

120-
// Status
121-
statusBadge
145+
// MARK: - Empty State
122146

123-
// Actions
124-
VStack(spacing: 12) {
125-
if !viewModel.hasLocalNetworkAccess {
126-
settingsButton
127-
} else if viewModel.isScanning {
128-
scanningIndicator
129-
stopScanButton
130-
} else {
131-
startScanButton
147+
private var emptyState: some View {
148+
ScrollView {
149+
VStack(spacing: 20) {
150+
// Remote icon
151+
ZStack {
152+
Circle()
153+
.fill(AppTheme.secondary.opacity(0.12))
154+
.frame(width: 100, height: 100)
155+
156+
Circle()
157+
.strokeBorder(AppTheme.secondary.opacity(0.25), lineWidth: 1.5)
158+
.frame(width: 100, height: 100)
159+
160+
Image(systemName: "antenna.radiowaves.left.and.right")
161+
.font(.system(size: 40, weight: .medium))
162+
.foregroundColor(AppTheme.secondary)
163+
}
164+
.padding(.top, 24)
165+
166+
VStack(spacing: 8) {
167+
Text(NSLocalizedString("Remote Mode", comment: ""))
168+
.font(.title2)
169+
.fontWeight(.bold)
170+
.foregroundColor(.primary)
171+
172+
Text(NSLocalizedString("You need at least 2 devices running Remote Shutter", comment: ""))
173+
.font(.subheadline)
174+
.foregroundColor(.secondary)
175+
.multilineTextAlignment(.center)
176+
.lineLimit(nil)
177+
.fixedSize(horizontal: false, vertical: true)
132178
}
179+
.padding(.horizontal, 20)
180+
181+
// Status
182+
statusBadge
183+
184+
// Actions
185+
VStack(spacing: 12) {
186+
if !viewModel.hasLocalNetworkAccess {
187+
settingsButton
188+
} else if viewModel.isScanning {
189+
scanningIndicator
190+
stopScanButton
191+
} else {
192+
startScanButton
193+
}
133194

134-
shareButton
195+
shareButton
196+
}
197+
.padding(.horizontal, 20)
198+
199+
// QR code + tip
200+
qrCodeSection
135201
}
136-
.padding(.horizontal, 20)
202+
}
203+
}
137204

138-
Spacer()
205+
// MARK: - QR Code Section
206+
207+
private var qrCodeSection: some View {
208+
VStack(spacing: 12) {
209+
if let qrImage = qrCodeImage {
210+
Image(uiImage: qrImage)
211+
.interpolation(.none)
212+
.resizable()
213+
.aspectRatio(contentMode: .fit)
214+
.frame(width: 120, height: 120)
215+
.padding(8)
216+
.background(.ultraThinMaterial)
217+
.clipShape(RoundedRectangle(cornerRadius: 14))
218+
.overlay(
219+
RoundedRectangle(cornerRadius: 14)
220+
.strokeBorder(AppTheme.glassBorder, lineWidth: 0.5)
221+
)
222+
}
139223

140-
// Tip
141-
HStack(spacing: 8) {
224+
HStack(alignment: .top, spacing: 8) {
142225
Image(systemName: "qrcode")
143226
.font(.caption)
144227
.foregroundColor(AppTheme.accent)
228+
.padding(.top, 2)
145229
Text(NSLocalizedString("Scan the QR code on another device to download Remote Shutter", comment: ""))
146230
.font(.caption)
147231
.foregroundColor(.secondary)
232+
.lineLimit(nil)
233+
.fixedSize(horizontal: false, vertical: true)
148234
}
149235
.padding(.horizontal, 20)
150-
.padding(.bottom, 20)
151236
}
237+
.padding(.bottom, 20)
152238
}
153239

154240
// MARK: - Components
@@ -225,12 +311,16 @@ struct DeviceScannerView: View {
225311
private var scanningIndicator: some View {
226312
HStack(spacing: 12) {
227313
ProgressView()
228-
Text(NSLocalizedString("Scanning for nearby devices...", comment: ""))
314+
Text(NSLocalizedString("Scanning for nearby cameras...", comment: ""))
229315
.font(.subheadline)
230316
.foregroundColor(.secondary)
317+
.lineLimit(nil)
318+
.fixedSize(horizontal: false, vertical: true)
231319
}
232320
.frame(maxWidth: .infinity)
233-
.frame(height: 54)
321+
.frame(minHeight: 54)
322+
.padding(.vertical, 8)
323+
.padding(.horizontal, 16)
234324
.background(.ultraThinMaterial)
235325
.clipShape(RoundedRectangle(cornerRadius: 14))
236326
.overlay(
@@ -239,6 +329,67 @@ struct DeviceScannerView: View {
239329
)
240330
}
241331

332+
private var cameraAdvertisingIndicator: some View {
333+
HStack(spacing: 12) {
334+
ProgressView()
335+
Text(NSLocalizedString("Waiting for a remote to connect...", comment: ""))
336+
.font(.subheadline)
337+
.foregroundColor(.secondary)
338+
.lineLimit(nil)
339+
.fixedSize(horizontal: false, vertical: true)
340+
}
341+
.frame(maxWidth: .infinity)
342+
.frame(minHeight: 54)
343+
.padding(.vertical, 8)
344+
.padding(.horizontal, 16)
345+
.background(.ultraThinMaterial)
346+
.clipShape(RoundedRectangle(cornerRadius: 14))
347+
.overlay(
348+
RoundedRectangle(cornerRadius: 14)
349+
.strokeBorder(AppTheme.glassBorder, lineWidth: 0.5)
350+
)
351+
}
352+
353+
private var goOnlineButton: some View {
354+
Button(action: onStartScanning) {
355+
HStack {
356+
Image(systemName: "antenna.radiowaves.left.and.right")
357+
.font(.headline)
358+
Text(NSLocalizedString("Go Online", comment: ""))
359+
.font(.headline)
360+
}
361+
.foregroundColor(.black)
362+
.frame(maxWidth: .infinity)
363+
.frame(height: 54)
364+
.background(
365+
LinearGradient(
366+
colors: [AppTheme.accent, AppTheme.accentLight],
367+
startPoint: .leading,
368+
endPoint: .trailing
369+
)
370+
)
371+
.clipShape(RoundedRectangle(cornerRadius: 14))
372+
.shadow(color: AppTheme.accent.opacity(0.3), radius: 8, y: 4)
373+
}
374+
}
375+
376+
private var goOfflineButton: some View {
377+
Button(action: onStopScanning) {
378+
HStack {
379+
Image(systemName: "stop.fill")
380+
.font(.subheadline)
381+
Text(NSLocalizedString("Go Offline", comment: ""))
382+
.font(.subheadline)
383+
.fontWeight(.medium)
384+
}
385+
.foregroundColor(.secondary)
386+
.frame(maxWidth: .infinity)
387+
.frame(height: 44)
388+
.background(Color(.secondarySystemGroupedBackground))
389+
.clipShape(RoundedRectangle(cornerRadius: 12))
390+
}
391+
}
392+
242393
private var settingsButton: some View {
243394
Button(action: onOpenSettings) {
244395
HStack {

0 commit comments

Comments
 (0)