feat: Ipad app production readiness, Colony orchestration, Social posting

This commit is contained in:
Sayan Datta
2026-05-03 18:28:04 +05:30
parent acfc602157
commit 6c93e31741
86 changed files with 20349 additions and 1655 deletions

View File

@@ -1,21 +1,24 @@
import SwiftUI
import LocalAuthentication
import UIKit
enum AppSection: String, CaseIterable, Hashable, Identifiable {
var id: String { rawValue }
case dashboard = "Dashboard"
case clients = "Clients"
case imports = "Imports"
case communications = "Communications"
case calendar = "Calendar"
case oracle = "Oracle"
case sentinel = "Sentinel"
case inventory = "Inventory"
case settings = "Settings"
var displayTitle: String {
rawValue
}
var dockTitle: String {
switch self {
case .sentinel:
return SentinelScope.navigationTitle
case .communications:
return "Comms"
default:
return rawValue
}
@@ -25,11 +28,8 @@ enum AppSection: String, CaseIterable, Hashable, Identifiable {
switch self {
case .dashboard: return "square.grid.2x2"
case .clients: return "person.text.rectangle"
case .imports: return "tray.and.arrow.down"
case .communications: return "phone.connection"
case .calendar: return "calendar.badge.clock"
case .oracle: return "message.and.waveform"
case .sentinel: return "person.crop.rectangle"
case .inventory: return "shippingbox"
case .settings: return "gearshape"
}
@@ -39,11 +39,8 @@ enum AppSection: String, CaseIterable, Hashable, Identifiable {
switch self {
case .dashboard: return VelocityTheme.accent
case .clients: return Color(red: 0.22, green: 0.78, blue: 0.96)
case .imports: return Color(red: 0.94, green: 0.70, blue: 0.25)
case .communications: return Color(red: 0.19, green: 0.84, blue: 0.63)
case .calendar: return Color(red: 0.96, green: 0.67, blue: 0.16)
case .oracle: return Color(red: 0.13, green: 0.83, blue: 0.93) // cyan
case .sentinel: return Color(red: 0.60, green: 0.57, blue: 0.99) // indigo
case .inventory: return VelocityTheme.warning
case .settings: return VelocityTheme.mutedFg
}
@@ -51,102 +48,233 @@ enum AppSection: String, CaseIterable, Hashable, Identifiable {
}
struct ContentView: View {
@State private var selectedSection: AppSection? = .dashboard
@State private var selectedSection: AppSection = .dashboard
@State private var session = SessionStore.shared
@State private var store = AppStore.shared
@State private var isOraclePresented = false
@State private var dockFocusedSection: AppSection?
@State private var isPrivacyLocked = true
@State private var isAuthenticating = false
@State private var privacyMessage = "Unlock Velocity"
@Environment(\.scenePhase) private var scenePhase
@Namespace private var dockSelectionNamespace
var body: some View {
Group {
if session.isConfigured {
NavigationSplitView(columnVisibility: .constant(.all)) {
sidebarContent
} detail: {
ZStack(alignment: .bottom) {
detailContent
floatingNavigationPill
.padding(.horizontal, 24)
.padding(.bottom, 24)
}
.overlay(alignment: .bottomTrailing) {
OracleFloatingOrb(alertCount: oracleAlertCount) {
isOraclePresented = true
}
.padding(.trailing, 28)
.padding(.bottom, selectedSection == .clients ? 154 : 112)
}
.overlay(alignment: .top) {
OfflineSyncGlow(isActive: hasPendingSync)
}
.overlay(alignment: .top) {
VaultShareToast(
message: store.vaultShareMessage,
error: store.vaultShareError
) {
withAnimation(.interactiveSpring(response: 0.35, dampingFraction: 0.86)) {
store.vaultShareMessage = nil
store.vaultShareError = nil
}
}
.padding(.top, 18)
}
.overlay {
if store.isShowroomModeEnabled, store.errorMessage != nil {
ShowroomAmbientFallback()
.transition(.opacity.animation(.interactiveSpring(response: 0.55, dampingFraction: 0.9)))
}
}
.overlay {
if isPrivacyLocked {
PrivacyLockOverlay(
message: privacyMessage,
isAuthenticating: isAuthenticating
) {
authenticateSession()
}
.transition(.opacity.animation(.interactiveSpring(response: 0.35, dampingFraction: 0.88)))
}
}
.sheet(isPresented: $isOraclePresented) {
OracleConciergeSheet()
.presentationDetents([.medium, .large])
.presentationDragIndicator(.visible)
}
.navigationSplitViewStyle(.balanced)
} else {
ConfigurationGateView()
}
}
}
// MARK: Sidebar
private var sidebarContent: some View {
ZStack {
VelocityTheme.sidebarBg.ignoresSafeArea()
VStack(spacing: 0) {
// App title
HStack(spacing: 10) {
ZStack {
RoundedRectangle(cornerRadius: 9)
.fill(VelocityTheme.accent.opacity(0.18))
.frame(width: 34, height: 34)
Image(systemName: "bolt.fill")
.font(.system(size: 15, weight: .semibold))
.foregroundStyle(VelocityTheme.accent)
}
VStack(alignment: .leading, spacing: 1) {
Text("Velocity")
.font(.system(size: 16, weight: .semibold))
.foregroundStyle(VelocityTheme.foreground)
Text("Project Velocity · v1.1")
.font(.system(size: 10))
.foregroundStyle(VelocityTheme.mutedFg)
}
Spacer()
.onAppear {
authenticateSession()
}
.onChange(of: scenePhase) { _, phase in
switch phase {
case .background, .inactive:
withAnimation(.interactiveSpring(response: 0.35, dampingFraction: 0.9)) {
isPrivacyLocked = true
privacyMessage = "Velocity is locked"
}
.padding(.horizontal, 16)
.padding(.top, 20)
.padding(.bottom, 16)
Divider()
.background(VelocityTheme.borderSubtle)
.padding(.bottom, 8)
// Nav items
VStack(spacing: 2) {
ForEach(AppSection.allCases) { section in
Button {
selectedSection = section
} label: {
SidebarRow(section: section, isSelected: selectedSection == section)
}
.buttonStyle(.plain)
.accessibilityLabel(section.displayTitle)
.accessibilityAddTraits(selectedSection == section ? [.isSelected] : [])
}
case .active:
if isPrivacyLocked {
authenticateSession()
}
@unknown default:
break
}
}
}
private var floatingNavigationPill: some View {
VStack(spacing: 6) {
HStack(alignment: .bottom, spacing: 12) {
ForEach(AppSection.allCases) { section in
let isSelected = selectedSection == section
let isFocused = dockFocusedSection == section
Button {
UIImpactFeedbackGenerator(style: .soft).impactOccurred()
withAnimation(.interactiveSpring(response: 0.46, dampingFraction: 0.78)) {
selectedSection = section
dockFocusedSection = section
}
hideDockTooltipAfterTap(for: section)
} label: {
dockItem(for: section, isSelected: isSelected, isFocused: isFocused)
}
.buttonStyle(.plain)
.accessibilityLabel(section.dockTitle)
.accessibilityAddTraits(isSelected ? [.isSelected] : [])
.onHover { hovering in
withAnimation(.interactiveSpring(response: 0.34, dampingFraction: 0.76)) {
dockFocusedSection = hovering ? section : nil
}
}
}
}
.padding(.horizontal, 13)
.padding(.top, 9)
.padding(.bottom, 8)
}
.background(
RoundedRectangle(cornerRadius: 30, style: .continuous)
.fill(.ultraThinMaterial)
.background(
RoundedRectangle(cornerRadius: 30, style: .continuous)
.fill(Color.black.opacity(0.34))
.blur(radius: 0.5)
)
.shadow(color: Color.black.opacity(0.42), radius: 28, y: 18)
)
.overlay(
RoundedRectangle(cornerRadius: 30, style: .continuous)
.stroke(Color.white.opacity(0.16), lineWidth: 1)
)
.frame(maxWidth: .infinity, alignment: .center)
}
@ViewBuilder
private func dockItem(for section: AppSection, isSelected: Bool, isFocused: Bool) -> some View {
VStack(spacing: 7) {
ZStack {
RoundedRectangle(cornerRadius: 16, style: .continuous)
.fill(
LinearGradient(
colors: [
section.accentColor.opacity(isSelected ? 0.34 : 0.18),
Color.white.opacity(isSelected ? 0.12 : 0.05),
],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
.overlay(
RoundedRectangle(cornerRadius: 16, style: .continuous)
.stroke(Color.white.opacity(isSelected ? 0.26 : 0.12), lineWidth: 1)
)
.shadow(
color: section.accentColor.opacity(isSelected || isFocused ? 0.32 : 0.10),
radius: isSelected || isFocused ? 16 : 7,
y: isSelected || isFocused ? 8 : 3
)
.if(isSelected) { view in
view.matchedGeometryEffect(id: "selectedDockTile", in: dockSelectionNamespace)
}
Image(systemName: section.systemImage)
.font(.system(size: isFocused ? 24 : (isSelected ? 22 : 19), weight: .semibold))
.foregroundStyle(isSelected ? VelocityTheme.foreground : section.accentColor)
}
.frame(width: 50, height: 50)
.scaleEffect(isFocused ? 1.34 : (isSelected ? 1.16 : 1.0), anchor: .bottom)
.offset(y: isFocused ? -13 : (isSelected ? -5 : 0))
Circle()
.fill(isSelected ? section.accentColor.opacity(0.92) : Color.clear)
.frame(width: 5, height: 5)
.shadow(color: section.accentColor.opacity(isSelected ? 0.45 : 0), radius: 5)
}
.frame(width: 58, height: 70, alignment: .bottom)
.overlay(alignment: .top) {
if isFocused {
dockTooltip(section.dockTitle)
.offset(y: -46)
.transition(
.asymmetric(
insertion: .scale(scale: 0.88, anchor: .bottom).combined(with: .opacity),
removal: .opacity
)
)
}
}
.contentShape(Rectangle())
.animation(.interactiveSpring(response: 0.44, dampingFraction: 0.76), value: isSelected)
.animation(.interactiveSpring(response: 0.32, dampingFraction: 0.70), value: isFocused)
}
private func dockTooltip(_ title: String) -> some View {
VStack(spacing: 0) {
Text(title)
.font(.system(size: 12, weight: .semibold))
.foregroundStyle(VelocityTheme.foreground)
.lineLimit(1)
.padding(.horizontal, 11)
.padding(.vertical, 6)
.background(
RoundedRectangle(cornerRadius: 6, style: .continuous)
.fill(Color(red: 0.13, green: 0.13, blue: 0.13).opacity(0.96))
.overlay(
RoundedRectangle(cornerRadius: 6, style: .continuous)
.stroke(Color.white.opacity(0.18), lineWidth: 1)
)
)
Triangle()
.fill(Color(red: 0.13, green: 0.13, blue: 0.13).opacity(0.96))
.frame(width: 12, height: 7)
}
.fixedSize()
.shadow(color: Color.black.opacity(0.42), radius: 10, y: 6)
}
private func hideDockTooltipAfterTap(for section: AppSection) {
Task {
try? await Task.sleep(nanoseconds: 1_200_000_000)
await MainActor.run {
guard dockFocusedSection == section else { return }
withAnimation(.interactiveSpring(response: 0.32, dampingFraction: 0.78)) {
dockFocusedSection = nil
}
.padding(.horizontal, 8)
Spacer()
// User footer
Divider()
.background(VelocityTheme.borderSubtle)
HStack(spacing: 10) {
ZStack {
RoundedRectangle(cornerRadius: 8)
.fill(VelocityTheme.accent)
.frame(width: 32, height: 32)
Text(operatorInitials)
.font(.system(size: 11, weight: .bold))
.foregroundStyle(.white)
}
VStack(alignment: .leading, spacing: 2) {
Text(operatorName)
.font(.system(size: 12, weight: .medium))
.foregroundStyle(VelocityTheme.foreground)
Text(session.authModeDescription)
.font(.system(size: 10))
.foregroundStyle(VelocityTheme.mutedFg)
}
Spacer()
}
.padding(16)
}
}
.navigationTitle("")
.toolbar(.hidden, for: .navigationBar)
}
// MARK: Detail
@@ -156,16 +284,17 @@ struct ContentView: View {
Group {
switch selectedSection {
case .dashboard: DashboardView()
case .dashboard:
DashboardView { section in
withAnimation(.interactiveSpring(response: 0.46, dampingFraction: 0.82)) {
selectedSection = section
}
}
case .clients: ClientsView()
case .imports: ImportsView()
case .communications: CommunicationsView()
case .calendar: CalendarView()
case .oracle: OracleView()
case .sentinel: SentinelView()
case .inventory: InventoryView()
case .settings: SettingsView()
case .none: DashboardView()
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
@@ -185,37 +314,253 @@ struct ContentView: View {
let initials = parts.prefix(2).compactMap(\.first)
return initials.isEmpty ? "VO" : String(initials)
}
private var hasPendingSync: Bool {
!store.isShowroomModeEnabled && (!store.pendingSyncTaskIDs.isEmpty || !store.pendingSyncCalendarEventIDs.isEmpty)
}
private var oracleAlertCount: Int {
guard let alertSnapshot = store.alertSnapshot else { return 0 }
return alertSnapshot.pendingInsights + alertSnapshot.pendingTranscriptions + alertSnapshot.upcomingCalendarEvents24h
}
private func authenticateSession() {
guard session.isConfigured, !isAuthenticating else { return }
isAuthenticating = true
privacyMessage = "Authenticating..."
let context = LAContext()
context.localizedCancelTitle = "Lock"
var error: NSError?
let policy: LAPolicy = context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
? .deviceOwnerAuthenticationWithBiometrics
: .deviceOwnerAuthentication
guard context.canEvaluatePolicy(policy, error: &error) else {
isAuthenticating = false
privacyMessage = error?.localizedDescription ?? "Device authentication is unavailable."
return
}
context.evaluatePolicy(policy, localizedReason: "Unlock Project Velocity") { success, authError in
Task { @MainActor in
isAuthenticating = false
withAnimation(.interactiveSpring(response: 0.38, dampingFraction: 0.86)) {
isPrivacyLocked = !success
}
privacyMessage = success
? "Unlocked"
: (authError?.localizedDescription ?? "Authentication failed.")
}
}
}
}
// MARK: Sidebar Row
private struct SidebarRow: View {
let section: AppSection
let isSelected: Bool
private extension View {
@ViewBuilder
func `if`<Content: View>(_ condition: Bool, transform: (Self) -> Content) -> some View {
if condition {
transform(self)
} else {
self
}
}
}
private struct Triangle: Shape {
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: rect.midX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.minX, y: rect.minY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
path.closeSubpath()
return path
}
}
private struct OracleFloatingOrb: View {
let alertCount: Int
let action: () -> Void
@State private var isPressed = false
var body: some View {
HStack(spacing: 11) {
Image(systemName: section.systemImage)
.font(.system(size: 14, weight: .medium))
.foregroundStyle(isSelected ? section.accentColor : VelocityTheme.mutedFg)
.frame(width: 20)
Text(section.displayTitle)
.font(.system(size: 14, weight: isSelected ? .semibold : .regular))
.foregroundStyle(isSelected ? VelocityTheme.foreground : VelocityTheme.mutedFg)
Spacer()
Button {
UIImpactFeedbackGenerator(style: .soft).impactOccurred()
action()
} label: {
ZStack {
Circle()
.fill(.ultraThinMaterial)
.frame(width: 64, height: 64)
.overlay(
Circle()
.stroke(Color(red: 0.13, green: 0.83, blue: 0.93).opacity(0.55), lineWidth: 1)
)
.shadow(color: Color(red: 0.13, green: 0.83, blue: 0.93).opacity(0.35), radius: isPressed ? 10 : 18)
Image(systemName: "sparkles")
.font(.system(size: 24, weight: .semibold))
.foregroundStyle(Color(red: 0.68, green: 0.95, blue: 1.0))
if alertCount > 0 {
Text(alertCount > 99 ? "99+" : "\(alertCount)")
.font(.system(size: 9, weight: .bold))
.foregroundStyle(.white)
.padding(.horizontal, 6)
.padding(.vertical, 3)
.background(Capsule().fill(VelocityTheme.danger))
.offset(x: 22, y: -22)
.transition(.scale.combined(with: .opacity))
}
}
}
.padding(.horizontal, 12)
.padding(.vertical, 9)
.background(
RoundedRectangle(cornerRadius: 10)
.fill(isSelected ? section.accentColor.opacity(0.12) : .clear)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(isSelected ? section.accentColor.opacity(0.25) : .clear, lineWidth: 1)
)
.buttonStyle(.plain)
.accessibilityLabel("Open Oracle")
.simultaneousGesture(
DragGesture(minimumDistance: 0)
.onChanged { _ in
withAnimation(.interactiveSpring(response: 0.24, dampingFraction: 0.8)) {
isPressed = true
}
}
.onEnded { _ in
withAnimation(.interactiveSpring(response: 0.28, dampingFraction: 0.82)) {
isPressed = false
}
}
)
.contentShape(Rectangle())
}
}
private struct OfflineSyncGlow: View {
let isActive: Bool
var body: some View {
Rectangle()
.fill(VelocityTheme.warning.opacity(isActive ? 0.28 : 0))
.frame(height: isActive ? 5 : 0)
.shadow(color: VelocityTheme.warning.opacity(isActive ? 0.65 : 0), radius: 14, y: 5)
.animation(.interactiveSpring(response: 0.42, dampingFraction: 0.9), value: isActive)
.ignoresSafeArea(edges: .top)
}
}
private struct VaultShareToast: View {
let message: String?
let error: String?
let dismiss: () -> Void
var body: some View {
if let text = message ?? error {
HStack(spacing: 10) {
Image(systemName: error == nil ? "link.circle.fill" : "exclamationmark.triangle.fill")
.foregroundStyle(error == nil ? VelocityTheme.success : VelocityTheme.warning)
Text(text)
.font(.system(size: 12, weight: .semibold))
.foregroundStyle(VelocityTheme.foreground)
Button {
dismiss()
} label: {
Image(systemName: "xmark")
.font(.system(size: 10, weight: .bold))
.foregroundStyle(VelocityTheme.mutedFg)
}
.buttonStyle(.plain)
}
.padding(.horizontal, 14)
.padding(.vertical, 9)
.background(
Capsule()
.fill(.ultraThinMaterial)
.overlay(Capsule().stroke(Color.white.opacity(0.16), lineWidth: 1))
)
.transition(.move(edge: .top).combined(with: .opacity))
}
}
}
private struct ShowroomAmbientFallback: View {
var body: some View {
ZStack {
LinearGradient(
colors: [
Color.black.opacity(0.88),
Color(red: 0.045, green: 0.055, blue: 0.075).opacity(0.96),
Color.black.opacity(0.92),
],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
.ignoresSafeArea()
VStack(spacing: 14) {
Image(systemName: "building.2.crop.circle")
.font(.system(size: 58, weight: .light))
.foregroundStyle(.white.opacity(0.72))
Text("Velocity")
.font(.system(size: 34, weight: .semibold))
.foregroundStyle(.white)
Text("Preparing the showroom view")
.font(.system(size: 13, weight: .medium))
.foregroundStyle(.white.opacity(0.58))
}
}
}
}
private struct PrivacyLockOverlay: View {
let message: String
let isAuthenticating: Bool
let unlock: () -> Void
var body: some View {
ZStack {
Rectangle()
.fill(.ultraThinMaterial)
.ignoresSafeArea()
Color.black.opacity(0.62)
.ignoresSafeArea()
VStack(spacing: 16) {
Image(systemName: "faceid")
.font(.system(size: 50, weight: .light))
.foregroundStyle(VelocityTheme.foreground)
Text("Velocity")
.font(.system(size: 30, weight: .semibold, design: .default))
.foregroundStyle(VelocityTheme.foreground)
Text(message)
.font(.system(size: 13, weight: .medium))
.foregroundStyle(VelocityTheme.mutedFg)
Button {
unlock()
} label: {
HStack(spacing: 8) {
if isAuthenticating {
ProgressView()
.progressViewStyle(CircularProgressViewStyle(tint: .white))
} else {
Image(systemName: "lock.open")
}
Text(isAuthenticating ? "Unlocking" : "Unlock")
}
.font(.system(size: 14, weight: .semibold))
.foregroundStyle(.white)
.padding(.horizontal, 20)
.padding(.vertical, 11)
.background(Capsule().fill(VelocityTheme.accent))
}
.buttonStyle(.plain)
.disabled(isAuthenticating)
}
.padding(28)
.background(
RoundedRectangle(cornerRadius: 26)
.fill(Color.black.opacity(0.32))
.overlay(
RoundedRectangle(cornerRadius: 26)
.stroke(Color.white.opacity(0.16), lineWidth: 1)
)
)
}
}
}