Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions ios/.swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ disabled_rules:
# collection literals. SwiftLint's `trailing_comma` rule forbids them by
# default, which conflicts. swift-format is authoritative — disable here.
- trailing_comma
# swift-format wraps declarations with long protocol conformance lists
# onto multiple lines, placing the opening brace on its own line.
# SwiftLint's `opening_brace` insists the brace stay on the declaration
# line. Same precedent as `trailing_comma` — swift-format is authoritative.
- opening_brace

# Design-system token names (MWSpacing.s, MWRadius.m, …) intentionally use
# one- and two-character identifiers to match the canonical t-shirt scale.
Expand Down
30 changes: 16 additions & 14 deletions ios/Flashcards/App/RootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,22 +60,24 @@ struct RootView: View {
.font(MWType.headingM).foregroundStyle(MWColor.ink)
default:
switch step {
case .splash: SplashView().task {
try? await Task.sleep(nanoseconds: 1_200_000_000)
step = .intro1
}
case .splash:
SplashView().task {
try? await Task.sleep(nanoseconds: 1_200_000_000)
step = .intro1
}
case .intro1: Intro1View { step = .intro2 }
case .intro2: Intro2View { step = .signup }
case .signup: SignUpWallView(
onAppleSignIn: {
try? await auth.signInWithApple()
},
onRequestMagicLink: { email in
try? await auth.requestMagicLink(email: email)
emailSent = email
step = .magicLinkSent
}
)
case .signup:
SignUpWallView(
onAppleSignIn: {
try? await auth.signInWithApple()
},
onRequestMagicLink: { email in
try? await auth.requestMagicLink(email: email)
emailSent = email
step = .magicLinkSent
}
)
case .magicLinkSent:
MagicLinkSentView(email: emailSent ?? "")
}
Expand Down
2 changes: 2 additions & 0 deletions ios/Flashcards/DesignSystem/Components/Layout/MWScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public struct MWScreen<Content: View>: View {
self.content = content
}

/// Renders the canvas background, the optional grid overlay, and the
/// supplied `content()` stacked in a `ZStack`.
public var body: some View {
ZStack {
MWColor.canvas.ignoresSafeArea()
Expand Down
2 changes: 2 additions & 0 deletions ios/Flashcards/DesignSystem/EnvironmentKeys/MWAccentKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,7 @@ public extension EnvironmentValues {
public extension View {
/// Scopes the `mwAccent` environment value for the receiver and its descendants.
/// - Parameter color: The accent color (usually an `MWAccent.color`).
/// - Returns: A view whose subtree reads the supplied accent colour via
/// `@Environment(\.mwAccent)`.
func mwAccent(_ color: Color) -> some View { environment(\.mwAccent, color) }
}
6 changes: 6 additions & 0 deletions ios/Flashcards/DesignSystem/Modifiers/MWButtonPress.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ public struct MWButtonPress: ViewModifier {

@Environment(\.accessibilityReduceMotion) private var reduceMotion

/// Applies the press-scale + reduce-motion-safe animation to `content`.
///
/// - Parameter content: The view being decorated.
/// - Returns: A view that scales to `pressedScale` while `isPressed` is
/// true, returning to `1.0` otherwise, with the transition collapsed
/// to an instantaneous change when Reduce Motion is enabled.
public func body(content: Content) -> some View {
content
.scaleEffect(isPressed ? Self.pressedScale : 1.0)
Expand Down
1 change: 1 addition & 0 deletions ios/Flashcards/DesignSystem/Tokens/Borders.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public extension View {
/// - cornerRadius: Corner radius for the overlay shape. Defaults to `0`
/// (sharp rectangle). Pass the same value used in an upstream
/// `mwCornerRadius` so the stroke hugs the rounded clip.
/// - Returns: A view overlaid with the configured stroke.
func mwStroke(
color: Color = MWColor.ink,
width: CGFloat = MWBorder.defaultWidth,
Expand Down
3 changes: 2 additions & 1 deletion ios/Flashcards/Features/Auth/AppleSignInService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ public final class AppleSignInService:
NSObject,
ObservableObject,
ASAuthorizationControllerDelegate,
ASAuthorizationControllerPresentationContextProviding {
ASAuthorizationControllerPresentationContextProviding
{
/// Pending continuation for the in-flight `signIn()` call. Lives on
/// the instance because the delegate callbacks can't capture it
/// directly. Cleared the moment it is resumed.
Expand Down
17 changes: 12 additions & 5 deletions ios/Flashcards/Features/Onboarding/SignUpWallView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,25 @@ struct SignUpWallView: View {
MWEyebrow("Save your progress")
Text("No password required.")
.font(MWType.headingL).foregroundStyle(MWColor.ink)
// swiftlint:disable:next line_length
Text("We ask for your email so your flashcards live in your account, not just this device. If you lose your phone, you won't lose a single card.")
.font(MWType.bodyL).foregroundStyle(MWColor.inkMuted)
Text(
"We ask for your email so your flashcards live in your account, "
+ "not just this device. If you lose your phone, you won't "
+ "lose a single card."
)
.font(MWType.bodyL).foregroundStyle(MWColor.inkMuted)

MWButton("Continue with Apple") {
Task { isSubmitting = true; await onAppleSignIn(); isSubmitting = false }
Task {
isSubmitting = true; await onAppleSignIn(); isSubmitting = false
}
}

MWTextField(label: "Email", text: $email, contentType: .emailAddress, keyboard: .emailAddress)

MWButton("Continue with email", kind: .secondary) {
Task { isSubmitting = true; await onRequestMagicLink(email); isSubmitting = false }
Task {
isSubmitting = true; await onRequestMagicLink(email); isSubmitting = false
}
}.disabled(email.isEmpty || isSubmitting)

Text("Free to use. No payment needed. We won't sell your data or email you marketing.")
Expand Down
Loading