diff --git a/Spawn-App-iOS-SwiftUI/Services/Core/ServiceConstants.swift b/Spawn-App-iOS-SwiftUI/Services/Core/ServiceConstants.swift index f3b1c8c3..9c514ebc 100644 --- a/Spawn-App-iOS-SwiftUI/Services/Core/ServiceConstants.swift +++ b/Spawn-App-iOS-SwiftUI/Services/Core/ServiceConstants.swift @@ -8,6 +8,10 @@ struct ServiceConstants { // Base URL for sharing activities - updated to match deployed web app static let shareBase = "https://getspawn.com" + + /// Privacy Policy — open in browser from Settings → Legal → Privacy Policy and from terms (Section 5). + static let privacyPolicy = + "https://doc-hosting.flycricket.io/spawn-privacy-policy/8f254bc3-3403-4928-8353-f1f787ed6eec/privacy" } // MARK: - Share URL Generation diff --git a/Spawn-App-iOS-SwiftUI/Services/UI/InAppNotificationService.swift b/Spawn-App-iOS-SwiftUI/Services/UI/InAppNotificationService.swift index 4a87be98..3cb2112a 100644 --- a/Spawn-App-iOS-SwiftUI/Services/UI/InAppNotificationService.swift +++ b/Spawn-App-iOS-SwiftUI/Services/UI/InAppNotificationService.swift @@ -813,7 +813,6 @@ final class InAppNotificationService { /// Generate a default success message for combinations not explicitly handled private func generateDefaultSuccessMessage(resource: ResourceContext, operation: OperationContext) -> String { let resourceName = resource.displayName - let article = resource.article switch operation { case .create: diff --git a/Spawn-App-iOS-SwiftUI/TERMS_SUGGESTIONS.md b/Spawn-App-iOS-SwiftUI/TERMS_SUGGESTIONS.md new file mode 100644 index 00000000..d87be02e --- /dev/null +++ b/Spawn-App-iOS-SwiftUI/TERMS_SUGGESTIONS.md @@ -0,0 +1,55 @@ +# Terms and Conditions — Suggestions & Notes + +This document contains suggestions, fixes, and app-related thoughts about the Spawn Terms and Conditions. **Do not copy these into the in-app terms** unless you intentionally adopt them after legal review. + +--- + +## 1. Placeholders / unincorporated (Section 7) + +- **Section 7 — Unincorporated:** You are **not** incorporated, so do not use "Spawn, Inc." or "Spawn LLC." The in-app terms now use "the operators of Spawn." If you prefer to name individuals, you can use: + - The **operators' names** (e.g. "Spawn is operated by [Founder A] and [Founder B]"). + - A **DBA / trade name** if you have one (e.g. "Spawn" as a trade name used by [Your Name(s)]). + Using a company name when you are not incorporated can be misleading; the current wording keeps the terms accurate. +- **Section 5 — Privacy Policy:** The app now links to your Privacy Policy (see **Section 6** below). The URL is set in `ServiceConstants.URLs.privacyPolicy`. + +--- + +## 2. Location (Section 6) + +- The in-app terms have been aligned with your **Info.plist** usage description: location is used to show nearby activities on the map and as the initial location for new activities you create and share with friends. Ensure the **Privacy Policy** describes how location is collected, stored, and shared, and for how long. + +--- + +## 3. Missing clauses often found in app terms + +- **Governing law and venue:** e.g. "These Terms are governed by the laws of [State/Country]. Any disputes will be resolved in the courts of [jurisdiction]." +- **Arbitration / class action waiver:** If you want to require arbitration (common in US consumer apps), add a clear arbitration clause and, where applicable, class action waiver, and ensure it's consistent with the rest of the Terms. +- **Severability:** If one provision is invalid, the rest remain in effect. +- **Entire agreement:** These Terms (together with the Privacy Policy) constitute the entire agreement between the user and Spawn regarding the App. + +--- + +## 4. Limitation of liability (Section 8) + +- Section 8 is brief. Many apps add: + - A **cap on liability** (e.g. the amount paid to Spawn in the past 12 months, or a fixed sum). + - Clarification that Spawn is not liable for **user conduct**, **third-party services**, or **location inaccuracy**. +- **Jurisdiction-dependent:** Some countries do not allow certain liability exclusions; consider a carve-out (e.g. "Some jurisdictions do not allow …; in those jurisdictions our liability is limited to the maximum permitted by law"). + +--- + +## 5. Changes to terms (Section 11) + +- Consider specifying **how** you'll notify users of material changes (e.g. in-app notice, email, or push) and **when** the new terms take effect (e.g. 30 days after notice). +- For material changes, some apps require **re-acceptance** (e.g. checkbox or "I agree" after the next app update). Your onboarding already has acceptance; you could mirror that for major updates. + +--- + +## 6. App-specific implementation notes + +- **In-app:** Terms are shown in **TermsAndConditionsView** and linked from the onboarding "Terms" link and from **Settings → Legal → Terms and Conditions**. The Privacy Policy is linked from **Settings → Legal → Privacy Policy** and from the terms (Section 5); the app opens the URL defined in `ServiceConstants.URLs.privacyPolicy` (your Flycricket-hosted policy). +- **Acceptance:** Users accept by checking the box and continuing on the **UserToS** screen; consider logging acceptance (user id + timestamp + terms version) for compliance and dispute purposes. + +--- + +*This file is for internal use only. Have a lawyer review the final Terms and Privacy Policy before release.* diff --git a/Spawn-App-iOS-SwiftUI/Views/Pages/Activities/ActivityCreation/Steps/ActivityTypeSelection/ActivityTypeManagement/ActivityTypeFriendSelectionView.swift b/Spawn-App-iOS-SwiftUI/Views/Pages/Activities/ActivityCreation/Steps/ActivityTypeSelection/ActivityTypeManagement/ActivityTypeFriendSelectionView.swift index 992cace9..646f0c82 100644 --- a/Spawn-App-iOS-SwiftUI/Views/Pages/Activities/ActivityCreation/Steps/ActivityTypeSelection/ActivityTypeManagement/ActivityTypeFriendSelectionView.swift +++ b/Spawn-App-iOS-SwiftUI/Views/Pages/Activities/ActivityCreation/Steps/ActivityTypeSelection/ActivityTypeManagement/ActivityTypeFriendSelectionView.swift @@ -61,10 +61,17 @@ struct ActivityTypeFriendSelectionView: View { } } } - .navigationTitle("Select friends to add to this type") .navigationBarTitleDisplayMode(.inline) .navigationBarBackButtonHidden(false) .toolbar { + ToolbarItem(placement: .principal) { + Text("Select friends to add to this type") + .font(.onestSemiBold(size: 17)) + .foregroundColor(universalAccentColor) + .multilineTextAlignment(.center) + .lineLimit(2) + .fixedSize(horizontal: false, vertical: true) + } ToolbarItem(placement: .navigationBarTrailing) { Button(action: { saveActivityType() }) { Text("Save") diff --git a/Spawn-App-iOS-SwiftUI/Views/Pages/AuthFlow/Components/AuthProviderButtonView.swift b/Spawn-App-iOS-SwiftUI/Views/Pages/AuthFlow/Components/AuthProviderButtonView.swift index 8ac6e332..c8e1e154 100644 --- a/Spawn-App-iOS-SwiftUI/Views/Pages/AuthFlow/Components/AuthProviderButtonView.swift +++ b/Spawn-App-iOS-SwiftUI/Views/Pages/AuthFlow/Components/AuthProviderButtonView.swift @@ -57,12 +57,6 @@ struct AuthProviderButtonView: View { RoundedRectangle(cornerRadius: 16) .fill(getButtonBackgroundColor()) ) - .shadow( - color: Color.black.opacity(0.15), - radius: 8, - x: 0, - y: 4 - ) } private func getButtonBackgroundColor() -> Color { diff --git a/Spawn-App-iOS-SwiftUI/Views/Pages/AuthFlow/Registration/PrivacyPolicyPlaceholderView.swift b/Spawn-App-iOS-SwiftUI/Views/Pages/AuthFlow/Registration/PrivacyPolicyPlaceholderView.swift new file mode 100644 index 00000000..afa8f608 --- /dev/null +++ b/Spawn-App-iOS-SwiftUI/Views/Pages/AuthFlow/Registration/PrivacyPolicyPlaceholderView.swift @@ -0,0 +1,73 @@ +// +// PrivacyPolicyPlaceholderView.swift +// Spawn-App-iOS-SwiftUI +// + +import SwiftUI + +struct PrivacyPolicyPlaceholderView: View { + @Environment(\.dismiss) private var dismiss + @ObservedObject var themeService = ThemeService.shared + @Environment(\.colorScheme) var colorScheme + + private var privacyPolicyURL: URL? { URL(string: ServiceConstants.URLs.privacyPolicy) } + + var body: some View { + VStack(spacing: 0) { + HStack { + UnifiedBackButton { dismiss() } + Spacer() + Text("Privacy Policy") + .font(.onestSemiBold(size: 18)) + .foregroundColor(universalAccentColor(from: themeService, environment: colorScheme)) + Spacer() + Color.clear.frame(width: 44, height: 44) + } + .padding(.horizontal, 25) + .padding(.vertical, 12) + + Spacer() + VStack(spacing: 20) { + Text("Our Privacy Policy explains how we collect, use, and protect your data.") + .font(.onestRegular(size: 16)) + .foregroundColor(universalAccentColor(from: themeService, environment: colorScheme)) + .multilineTextAlignment(.center) + .padding(.horizontal, 32) + + if let url = privacyPolicyURL { + Button(action: { + UIApplication.shared.open(url) + }) { + HStack(spacing: 8) { + Image(systemName: "arrow.up.right.square") + .font(.system(size: 18)) + Text("View Privacy Policy") + .font(.onestSemiBold(size: 16)) + } + .foregroundColor(universalAccentColor(from: themeService, environment: colorScheme)) + .padding(.horizontal, 24) + .padding(.vertical, 14) + .background( + RoundedRectangle(cornerRadius: 12) + .stroke( + universalAccentColor(from: themeService, environment: colorScheme), lineWidth: 1.5) + ) + } + .buttonStyle(PlainButtonStyle()) + } + } + Text("For questions, contact spawnappmarketing@gmail.com") + .font(.onestRegular(size: 14)) + .foregroundColor(universalPlaceHolderTextColor(from: themeService, environment: colorScheme)) + .padding(.top, 24) + .padding(.horizontal, 32) + Spacer() + } + .background(universalBackgroundColor(from: themeService, environment: colorScheme).ignoresSafeArea()) + .navigationBarHidden(true) + } +} + +#Preview { + PrivacyPolicyPlaceholderView() +} diff --git a/Spawn-App-iOS-SwiftUI/Views/Pages/AuthFlow/Registration/TermsAndConditionsView.swift b/Spawn-App-iOS-SwiftUI/Views/Pages/AuthFlow/Registration/TermsAndConditionsView.swift new file mode 100644 index 00000000..8419236a --- /dev/null +++ b/Spawn-App-iOS-SwiftUI/Views/Pages/AuthFlow/Registration/TermsAndConditionsView.swift @@ -0,0 +1,131 @@ +// +// TermsAndConditionsView.swift +// Spawn-App-iOS-SwiftUI +// + +import SwiftUI + +struct TermsAndConditionsView: View { + @Environment(\.dismiss) private var dismiss + @ObservedObject var themeService = ThemeService.shared + @Environment(\.colorScheme) var colorScheme + + var body: some View { + VStack(spacing: 0) { + HStack { + UnifiedBackButton { dismiss() } + Spacer() + Text("Terms and Conditions") + .font(.onestSemiBold(size: 18)) + .foregroundColor(universalAccentColor(from: themeService, environment: colorScheme)) + Spacer() + // Balance back button + Color.clear.frame(width: 44, height: 44) + } + .padding(.horizontal, 25) + .padding(.vertical, 12) + + ScrollView { + VStack(alignment: .leading, spacing: 20) { + Text("March 10th 2026") + .font(.onestMedium(size: 14)) + .foregroundColor(universalPlaceHolderTextColor(from: themeService, environment: colorScheme)) + + sectionTitle("1. Introduction") + bodyText( + "Welcome to Spawn! These Terms and Conditions (\"Terms\") govern your use of the Spawn mobile application (\"App\"), which allows users to discover and join friends' activities in real time. By accessing or using Spawn, you agree to comply with these Terms. If you do not agree, please do not use the App." + ) + + sectionTitle("2. Eligibility") + bodyText( + "You must be at least 13 years old to use Spawn. If you are under 18, you must have parental or legal guardian consent. By using the App, you confirm that you meet these requirements." + ) + + sectionTitle("3. User Accounts") + bodyText("You are responsible for maintaining the confidentiality of your account credentials.") + bodyText( + "You agree not to share your account with others or use another person's account without permission." + ) + bodyText("Spawn reserves the right to suspend or terminate accounts that violate these Terms.") + + sectionTitle("4. Acceptable Use") + bodyText("When using Spawn, you agree to:") + bodyText("Share and engage with activities responsibly and respectfully.") + bodyText("Not post false, misleading, or inappropriate content.") + bodyText("Not use the App for illegal, harmful, or fraudulent purposes.") + bodyText("Not attempt to hack, disrupt, or exploit the App.") + bodyText( + "You retain ownership of content you create (e.g. activity descriptions, photos). By posting content, you grant Spawn a license to use, display, and share it as needed to operate the service. Activity locations and times may be visible to friends you invite or others based on your and the App's settings." + ) + + sectionTitle("5. Privacy Policy") + bodyText( + "Your use of Spawn is subject to our Privacy Policy, which explains how we collect, use, and protect your data. By using Spawn, you agree to our data practices. Our Privacy Policy is available in the app under Settings → Legal → Privacy Policy and at the link provided there." + ) + + sectionTitle("6. Location Services") + bodyText( + "Spawn uses your location to show nearby activities on the map and as the initial location for new activities you create and share with friends. You can control location access in your device and in-app settings. Disabling location may limit features such as seeing or joining nearby activities." + ) + + sectionTitle("7. Intellectual Property") + bodyText( + "Spawn and its associated trademarks, logos, and content are the exclusive property of the operators of Spawn." + ) + bodyText("Users may not copy, modify, or distribute any content from Spawn without permission.") + + sectionTitle("8. Limitation of Liability") + bodyText( + "Spawn is provided \"as is\" without warranties of any kind. We do not guarantee uninterrupted or error-free service. Spawn is not responsible for any loss, damages, or disputes arising from use of the App." + ) + + sectionTitle("9. Third-Party Links & Services") + bodyText( + "Spawn may contain links to third-party websites or services. We do not control or endorse these services and are not responsible for their content or policies." + ) + + sectionTitle("10. Termination") + bodyText( + "We reserve the right to suspend or terminate your access to Spawn at our discretion if you violate these Terms or engage in harmful activities on the App." + ) + + sectionTitle("11. Changes to Terms") + bodyText( + "Spawn may update these Terms periodically. Continued use of the App after changes constitutes acceptance of the updated Terms." + ) + + sectionTitle("12. Contact Us") + bodyText( + "If you have any questions about these Terms, please contact us at spawnappmarketing@gmail.com." + ) + + bodyText( + "By using Spawn, you acknowledge that you have read, understood, and agreed to these Terms and Conditions." + ) + .padding(.top, 8) + } + .padding(.horizontal, 24) + .padding(.bottom, 32) + } + } + .background(universalBackgroundColor(from: themeService, environment: colorScheme).ignoresSafeArea()) + .navigationBarHidden(true) + } + + private func sectionTitle(_ text: String) -> some View { + Text(text) + .font(.onestSemiBold(size: 16)) + .foregroundColor(universalAccentColor(from: themeService, environment: colorScheme)) + } + + private func bodyText(_ text: String) -> some View { + Text(text) + .font(.onestRegular(size: 15)) + .foregroundColor(universalAccentColor(from: themeService, environment: colorScheme)) + .fixedSize(horizontal: false, vertical: true) + } +} + +#Preview { + TermsAndConditionsView() +} diff --git a/Spawn-App-iOS-SwiftUI/Views/Pages/AuthFlow/Registration/UserToS.swift b/Spawn-App-iOS-SwiftUI/Views/Pages/AuthFlow/Registration/UserToS.swift index a19c95c0..9fea9e7a 100644 --- a/Spawn-App-iOS-SwiftUI/Views/Pages/AuthFlow/Registration/UserToS.swift +++ b/Spawn-App-iOS-SwiftUI/Views/Pages/AuthFlow/Registration/UserToS.swift @@ -12,6 +12,8 @@ struct UserToS: View { @ObservedObject private var userAuth = UserAuthViewModel.shared @State private var agreed: Bool = false @State private var isSubmitting: Bool = false + @State private var showTermsSheet: Bool = false + @State private var showPrivacySheet: Bool = false @ObservedObject var themeService = ThemeService.shared @Environment(\.colorScheme) var colorScheme @@ -77,20 +79,28 @@ struct UserToS: View { } .buttonStyle(PlainButtonStyle()) - Text("I agree to the ") - .font(.onestMedium(size: 14)) - .foregroundColor(universalAccentColor(from: themeService, environment: colorScheme)) - + Text("Terms") - .font(.onestMedium(size: 14)) - .underline() - .foregroundColor(universalAccentColor(from: themeService, environment: colorScheme)) - + Text(" & ") - .font(.onestMedium(size: 14)) - .foregroundColor(universalAccentColor(from: themeService, environment: colorScheme)) - + Text("Privacy Policy") - .font(.onestMedium(size: 14)) - .underline() - .foregroundColor(universalAccentColor(from: themeService, environment: colorScheme)) + HStack(spacing: 0) { + Text("I agree to the ") + .font(.onestMedium(size: 14)) + .foregroundColor(universalAccentColor(from: themeService, environment: colorScheme)) + Button(action: { showTermsSheet = true }) { + Text("Terms") + .font(.onestMedium(size: 14)) + .underline() + .foregroundColor(universalAccentColor(from: themeService, environment: colorScheme)) + } + .buttonStyle(PlainButtonStyle()) + Text(" & ") + .font(.onestMedium(size: 14)) + .foregroundColor(universalAccentColor(from: themeService, environment: colorScheme)) + Button(action: { showPrivacySheet = true }) { + Text("Privacy Policy") + .font(.onestMedium(size: 14)) + .underline() + .foregroundColor(universalAccentColor(from: themeService, environment: colorScheme)) + } + .buttonStyle(PlainButtonStyle()) + } } .padding(.horizontal, 40) @@ -141,6 +151,12 @@ struct UserToS: View { // Clear any previous error state when this view appears userAuth.clearAllErrors() } + .sheet(isPresented: $showTermsSheet) { + TermsAndConditionsView() + } + .sheet(isPresented: $showPrivacySheet) { + PrivacyPolicyPlaceholderView() + } } } diff --git a/Spawn-App-iOS-SwiftUI/Views/Pages/AuthFlow/Registration/VerificationCodeView.swift b/Spawn-App-iOS-SwiftUI/Views/Pages/AuthFlow/Registration/VerificationCodeView.swift index 0cb42dba..0d53914c 100644 --- a/Spawn-App-iOS-SwiftUI/Views/Pages/AuthFlow/Registration/VerificationCodeView.swift +++ b/Spawn-App-iOS-SwiftUI/Views/Pages/AuthFlow/Registration/VerificationCodeView.swift @@ -22,10 +22,8 @@ struct VerificationCodeView: View { var body: some View { VStack(spacing: 0) { - // Navigation Bar - matches activity creation flow positioning HStack { UnifiedBackButton { - // Clear any error states when going back userAuthViewModel.clearAllErrors() dismiss() } @@ -33,6 +31,7 @@ struct VerificationCodeView: View { } .padding(.horizontal, 25) .padding(.top, 16) + Spacer() mainContent Spacer() @@ -41,27 +40,21 @@ struct VerificationCodeView: View { .onAppear { viewModel.initialize() focusedIndex = viewModel.focusedIndex - // Clear any previous error state when this view appears userAuthViewModel.clearAllErrors() } .onDisappear { viewModel.stopTimer() } - .navigationBarHidden(true) - } - - private var navigationBar: some View { - HStack { - UnifiedBackButton { - // Go back one step in the onboarding flow - dismiss() + .toolbar { + ToolbarItemGroup(placement: .keyboard) { + Spacer() + Button("Done") { + focusedIndex = nil + viewModel.focusedIndex = nil + } } - Spacer() } - .padding(.horizontal, 25) - .padding(.top, 16) - .background(universalBackgroundColor(from: themeService, environment: colorScheme)) - .zIndex(1) + .navigationBarHidden(true) } private var mainContent: some View { @@ -152,7 +145,6 @@ struct VerificationCodeView: View { Binding( get: { viewModel.code[index] }, set: { newValue in - // The custom text field handles validation, so just update the value viewModel.code[index] = newValue } ) @@ -164,13 +156,25 @@ struct VerificationCodeView: View { await viewModel.verifyCode() } }) { - OnboardingButtonCoreView("Verify") { - viewModel.isFormValid ? figmaIndigo : Color.gray.opacity(0.6) + HStack { + Spacer() + Text("Verify") + .font(.onestSemiBold(size: 20)) + .foregroundColor(.white) + Spacer() } + .padding(.vertical, 16) + .background( + RoundedRectangle(cornerRadius: 16) + .fill(viewModel.isFormValid ? figmaIndigo : Color.gray.opacity(0.6)) + ) + .shadow( + color: Color.black.opacity(0.15), + radius: 8, + x: 0, + y: 4 + ) } - .padding(.top, -16) - .padding(.bottom, -30) - .padding(.horizontal, -22) .disabled(!viewModel.isFormValid) } diff --git a/Spawn-App-iOS-SwiftUI/Views/Pages/Profile/MyProfile/DayActivities/DayActivitiesPageView.swift b/Spawn-App-iOS-SwiftUI/Views/Pages/Profile/MyProfile/DayActivities/DayActivitiesPageView.swift index e328dcc1..80672c10 100644 --- a/Spawn-App-iOS-SwiftUI/Views/Pages/Profile/MyProfile/DayActivities/DayActivitiesPageView.swift +++ b/Spawn-App-iOS-SwiftUI/Views/Pages/Profile/MyProfile/DayActivities/DayActivitiesPageView.swift @@ -139,8 +139,15 @@ struct DayActivitiesPageView: View { activity: fullActivity, color: getColorForActivity(activity), locationManager: locationManager, - callback: { _, _ in - onActivitySelected(activity) + callback: { tappedActivity, activityColor in + // Post notification directly with full activity - we already have it, + // avoiding the parent's fetch flow which could show a blank drawer + NotificationCenter.default.post( + name: .showGlobalActivityPopup, + object: nil, + userInfo: ["activity": tappedActivity, "color": activityColor] + ) + onDismiss() }, horizontalPadding: 16 ) diff --git a/Spawn-App-iOS-SwiftUI/Views/Pages/Profile/MyProfile/MyProfileView.swift b/Spawn-App-iOS-SwiftUI/Views/Pages/Profile/MyProfile/MyProfileView.swift index dc97a7d3..77ebc618 100644 --- a/Spawn-App-iOS-SwiftUI/Views/Pages/Profile/MyProfile/MyProfileView.swift +++ b/Spawn-App-iOS-SwiftUI/Views/Pages/Profile/MyProfile/MyProfileView.swift @@ -382,7 +382,7 @@ struct MyProfileView: View { let currentName = userAuth.spawnUser?.name ?? "" let currentUsername = userAuth.spawnUser?.username ?? "" if username != currentUsername || name != currentName { - await userAuth.spawnEditProfile( + let _ = await userAuth.spawnEditProfile( username: username, name: name ) diff --git a/Spawn-App-iOS-SwiftUI/Views/Pages/Profile/MyProfile/Settings/SettingsView.swift b/Spawn-App-iOS-SwiftUI/Views/Pages/Profile/MyProfile/Settings/SettingsView.swift index 99333348..c3d67cd7 100644 --- a/Spawn-App-iOS-SwiftUI/Views/Pages/Profile/MyProfile/Settings/SettingsView.swift +++ b/Spawn-App-iOS-SwiftUI/Views/Pages/Profile/MyProfile/Settings/SettingsView.swift @@ -152,6 +152,51 @@ struct SettingsView: View { } } + // Legal + SettingsSection(title: "Legal") { + NavigationLink(destination: TermsAndConditionsView()) { + HStack { + Image(systemName: "doc.text") + .font(.system(size: 18)) + .foregroundColor(universalAccentColor) + .frame(width: 24, height: 24) + + Text("Terms and Conditions") + .font(.body) + .foregroundColor(universalAccentColor) + + Spacer() + + Image(systemName: "chevron.right") + .font(.system(size: 14)) + .foregroundColor(.gray) + } + .padding(.horizontal) + .frame(height: 44) + } + + NavigationLink(destination: PrivacyPolicyPlaceholderView()) { + HStack { + Image(systemName: "hand.raised") + .font(.system(size: 18)) + .foregroundColor(universalAccentColor) + .frame(width: 24, height: 24) + + Text("Privacy Policy") + .font(.body) + .foregroundColor(universalAccentColor) + + Spacer() + + Image(systemName: "chevron.right") + .font(.system(size: 14)) + .foregroundColor(.gray) + } + .padding(.horizontal) + .frame(height: 44) + } + } + // Contact Us SettingsSection(title: "Contact Us") { if let userId = userAuth.spawnUser?.id, let email = userAuth.spawnUser?.email { diff --git a/Spawn-App-iOS-SwiftUI/Views/Pages/Profile/UserProfile/UserProfileView.swift b/Spawn-App-iOS-SwiftUI/Views/Pages/Profile/UserProfile/UserProfileView.swift index 32e9becd..660d271e 100644 --- a/Spawn-App-iOS-SwiftUI/Views/Pages/Profile/UserProfile/UserProfileView.swift +++ b/Spawn-App-iOS-SwiftUI/Views/Pages/Profile/UserProfile/UserProfileView.swift @@ -117,7 +117,7 @@ struct UserProfileView: View { ) // Fetch activities if they're friends OR if viewing own profile - if let currentUserId = currentUserId { + if currentUserId != nil { if profileViewModel.friendshipStatus == .friends || profileViewModel.friendshipStatus == .themself { await profileViewModel.fetchProfileActivities( profileUserId: user.id