Skip to content

Commit 4d891d5

Browse files
committed
feat: add connection button/state on all views
1 parent e83f67b commit 4d891d5

4 files changed

Lines changed: 79 additions & 108 deletions

File tree

AllSpark-ios/AppConstants.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ enum AppConstants {
2626
// Font Sizing
2727
static let fontSizeGateIcon: CGFloat = 56.0
2828
static let fontSizeTimer: CGFloat = 20.0
29+
static let fontSizeStandard: CGFloat = 16.0
2930
static let fontSizeModes: CGFloat = 11.0
3031

3132
// Spacing & Padding
@@ -43,6 +44,7 @@ enum AppConstants {
4344
static let paddingMicro: CGFloat = 2.0
4445

4546
static let offsetTrailingStatus: CGFloat = -80.0
47+
static let offsetSecureBadge: CGFloat = 4.0
4648

4749
// Corner Radii
4850
static let cornerRadiusSwitch: CGFloat = 25.0
@@ -78,6 +80,7 @@ enum AppConstants {
7880

7981
// Actions
8082
static let buttonPrimary = Color.blue
83+
static let buttonInfo = Color.teal
8184
static let buttonDestructive = Color.red
8285
static let actionToggleOn = Color.blue
8386
static let actionToggleOff = Color.red

AllSpark-ios/CameraViewController.swift

Lines changed: 16 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import UIKit
22
import AVFoundation
33
import Vision
44
import CoreImage
5+
import SwiftUI
56
import Combine
67

78
class CameraViewController: UIViewController, UINavigationControllerDelegate {
@@ -67,8 +68,7 @@ class CameraViewController: UIViewController, UINavigationControllerDelegate {
6768
private var captureModesLabel: UILabel!
6869
private var recordingTimer: Timer?
6970
private var recordingDuration: TimeInterval = 0
70-
private var connectionStatusIcon: UIButton!
71-
private var connectionSecureIcon: UIButton!
71+
private var connectionHostingController: UIHostingController<ConnectionStatusButton>!
7272

7373
override func viewDidLoad() {
7474
super.viewDidLoad()
@@ -78,13 +78,12 @@ class CameraViewController: UIViewController, UINavigationControllerDelegate {
7878

7979
setupSwitchCameraButton()
8080
setupTimerLabel()
81-
setupConnectionStatusIcon()
81+
setupConnectionStatusButton()
8282
setupCamera()
8383
setupPrivacyFiltering()
8484

8585
// ConnectionManager is a singleton, so we just ensure it's connected and observe it
8686
ConnectionManager.shared.connect()
87-
setupConnectionObserver()
8887
setupCommandObserver()
8988
}
9089

@@ -140,7 +139,7 @@ class CameraViewController: UIViewController, UINavigationControllerDelegate {
140139
let label = UILabel()
141140
label.text = "Initializing Privacy Models..."
142141
label.textColor = .white
143-
label.font = .systemFont(ofSize: 16, weight: .medium)
142+
label.font = .systemFont(ofSize: AppConstants.UI.fontSizeStandard, weight: .medium)
144143
label.translatesAutoresizingMaskIntoConstraints = false
145144

146145
let stack = UIStackView(arrangedSubviews: [indicator, label])
@@ -261,94 +260,20 @@ class CameraViewController: UIViewController, UINavigationControllerDelegate {
261260
])
262261
}
263262

264-
private func setupConnectionStatusIcon() {
265-
connectionStatusIcon = UIButton(type: .system)
266-
connectionStatusIcon.translatesAutoresizingMaskIntoConstraints = false
267-
connectionStatusIcon.tintColor = .white
268-
connectionStatusIcon.backgroundColor = AppConstants.Colors.backgroundBaseUI.withAlphaComponent(AppConstants.UI.buttonBackgroundAlpha)
269-
connectionStatusIcon.layer.cornerRadius = AppConstants.UI.cornerRadiusLarge
270-
connectionStatusIcon.isUserInteractionEnabled = false // Disable interaction, just display
271-
272-
view.addSubview(connectionStatusIcon)
273-
274-
NSLayoutConstraint.activate([
275-
connectionStatusIcon.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: AppConstants.UI.paddingStandard),
276-
connectionStatusIcon.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: AppConstants.UI.offsetTrailingStatus),
277-
connectionStatusIcon.widthAnchor.constraint(equalToConstant: AppConstants.UI.buttonSizeMedium),
278-
connectionStatusIcon.heightAnchor.constraint(equalToConstant: AppConstants.UI.buttonSizeMedium)
279-
])
280-
281-
// Setup lock icon overlay for secure connection indicator
282-
connectionSecureIcon = UIButton(type: .system)
283-
connectionSecureIcon.translatesAutoresizingMaskIntoConstraints = false
284-
connectionSecureIcon.tintColor = .systemGreen
285-
connectionSecureIcon.isUserInteractionEnabled = false // Disable interaction, just display
286-
connectionSecureIcon.isHidden = true // Initially hidden
287-
288-
if let lockImage = UIImage(systemName: "lock.fill") {
289-
let config = UIImage.SymbolConfiguration(pointSize: AppConstants.UI.iconSizeSecurePoint, weight: .semibold, scale: .default)
290-
connectionSecureIcon.setImage(lockImage.withConfiguration(config), for: .normal)
291-
}
292-
293-
view.addSubview(connectionSecureIcon)
294-
263+
private func setupConnectionStatusButton() {
264+
let buttonView = ConnectionStatusButton()
265+
connectionHostingController = UIHostingController(rootView: buttonView)
266+
connectionHostingController.view.translatesAutoresizingMaskIntoConstraints = false
267+
connectionHostingController.view.backgroundColor = .clear
268+
269+
addChild(connectionHostingController)
270+
view.addSubview(connectionHostingController.view)
271+
connectionHostingController.didMove(toParent: self)
272+
295273
NSLayoutConstraint.activate([
296-
connectionSecureIcon.bottomAnchor.constraint(equalTo: connectionStatusIcon.bottomAnchor, constant: AppConstants.UI.paddingMicro),
297-
connectionSecureIcon.trailingAnchor.constraint(equalTo: connectionStatusIcon.trailingAnchor, constant: AppConstants.UI.paddingMicro),
298-
connectionSecureIcon.widthAnchor.constraint(equalToConstant: AppConstants.UI.iconSizeSecure),
299-
connectionSecureIcon.heightAnchor.constraint(equalToConstant: AppConstants.UI.iconSizeSecure)
274+
connectionHostingController.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: AppConstants.UI.paddingStandard),
275+
connectionHostingController.view.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: AppConstants.UI.offsetTrailingStatus)
300276
])
301-
302-
updateConnectionStatusIcon()
303-
}
304-
305-
private func updateConnectionStatusIcon() {
306-
DispatchQueue.main.async { [weak self] in
307-
guard let self = self else { return }
308-
309-
if ConnectionManager.shared.isConnected {
310-
// Connected - green
311-
if let image = UIImage(systemName: "wifi") {
312-
self.connectionStatusIcon.setImage(image, for: .normal)
313-
self.connectionStatusIcon.tintColor = AppConstants.Colors.statusConnectedUI
314-
}
315-
// Show lock icon if using secure protocol
316-
self.connectionSecureIcon.isHidden = !ConnectionManager.shared.isSecureProtocol
317-
} else if ConnectionManager.shared.isAttemptingConnection {
318-
// Attempting connection - amber/orange
319-
if let image = UIImage(systemName: "wifi") {
320-
self.connectionStatusIcon.setImage(image, for: .normal)
321-
self.connectionStatusIcon.tintColor = AppConstants.Colors.statusConnectingUI
322-
}
323-
// Hide lock icon while attempting
324-
self.connectionSecureIcon.isHidden = true
325-
} else {
326-
// Disconnected - red
327-
if let image = UIImage(systemName: "wifi.slash") {
328-
self.connectionStatusIcon.setImage(image, for: .normal)
329-
self.connectionStatusIcon.tintColor = AppConstants.Colors.statusDisconnectedUI
330-
}
331-
// Hide lock icon when disconnected
332-
self.connectionSecureIcon.isHidden = true
333-
}
334-
}
335-
}
336-
337-
private func setupConnectionObserver() {
338-
// Observe connection state changes
339-
ConnectionManager.shared.$isConnected
340-
.receive(on: DispatchQueue.main)
341-
.sink { [weak self] _ in
342-
self?.updateConnectionStatusIcon()
343-
}
344-
.store(in: &cancellables)
345-
346-
ConnectionManager.shared.$isAttemptingConnection
347-
.receive(on: DispatchQueue.main)
348-
.sink { [weak self] _ in
349-
self?.updateConnectionStatusIcon()
350-
}
351-
.store(in: &cancellables)
352277
}
353278

354279
private func setupCommandObserver() {
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import SwiftUI
2+
3+
struct ConnectionStatusButton: View {
4+
@ObservedObject private var connectionManager = ConnectionManager.shared
5+
6+
var body: some View {
7+
Button(action: {
8+
if connectionManager.isConnected {
9+
connectionManager.disconnect()
10+
} else if !connectionManager.isAttemptingConnection {
11+
connectionManager.connect()
12+
}
13+
}) {
14+
ZStack(alignment: .bottomTrailing) {
15+
Image(systemName: iconName)
16+
.font(.system(size: AppConstants.UI.iconSizeSecure))
17+
.foregroundColor(iconColor)
18+
19+
if connectionManager.isConnected && connectionManager.isSecureProtocol {
20+
Image(systemName: "lock.fill")
21+
.font(.system(size: AppConstants.UI.iconSizeSecurePoint, weight: .bold))
22+
.foregroundColor(AppConstants.Colors.statusConnected)
23+
.offset(x: AppConstants.UI.offsetSecureBadge, y: AppConstants.UI.offsetSecureBadge)
24+
}
25+
}
26+
.frame(width: AppConstants.UI.buttonSizeMedium, height: AppConstants.UI.buttonSizeMedium)
27+
.background(Color(AppConstants.Colors.backgroundBaseUI).opacity(AppConstants.UI.buttonBackgroundAlpha))
28+
.clipShape(Circle())
29+
}
30+
.disabled(connectionManager.isAttemptingConnection)
31+
}
32+
33+
private var iconName: String {
34+
if connectionManager.isConnected {
35+
return "wifi"
36+
} else if connectionManager.isAttemptingConnection {
37+
return "wifi"
38+
} else {
39+
return "wifi.slash"
40+
}
41+
}
42+
43+
private var iconColor: Color {
44+
if connectionManager.isConnected {
45+
return AppConstants.Colors.statusConnected
46+
} else if connectionManager.isAttemptingConnection {
47+
return AppConstants.Colors.statusConnecting
48+
} else {
49+
return AppConstants.Colors.statusDisconnected
50+
}
51+
}
52+
}

AllSpark-ios/SettingsView.swift

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,17 @@ struct SettingsView: View {
2929

3030
Spacer()
3131

32+
ConnectionStatusButton()
33+
3234
Button(action: {
3335
showingStatusInfo = true
3436
}) {
35-
Image(systemName: "info.circle")
36-
.font(.title2)
37-
.foregroundColor(AppConstants.Colors.buttonPrimary)
37+
Image(systemName: "info")
38+
.font(.system(size: AppConstants.UI.iconSizeSecure, weight: .bold))
39+
.foregroundColor(AppConstants.Colors.buttonInfo)
40+
.frame(width: AppConstants.UI.buttonSizeMedium, height: AppConstants.UI.buttonSizeMedium)
41+
.background(Color(AppConstants.Colors.backgroundBaseUI).opacity(AppConstants.UI.buttonBackgroundAlpha))
42+
.clipShape(Circle())
3843
}
3944
}
4045
.padding(.horizontal, AppConstants.UI.paddingStandard)
@@ -118,20 +123,6 @@ struct SettingsView: View {
118123

119124
Toggle("Verify SSL Certificate", isOn: $verifyCertificate)
120125

121-
HStack {
122-
Text("WebSocket")
123-
Spacer()
124-
Button(action: {
125-
if connectionManager.isConnected {
126-
connectionManager.disconnect()
127-
} else {
128-
connectionManager.connect()
129-
}
130-
}) {
131-
Text(connectionManager.isConnected ? "Disconnect" : "Connect")
132-
.foregroundColor(connectionManager.isConnected ? AppConstants.Colors.actionToggleOff : AppConstants.Colors.actionToggleOn)
133-
}
134-
}
135126
}
136127

137128
Section(header: Text("Privacy Filter Mode")) {

0 commit comments

Comments
 (0)