WikifitaGitHub live67e8de5
outro · sprite-mobile/sprite-mobile-components

Sprite Mobile — Components & Screens

Catalog of all React Native components and screens in Sprite Mobile with props, state connections, and animation details

Baixar raw

Sprite Mobile — Components & Screens

Overview

Sprite Mobile contains 4 screens and 13 reusable components. All components use react-native-reanimated for animations. The component tree is flat — no deep nesting, no context providers beyond Redux. Each component is self-contained with its own styles file.

Screens

OnboardingScreen

Path: src/screens/Onboarding/OnboardingScreen.tsx Purpose: First-time device pairing flow. Shown when isOnboardingComplete === false.

Behavior:

  • On Android, polls ApplicationService.requestPermission() every 1s until granted
  • Displays AnimatedLocket based on paired device's jewelModel (0=circle, 1=square, 2=oval)
  • Shows SearchingDeviceIndicator (loading spinner while scanning, "Device Found" when paired)
  • OK button navigates to Config and sets isOnboardingComplete = true
  • Uses react-i18next for localized strings

State connections:

  • state.ble.pairedDevice — determines which locket model to display
  • state.application.isOnboardingComplete — set to true on confirm

Components used: AnimatedLocket, SearchingDeviceIndicator, SpriteLogo


HomeScreen

Path: src/screens/Home/HomeScreen.tsx Purpose: Main landing screen with two navigation buttons.

Behavior:

  • Renders BackgroundVideo with 'home' variant
  • Two AnimatedButtonWithIcon buttons: Radar (left) and Config (right)
  • Radar button has infinityPulse animation (random quadrant rotation every 1.4s)
  • Config button has infinityRotate animation (continuous 8s rotation cycle)

State connections: None (pure navigation screen)

Components used: BackgroundVideo, AnimatedButtonWithIcon


ConfigScreen

Path: src/screens/Config/ConfigScreen.tsx Purpose: Settings screen — activate alerts, set distance threshold, configure emergency URL.

Behavior:

  • ActivateApplicationSwitch — toggles isAlertActive in Redux
  • SliderCustom — sets emergencyRange (3-7m, integer steps)
  • UrlButton — editable text input for emergency URL (defaults to remote config defaultVideo)
  • ConfirmButton — navigates to Radar
  • Keyboard-aware: uses KeyboardAvoidingView with platform-specific behavior
  • Backdrop overlay when URL input is focused (dismisses keyboard on tap)

State connections:

  • state.application.isAlertActive — read/write via ActivateApplicationSwitch
  • state.application.emergencyRange — read/write via SliderCustom
  • state.application.emergencyUrl — read/write via UrlButton

Components used: BackgroundVideo, ActivateApplicationSwitch, SliderCustom, UrlButton, ConfirmButton


RadarScreen

Path: src/screens/Radar/RadarScreen.tsx Purpose: Real-time distance visualization to paired BLE device.

Behavior:

  • On mount, calls ApplicationService.monitorRssiForPairedDevice() to start RSSI monitoring
  • Renders RadarComponent (animated radar visualization)
  • ConnectionStateManagerView shows paired device name and locket icon
  • ConfirmButton navigates back to Home
  • Background video variant: 'radar'

State connections:

  • state.ble.pairedDevice.approximateDistance — displayed by RadarComponent

Components used: BackgroundVideo, RadarComponent, ConnectionStateManagerView, ConfirmButton


Components

RadarComponent

Path: src/components/Radar/RadarComponent.tsx Purpose: Displays current distance to paired device with animated radar visualization.

Props: None (reads from Redux directly)

State connections:

  • state.ble.pairedDevice.approximateDistance — displayed as ${distance}m or ---

Sub-components: RadarBaseAndEffectComponent

Behavior:

  • Shows distance text in uppercase (TCCC-UnityHeadline-Bold font)
  • Delegates radar animation to RadarBaseAndEffect

RadarBaseAndEffect

Path: src/components/Radar/RadarBaseAndEffect.tsx Purpose: Animated radar sweep with rotating effect overlay.

Props: None

Animations:

  • Spin: Continuous 360-degree rotation of RadarEffectOriginal image over 5s, linear easing, infinite repeat
  • Dot opacity: Pulse effect (0→1→0) on 1s cycle, infinite repeat
  • Rotation: Secondary rotation value (0→1) over 3s, infinite repeat

Assets: RadarBase (static base image, 230x227px), RadarEffectOriginal (rotating overlay, 340x340px)

Technical notes: Uses ReduceMotion.Never to ensure animations run even with accessibility settings enabled. All three animations run simultaneously via separate useSharedValue hooks.


ActivateApplicationSwitch

Path: src/components/ActivateApplicationSwitch/ActivateApplicationSwitch.tsx Purpose: On/Off toggle switch for the proximity alert system.

Props: None (reads/writes Redux)

State connections:

  • state.application.isAlertActive — toggled on press
  • Dispatches Actions.setAlertState(!isAlertActive)
  • Also resets notificationAlreadyShownForRange via ApplicationService

