Sprite Mobile — Components & Screens
Catalog of all React Native components and screens in Sprite Mobile with props, state connections, and animation details
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
AnimatedLocketbased on paired device'sjewelModel(0=circle, 1=square, 2=oval) - Shows
SearchingDeviceIndicator(loading spinner while scanning, "Device Found" when paired) - OK button navigates to
Configand setsisOnboardingComplete = true - Uses
react-i18nextfor localized strings
State connections:
state.ble.pairedDevice— determines which locket model to displaystate.application.isOnboardingComplete— set totrueon confirm
Components used: AnimatedLocket, SearchingDeviceIndicator, SpriteLogo
HomeScreen
Path: src/screens/Home/HomeScreen.tsx
Purpose: Main landing screen with two navigation buttons.
Behavior:
- Renders
BackgroundVideowith'home'variant - Two
AnimatedButtonWithIconbuttons: Radar (left) and Config (right) - Radar button has
infinityPulseanimation (random quadrant rotation every 1.4s) - Config button has
infinityRotateanimation (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— togglesisAlertActivein ReduxSliderCustom— setsemergencyRange(3-7m, integer steps)UrlButton— editable text input for emergency URL (defaults to remote configdefaultVideo)ConfirmButton— navigates toRadar- Keyboard-aware: uses
KeyboardAvoidingViewwith platform-specific behavior - Backdrop overlay when URL input is focused (dismisses keyboard on tap)
State connections:
state.application.isAlertActive— read/write viaActivateApplicationSwitchstate.application.emergencyRange— read/write viaSliderCustomstate.application.emergencyUrl— read/write viaUrlButton
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) ConnectionStateManagerViewshows paired device name and locket iconConfirmButtonnavigates back toHome- Background video variant:
'radar'
State connections:
state.ble.pairedDevice.approximateDistance— displayed byRadarComponent
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}mor---
Sub-components: RadarBaseAndEffectComponent
Behavior:
- Shows distance text in uppercase (
TCCC-UnityHeadline-Boldfont) - 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
RadarEffectOriginalimage 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
notificationAlreadyShownForRangeviaApplicationService
Animations:
- Button width: Spring animation from 0→100% on mount
- Switch thumb: React Native
Animated.timing(150ms) interpolatingmarginLeftfrom 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):
interpolatefrom 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/0→AnimatedJewel(circle pendant)square/1→AnimatedJewel3(square pendant)oval/2→AnimatedJewel2(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-skiaCanvas+ImagewithuseAnimatedImageValuefor animated GIF rendering - iOS: Uses React Native
ImageBackgroundwithresizeMode="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:
interpolatefrom 16→42px as container expands
Behavior:
- Left arrow button: navigates to
Home - Right confirm button: calls
onConfirmif provided, else navigates toHome - 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
ApplicationServicestate and navigates toOnboarding - 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),ActivityIndicatorspinner - 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)withReduceMotion.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 valuestate.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
forwardRefto exposeTextInputref 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:
- Spring expansion — Elements start at 0 width and spring to full width on mount (
withSequence(withTiming(0), withSpring(target))) - Infinite rotation — Radar sweep, gear icon (
withRepeat(withTiming(360), -1)) - Interpolated transforms — Thumb position, icon rotation interpolated from animated values
- 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-BoldandTCCC-UnityHeadline-Medium(custom fonts loaded viareact-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
- sprite-mobile — Hub overview with full project context
- sprite-mobile-architecture — State management and navigation flow
- sprite-mobile-ble — BLE integration details
- camdom — Comparison with CAMDOM's BLE mesh approach