Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
2f0e062
fix api call for empty response post request
Daggerpov Feb 8, 2026
7e97d3f
fmt
Daggerpov Feb 8, 2026
1f0e231
perf: profileview: only call relevant api calls when editing
Daggerpov Feb 10, 2026
54d5e67
Merge branch 'main' into bug-fixes
Daggerpov Feb 10, 2026
55c1f6f
error handling
Daggerpov Feb 28, 2026
d0e2adb
map centering on user location
Daggerpov Feb 28, 2026
3c4ed54
feat (toasts): for errors and successes
Daggerpov Feb 28, 2026
b0870f0
Fix map view zoom issue
Daggerpov Feb 28, 2026
09c55c1
dark mode friends page text styling
Daggerpov Feb 28, 2026
a032c48
add to activity type view styling fixed
Daggerpov Feb 28, 2026
d2eef92
proper add to see spawns in unadded friend page
Daggerpov Feb 28, 2026
32ea1ca
rm tests
Daggerpov Feb 28, 2026
45afabc
add to activity type view: proper list formatting + proper pinned sor…
Daggerpov Feb 28, 2026
a87e2ca
More error alerts
Daggerpov Feb 28, 2026
c4912e5
profile image styling
Daggerpov Feb 28, 2026
838c43f
clean activity type card styling
Daggerpov Feb 28, 2026
e14516d
fix back button navigation from activity type editing view
Daggerpov Feb 28, 2026
b0b8980
day activities padding fix
Daggerpov Feb 28, 2026
f15e27a
friend profile styling fixes
Daggerpov Feb 28, 2026
75449a9
Create FriendActivitiesCalendarView.swift
Daggerpov Feb 28, 2026
432eedd
perf: fix only one profileviewmodel per user
Daggerpov Feb 28, 2026
c4dae51
Proper user activities pages order
Daggerpov Feb 28, 2026
b9af839
activity type card styling fixed for dark mode
Daggerpov Feb 28, 2026
e51295b
calendar view navigation fixed
Daggerpov Feb 28, 2026
d075a28
screen edge padding to ensure reasonable padding for smaller iphones
Daggerpov Feb 28, 2026
c9ba11d
friend activities loading fixed
Daggerpov Feb 28, 2026
74284cb
friend calendar pop out working
Daggerpov Feb 28, 2026
e9c33f0
friend activities standardized components
Daggerpov Feb 28, 2026
1ad188f
alerts -> in-app notifications, for consistency
Daggerpov Feb 28, 2026
82635a6
Fix activities popping up for friends & back page navigation states
Daggerpov Feb 28, 2026
b2cc9c8
fix profile editing states
Daggerpov Feb 28, 2026
c42aaee
redesign profile update & delete apis
Daggerpov Feb 28, 2026
10776b4
fix (tutorial): overlay styling
Daggerpov Feb 28, 2026
d9be8a5
fix (profile): proper phone number digit error handling
Daggerpov Feb 28, 2026
680fd9a
fix (errors): error handling for validation (profile fields)
Daggerpov Feb 28, 2026
fcd9115
fix (profile): editing causing user sign out
Daggerpov Feb 28, 2026
a023db6
v2.0
Daggerpov Feb 28, 2026
bd520b8
Merge branch 'main' into bug-fixes
Daggerpov Mar 6, 2026
c794460
fix: misc. warnings
Daggerpov Mar 6, 2026
fd1332b
Terms and Conditions
Daggerpov Mar 11, 2026
1c3044d
dismiss verification code keypad
Daggerpov Mar 11, 2026
003c1ec
Privacy policy + terms updates
Daggerpov Mar 11, 2026
073c9e7
fix: activity click from calendar
Daggerpov Mar 11, 2026
41ffdf6
fix: nav title for add friends to activity type truncation
Daggerpov Mar 11, 2026
e4398b8
fix: auth provider buttons no longer have drop shadow
Daggerpov Mar 11, 2026
8fb0544
fix: verification code entry
Daggerpov Mar 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Spawn-App-iOS-SwiftUI/Services/Core/ServiceConstants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
55 changes: 55 additions & 0 deletions Spawn-App-iOS-SwiftUI/TERMS_SUGGESTIONS.md
Original file line number Diff line number Diff line change
@@ -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.*
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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()
}
Original file line number Diff line number Diff line change
@@ -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()
}
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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()
}
}
}

Expand Down
Loading