Animations:

  • Button width: Spring animation from 0→100% on mount
  • Switch thumb: React Native Animated.timing (150ms) interpolating marginLeft from 4% to 50%
  • Spring sequence: Initial withTiming(0, 300ms)withSpring(100, 1200ms)

Visual: Two-state button with "Off"/"Text" labels, green active state (#40c965)


AnimatedButtonWithIcon

Path: src/components/AnimatedButtonWithIcon/AnimatedButtonWithIcon.tsx Purpose: Navigation button with icon animation (used on Home screen).

Props:

interface AnimatedButtonWithIconProps {
  icon: 'radar' | 'config';          // Icon to display
  infinityRotate?: boolean;           // Continuous rotation animation
  infinityPulse?: boolean;            // Random quadrant rotation
  delay?: number;                     // Animation delay (ms)
  onPress?: () => void;               // Tap handler
}

Animations:

  • Width expansion: Sequenced: Timing(38, 200ms)Timing(40, 200ms)Spring(width*0.7)
  • Icon rotation (config): interpolate from 180° to 0° as width expands
  • Infinity rotation (radar): Continuous 360° rotation over 8s, linear easing, infinite repeat
  • Infinity pulse (radar): Random quadrant (0/90/180/270°) every 1.4s via setInterval

Assets: RadarIcon or GearIcon depending on icon prop

Technical notes: Icon display is hidden until button width exceeds 42px via useDerivedValue with conditional 'none'/'flex'.


AnimatedLocket

Path: src/components/AnimatedLocket/AnimatedLocket.tsx Purpose: Displays the appropriate locket/jewel image based on device model.

Props:

interface AnimatedJewelComponentProps {
  model?: 'circle' | 'square' | 'oval' | number;
}

Mapping:

  • circle / 0AnimatedJewel (circle pendant)
  • square / 1AnimatedJewel3 (square pendant)
  • oval / 2AnimatedJewel2 (oval pendant)
  • Default → AnimatedJewel

Technical notes: Uses useMemo for image source selection. The image is a static PNG despite the "Animated" naming — the animation is in the CSS transform, not the image itself.


BackgroundVideo

Path: src/components/BackgroundVideo/BackgroundVideo.tsx (iOS), BackgroundVideo.android.tsx (Android) Purpose: Full-screen background image/animation for each screen.

Props:

interface BackgroundVideoProps {
  video?: 'home' | 'config' | 'radar';
}

Platform behavior:

  • Android: Uses @shopify/react-native-skia Canvas + Image with useAnimatedImageValue for animated GIF rendering
  • iOS: Uses React Native ImageBackground with resizeMode="contain"

Assets: HomeBg, ConfigBg, RadarBg (per-screen background images)

Technical notes: Memoized with React.memo. Positioned absolutely at full screen with zIndex: -1/-10.


BluetoothStateModal

Path: src/components/BluetoothStateModal/BluetoothStateModal.tsx Purpose: Displays when Bluetooth is disabled; prompts user to enable it.

Props: None

Behavior: Static display only — no action buttons. The app relies on the system Bluetooth toggle. The RootStack watches isBluetoothEnabled and navigates away automatically when BT is enabled.

Components used: SpriteLogo

Localized strings: bluetooth-disabled, enable-bluetooth


OverlaysStateModal

Path: src/components/BluetoothStateModal/OverlaysStateModal.tsx Purpose: Android-only overlay permission request screen.

Props: None

Behavior:

  • Polls ApplicationService.verifyAndroidOverlayPermissions() every 1s
  • When permission is granted: calls configureNativeBinds(), checks BLE state, navigates to appropriate screen
  • Shows "Grant overlay permissions" button that calls requestAndroidOverlayPermissions()
  • Navigates based on BLE state + onboarding state once overlay is granted

Components used: SpriteLogo

Localized strings: overlay-disabled, enable-overlay, grant-overlay


ConfirmButton

Path: src/components/ConfirmButton/ConfirmButton.tsx Purpose: Back/Confirm navigation button pair (used on Config and Radar screens).

Props:

interface ConfirmButtonProps {
  onConfirm?: () => void;     // Custom confirm action
  onBack?: () => void;        // (unused, defaults to Home)
  style?: ViewStyle;          // Container style override
}

Animations:

  • Width expansion: Timing(96, 200ms)Timing(128, 200ms)Spring(width) — expands to full screen width
  • Arrow button width: interpolate from 16→42px as container expands

Behavior:

  • Left arrow button: navigates to Home
  • Right confirm button: calls onConfirm if provided, else navigates to Home
  • Platform-specific bottom margin: 4% (Android) vs 12% (iOS)

ConnectionStateManagerView

Path: src/components/ConnectionStateManagerView/ConnectionStateManagerView.tsx Purpose: Shows paired device name, locket icon, and connection status.

Props:

interface ConnectionStateManagerViewProps {
  showDevice?: boolean;        // Show locket image
  showBluetothState?: boolean; // Show Bluetooth switch
  message?: string | null;     // Custom status message
}

State connections:

  • state.ble.pairedDevice — name, jewelModel

Behavior:

  • Long-press on locket triggers unpair confirmation dialog
  • Unpair resets ApplicationService state and navigates to Onboarding
  • Animated width expansion: Timing(10, 300ms)Timing(width*0.84, 900ms)

Components used: AnimatedLocket


SearchingDeviceIndicator

Path: src/components/SearchingDeviceIndicator/SearchingDeviceIndicator.tsx Purpose: Shows scanning status during device discovery.

Props: None

State connections:

  • state.ble.pairedDevice — if undefined, shows spinner; if defined, shows "Device Found"

Behavior:

  • Searching: Green border (#40c965), ActivityIndicator spinner
  • Found: Solid green background (#40c965), "Device Found" text

Localized strings: device-found


SliderCustom

Path: src/components/SliderCustom/SliderCustom.tsx Purpose: Distance threshold slider (3-7m range).

Props: None (reads/writes Redux)

State connections:

  • state.application.emergencyRange — initial value, updated on slide complete

Behavior:

  • Range: 3m to 7m, integer steps only
  • Green track (#3DCB65), dark track (#04240D), white thumb
  • Animated text label follows thumb position (Reanimated useDerivedValue)
  • Width measured via useAnimatedRef + measure() for accurate label positioning

Animations:

  • Container width: Spring(100, 1200ms) with ReduceMotion.Never
  • Label position: Derived from slider value × measured width

UrlButton

Path: src/components/UrlButton/UrlButton.tsx Purpose: Emergency URL input field with animated expansion.

Props:

interface UrlButtonProps {
  onfocus: () => void;       // Focus callback
  onblur: () => void;        // Blur callback
  onSubmited: () => void;    // Submit callback
  style?: ViewStyle;         // Container style override
}

State connections:

  • state.application.emergencyUrl — current URL value
  • state.application.defaultEmergencyUrl — fallback value

Behavior:

  • Displays placeholder text from t('emergency-url') translation
  • Animated width expansion: Timing(38, 200ms)Timing(56, 200ms)Spring(width*0.84)
  • On submit: navigates to Radar (resets navigation stack)
  • Uses forwardRef to expose TextInput ref for programmatic focus/blur

Technical notes: The onSubmitEditing handler resets the navigation stack before navigating to Radar, ensuring a clean transition.


SpriteLogo

Path: src/components/SpriteLogo/SpriteLogo.tsx Purpose: Renders the Sprite brand logo.

Props: None

Behavior: Simple Image component with LogoSprite asset, resizeMode="contain"

Used in: OnboardingScreen, BluetoothStateModal, OverlaysStateModal


Component Dependency Graph

graph TD
    subgraph "Screens"
        ONBOARDING["OnboardingScreen"]
        HOME["HomeScreen"]
        CONFIG["ConfigScreen"]
        RADAR["RadarScreen"]
    end

    subgraph "Components"
        AL["AnimatedLocket"]
        SDI["SearchingDeviceIndicator"]
        SLOGO["SpriteLogo"]
        BV["BackgroundVideo"]
        ABW["AnimatedButtonWithIcon"]
        RC["RadarComponent"]
        CSVM["ConnectionStateManagerView"]
        CB["ConfirmButton"]
        AAS["ActivateApplicationSwitch"]
        SC["SliderCustom"]
        UB["UrlButton"]
        BTM["BluetoothStateModal"]
        OSM["OverlaysStateModal"]
        RBE["RadarBaseAndEffect"]
    end

    ONBOARDING --> AL
    ONBOARDING --> SDI
    ONBOARDING --> SLOGO

    HOME --> BV
    HOME --> ABW

    CONFIG --> BV
    CONFIG --> AAS
    CONFIG --> SC
    CONFIG --> UB
    CONFIG --> CB

    RADAR --> BV
    RADAR --> RC
    RADAR --> CSVM
    RADAR --> CB

    RC --> RBE

    BTM --> SLOGO
    OSM --> SLOGO
    CSVM --> AL

Animation Patterns

All animations use react-native-reanimated with these common patterns:

  1. Spring expansion — Elements start at 0 width and spring to full width on mount (withSequence(withTiming(0), withSpring(target)))
  2. Infinite rotation — Radar sweep, gear icon (withRepeat(withTiming(360), -1))
  3. Interpolated transforms — Thumb position, icon rotation interpolated from animated values
  4. ReduceMotion.Never — All critical animations bypass accessibility reduce-motion for safety-critical UI (radar, alerts)

Styling Conventions

  • All style files use StyleSheet.create() for optimization
  • Color palette: Dark backgrounds (#000, #0d2814), Green accent (#40c965, #3DCB65), White text
  • Font: TCCC-UnityHeadline-Bold and TCCC-UnityHeadline-Medium (custom fonts loaded via react-native-asset)
  • Border radius: 25px for buttons (pill shape), 32px for circular elements
  • Platform-specific margins: Android gets smaller bottom margins than iOS

Cross-References