initial commit: Proximity iOS app proposal, full Swift code scaffold, reference backend, testing docs, generated Xcode project
This commit is contained in:
94
Proximity/Views/RevealView.swift
Normal file
94
Proximity/Views/RevealView.swift
Normal file
@@ -0,0 +1,94 @@
|
||||
import SwiftUI
|
||||
|
||||
/// The emotional centerpiece of the app: the **reveal moment**.
|
||||
///
|
||||
/// Two strangers who were physically near each other both chose to be seen.
|
||||
/// Their silhouettes resolve into people. This screen is deliberately
|
||||
/// ceremonial — it's the payoff for being present in the real world, and it
|
||||
/// should feel like it.
|
||||
///
|
||||
/// The interaction is framed as a physical act: you were *near* this person,
|
||||
/// you both *waved*, and now you *see* each other. The copy and motion are
|
||||
/// designed to make this feel significant, not like a notification.
|
||||
struct RevealView: View {
|
||||
@EnvironmentObject private var revealFlow: RevealFlow
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
@State private var isRevealing = false
|
||||
@State private var showProfileImport = false
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 32) {
|
||||
Spacer()
|
||||
|
||||
// The two avatars converging.
|
||||
ZStack {
|
||||
// "You" avatar (left)
|
||||
avatar(systemImage: "person.fill", offset: isRevealing ? -70 : -110)
|
||||
// "Them" avatar (right) — resolves from silhouette to person.
|
||||
avatar(systemImage: "person.fill", offset: isRevealing ? 70 : 110)
|
||||
}
|
||||
.frame(height: 160)
|
||||
|
||||
// Status text
|
||||
VStack(spacing: 12) {
|
||||
Text(isRevealing ? "You both waved." : "You're connected.")
|
||||
.font(.title.bold())
|
||||
Text(isRevealing
|
||||
? "Two people who were near each other chose to be seen."
|
||||
: "You can now say hello.")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
// Actions
|
||||
VStack(spacing: 12) {
|
||||
Button {
|
||||
revealFlow.proceedToConversation()
|
||||
showProfileImport = true
|
||||
} label: {
|
||||
Label("Say hello", systemImage: "bubble.left.and.bubble.right")
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.tint(.green)
|
||||
|
||||
Button("Not now") {
|
||||
dismiss()
|
||||
}
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.padding(.bottom, 24)
|
||||
}
|
||||
.onAppear {
|
||||
// Animate the convergence on appear.
|
||||
withAnimation(.easeInOut(duration: 1.2)) {
|
||||
isRevealing = true
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showProfileImport) {
|
||||
if let connection = revealFlow.revealedConnection {
|
||||
ProfileImportSheet(connection: connection)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func avatar(systemImage: String, offset: CGFloat) -> some View {
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(Color.green.opacity(0.2))
|
||||
.frame(width: 110, height: 110)
|
||||
Image(systemName: systemImage)
|
||||
.font(.system(size: 48))
|
||||
.foregroundColor(.green)
|
||||
}
|
||||
.offset(x: offset)
|
||||
.animation(.easeInOut(duration: 1.2), value: isRevealing)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user