fix(ios): address Pass B warnings from code audit

This commit is contained in:
null 2026-06-20 22:58:11 -05:00
parent cb54ed3079
commit 59c239694a
6 changed files with 133 additions and 177 deletions

View File

@ -85,9 +85,6 @@ struct DateMatchView: View {
.background(Color.closerBackground) .background(Color.closerBackground)
.navigationTitle("Date Ideas") .navigationTitle("Date Ideas")
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.navigationDestination(isPresented: .constant(false)) {
DateBuilderView()
}
} }
private func swipe(_ direction: SwipeDirection) { private func swipe(_ direction: SwipeDirection) {

View File

@ -4,20 +4,26 @@ struct ContentView: View {
@EnvironmentObject var appState: AppState @EnvironmentObject var appState: AppState
var body: some View { var body: some View {
Group { NavigationStack {
switch appState.authState { rootView
case .loading: }
LoadingView(message: "Getting ready...") .environmentObject(appState)
case .unauthenticated: }
OnboardingFlow()
case .authenticated(_, let isAnonymous): @ViewBuilder
if isAnonymous { private var rootView: some View {
CreateProfileView() switch appState.authState {
} else if appState.currentUser?.coupleId == nil { case .loading:
PairPromptView() LoadingView(message: "Getting ready...")
} else { case .unauthenticated:
MainTabView() OnboardingFlow()
} case .authenticated(_, let isAnonymous):
if isAnonymous {
CreateProfileView()
} else if appState.currentUser?.coupleId == nil {
PairPromptView()
} else {
MainTabView()
} }
} }
} }
@ -35,45 +41,35 @@ struct MainTabView: View {
var body: some View { var body: some View {
TabView(selection: $selectedTab) { TabView(selection: $selectedTab) {
NavigationStack { HomeView()
HomeView() .tabItem {
} Label("Home", systemImage: selectedTab == .home ? "house.fill" : "house")
.tabItem { }
Label("Home", systemImage: selectedTab == .home ? "house.fill" : "house") .tag(Tab.home)
}
.tag(Tab.home)
NavigationStack { DailyQuestionView()
DailyQuestionView() .tabItem {
} Label("Today", systemImage: selectedTab == .dailyQuestion ? "heart.fill" : "heart")
.tabItem { }
Label("Today", systemImage: selectedTab == .dailyQuestion ? "heart.fill" : "heart") .tag(Tab.dailyQuestion)
}
.tag(Tab.dailyQuestion)
NavigationStack { PlayHubView()
PlayHubView() .tabItem {
} Label("Play", systemImage: selectedTab == .play ? "play.fill" : "play")
.tabItem { }
Label("Play", systemImage: selectedTab == .play ? "play.fill" : "play") .tag(Tab.play)
}
.tag(Tab.play)
NavigationStack { QuestionPackLibraryView()
QuestionPackLibraryView() .tabItem {
} Label("Packs", systemImage: selectedTab == .questionPacks ? "star.fill" : "star")
.tabItem { }
Label("Packs", systemImage: selectedTab == .questionPacks ? "star.fill" : "star") .tag(Tab.questionPacks)
}
.tag(Tab.questionPacks)
NavigationStack { SettingsView()
SettingsView() .tabItem {
} Label("Settings", systemImage: selectedTab == .settings ? "gearshape.fill" : "gearshape")
.tabItem { }
Label("Settings", systemImage: selectedTab == .settings ? "gearshape.fill" : "gearshape") .tag(Tab.settings)
}
.tag(Tab.settings)
} }
.tint(.closerPrimary) .tint(.closerPrimary)
} }
@ -86,15 +82,13 @@ struct OnboardingFlow: View {
@State private var showSignUp = false @State private var showSignUp = false
var body: some View { var body: some View {
NavigationStack { OnboardingView(showLogin: $showLogin, showSignUp: $showSignUp)
OnboardingView(showLogin: $showLogin, showSignUp: $showSignUp) .navigationDestination(isPresented: $showLogin) {
.navigationDestination(isPresented: $showLogin) { LoginView()
LoginView() }
} .navigationDestination(isPresented: $showSignUp) {
.navigationDestination(isPresented: $showSignUp) { SignUpView()
SignUpView() }
}
}
} }
} }

View File

@ -143,6 +143,12 @@ struct CreateInviteView: View {
Task { Task {
do { do {
// TODO: Move invite creation to createInviteCallable Cloud Function.
// 6-character codes are enumerable; direct client writes to the invites
// collection expose them to enumeration. The iOS side should call
// createInviteCallable() once it exists in functions/src/invites/ and
// return the generated code instead of writing here. Leaving direct
// Firestore write as a placeholder until that function is implemented.
let userId = try FirestoreService.shared.userId() let userId = try FirestoreService.shared.userId()
let code = generateSixCharCode() let code = generateSixCharCode()
self.inviteCode = code self.inviteCode = code
@ -341,6 +347,9 @@ struct EmailInviteView: View {
Task { Task {
do { do {
// TODO: Use createInviteCallable Cloud Function instead of direct
// client writes to the invites collection. Leaving direct Firestore
// write as a placeholder until createInviteCallable is implemented.
let userId = try FirestoreService.shared.userId() let userId = try FirestoreService.shared.userId()
let code = generateSixCharCode() let code = generateSixCharCode()

View File

@ -263,24 +263,8 @@ struct ThisOrThatView: View {
.font(CloserFont.title3) .font(CloserFont.title3)
.foregroundColor(.closerText) .foregroundColor(.closerText)
ForEach(pairs[currentPair].0, pairs[currentPair].1, id: \.self) { option in let options = [pairs[currentPair].0, pairs[currentPair].1]
Button(action: { choose(option) }) { ForEach(options, id: \.self) { option in
Text(option)
.font(CloserFont.body)
.foregroundColor(.closerText)
.frame(maxWidth: .infinity)
.padding()
.background(Color.closerSurface)
.cornerRadius(CloserRadius.large)
.overlay(RoundedRectangle(cornerRadius: CloserRadius.large).stroke(Color.closerDivider))
}
}
Text("or")
.font(CloserFont.body)
.foregroundColor(.closerTextSecondary)
ForEach(pairs[currentPair].1, pairs[currentPair].0, id: \.self) { option in
Button(action: { choose(option) }) { Button(action: { choose(option) }) {
Text(option) Text(option)
.font(CloserFont.body) .font(CloserFont.body)

View File

@ -552,23 +552,25 @@ struct QuestionThreadView: View {
// MARK: - Sample Data // MARK: - Sample Data
// Fallback sample data. IDs must match real Android seed entries so that
// navigation to category/pack/question threads references actual data.
let bundledQuestions: [Question] = [ let bundledQuestions: [Question] = [
Question(id: "q1", text: "What made you smile today?", type: "text", options: nil, scaleMin: nil, scaleMax: nil, categoryId: nil, packId: nil), Question(id: "communication_001", text: "What is one small thing I do that helps you feel heard?", type: "text", options: nil, scaleMin: nil, scaleMax: nil, categoryId: "communication", packId: nil),
Question(id: "q2", text: "What's one thing you appreciate about your partner?", type: "text", options: nil, scaleMin: nil, scaleMax: nil, categoryId: nil, packId: nil), Question(id: "communication_002", text: "When do you feel it is easiest to talk to me?", type: "text", options: nil, scaleMin: nil, scaleMax: nil, categoryId: "communication", packId: nil),
Question(id: "q3", text: "How connected do you feel today?", type: "scale", options: nil, scaleMin: 1, scaleMax: 10, categoryId: nil, packId: nil), Question(id: "communication_151", text: "When you are upset, what helps most first?", type: "single_choice", options: ["Comfort", "Space", "Advice", "Distraction"], scaleMin: nil, scaleMax: nil, categoryId: "communication", packId: nil),
Question(id: "q4", text: "What's your ideal weekend activity together?", type: "multiple_choice", options: ["Relax at home", "Outdoor adventure", "Date night out", "Try something new"], scaleMin: nil, scaleMax: nil, categoryId: nil, packId: nil), Question(id: "date_night_001", text: "What would make a simple dinner feel fun for both of us?", type: "text", options: nil, scaleMin: nil, scaleMax: nil, categoryId: "date_night", packId: nil),
] ]
let sampleQuestions: [Question] = [ let sampleQuestions: [Question] = [
Question(id: "s1", text: "What's a dream you'd like to pursue together?", type: "text", options: nil, scaleMin: nil, scaleMax: nil, categoryId: nil, packId: nil), Question(id: "fun_001", text: "What is one thing we do together that always makes you smile?", type: "text", options: nil, scaleMin: nil, scaleMax: nil, categoryId: "fun", packId: nil),
Question(id: "s2", text: "How do you feel loved most?", type: "multiple_choice", options: ["Words of affirmation", "Quality time", "Physical touch", "Acts of service", "Gifts"], scaleMin: nil, scaleMax: nil, categoryId: nil, packId: nil), Question(id: "fun_002", text: "What is one silly memory of us that you still love?", type: "text", options: nil, scaleMin: nil, scaleMax: nil, categoryId: "fun", packId: nil),
Question(id: "s3", text: "Rate your communication today", type: "scale", options: nil, scaleMin: 1, scaleMax: 10, categoryId: nil, packId: nil), Question(id: "values_001", text: "What is one value that quietly guides your life?", type: "text", options: nil, scaleMin: nil, scaleMax: nil, categoryId: "values", packId: nil),
Question(id: "s4", text: "What's one new thing you want to try as a couple?", type: "text", options: nil, scaleMin: nil, scaleMax: nil, categoryId: nil, packId: nil), Question(id: "trust_001", text: "What makes it easy for you to trust someone?", type: "text", options: nil, scaleMin: nil, scaleMax: nil, categoryId: "trust", packId: nil),
] ]
let samplePacks: [QuestionPack] = [ let samplePacks: [QuestionPack] = [
QuestionPack(id: "p1", name: "Getting Closer", description: "Deepen your connection", categories: nil, isPremium: false), QuestionPack(id: "communication", name: "Communication", description: "Questions about listening, expressing needs, understanding each other, and talking clearly.", categories: nil, isPremium: false),
QuestionPack(id: "p2", name: "Fun & Playful", description: "Lighthearted questions", categories: nil, isPremium: false), QuestionPack(id: "fun", name: "Fun & Playful", description: "Lighthearted questions about playfulness, laughter, and shared activities.", categories: nil, isPremium: false),
QuestionPack(id: "p3", name: "Intimacy", description: "Build emotional intimacy", categories: nil, isPremium: true), QuestionPack(id: "date_night", name: "Date Night", description: "Questions designed for dates, meals, walks, or quiet time together.", categories: nil, isPremium: true),
QuestionPack(id: "p4", name: "Future Together", description: "Plan your future", categories: nil, isPremium: true), QuestionPack(id: "trust", name: "Trust", description: "Questions about trust, repair, and rebuilding.", categories: nil, isPremium: true),
] ]

View File

@ -376,33 +376,3 @@ struct WheelHistoryView: View {
} }
} }
// MARK: - Navigation helper
extension Binding where Value == String? {
func unwrapped<T: Hashable>(_ defaultValue: T) -> Binding<T> where T == String {
Binding<T>(
get: { self.wrappedValue as? T ?? defaultValue },
set: { self.wrappedValue = $0 as? String }
)
}
}
extension View {
func navigationDestination<T: Hashable>(item: Binding<T?>, @ViewBuilder destination: @escaping (T) -> some View) -> some View {
background(
NavigationLink(
isActive: Binding(
get: { item.wrappedValue != nil },
set: { if !$0 { item.wrappedValue = nil } }
),
destination: {
if let value = item.wrappedValue {
destination(value)
}
},
label: EmptyView.init
)
.hidden()
)
}
}