components/DailyWorkAreaModal.tsx

Purpose

This module implements a full-screen React Native modal for defining and editing a user’s “daily work area” using an interactive map interface.

The component combines:

  • map-based geographic selection
  • address autocomplete and geocoding
  • adjustable radius selection
  • work-area persistence actions
  • accessibility support
  • safe-area aware mobile layout behavior

The modal allows a user to:

  1. Search for a work location by address
  2. Select a center point on a map
  3. Define a circular work radius
  4. Save the resulting work area for the current day
  5. Reuse a previously saved work area

The file also encapsulates several reusable interaction primitives:

  • a custom slider implementation
  • region synchronization logic
  • draft-building helpers
  • debounced address suggestion handling
  • map marker/radius rendering behavior

Invariants

Draft Consistency

A valid DailyWorkAreaDraft always contains:

type DailyWorkAreaDraft = {
	centerLat: number;
	centerLon: number;
	radiusKm: number;
};

The modal never emits partial drafts.


Radius Bounds

Work area radius is always constrained to:

1 km ≤ radius ≤ 25 km

using:

clampRadius()

Radius Quantization

Radius values are normalized to fixed 0.5 km increments.

Examples:

InputStored
3.263.5
4.744.5

This guarantees stable UI rendering and predictable persistence values.


Slider Ratio Mapping

Slider state is always represented internally as a normalized ratio:

0.0 → minimum radius
1.0 → maximum radius

Conversions are reversible through:

  • radiusToRatio()
  • ratioToRadius()

Complete Map Selection

Map taps always produce a fully valid draft object.

buildDraftFromPress() guarantees:

  • valid center coordinates
  • retained radius when possible
  • fallback default radius otherwise

Address Selection Synchronization

Selecting an address suggestion always synchronizes:

  • text input value
  • selected draft coordinates
  • map camera position
  • autocomplete suggestion state

This prevents UI desynchronization between the map and search field.


Suggestion Request Ordering

Autocomplete requests are guarded by monotonically increasing request IDs.

Older async responses are ignored if newer searches have started.

This prevents stale suggestion lists from overwriting newer results.


Address suggestion requests are delayed by:

ADDRESS_SEARCH_DEBOUNCE_MS = 350

This reduces:

  • unnecessary network traffic
  • rapid API churn
  • autocomplete flickering

The map only resets when:

  • the modal becomes newly visible
  • the opening region changes

This avoids unnecessary MapView remounts during normal interaction.


Accessibility Guarantees

The component maintains accessibility semantics for:

  • adjustable slider controls
  • buttons
  • suggestion list items
  • close actions

Accessibility metadata includes:

  • labels
  • hints
  • adjustable values
  • disabled states

Busy-State Locking

When busy === true, the UI prevents:

  • map edits
  • radius edits
  • address selection
  • close actions
  • save actions

This prevents conflicting async mutations.


Variants

Map Selection Variant

Users may define a work area by:

Direct Map Interaction

  • tapping the map
  • dragging the radius slider
  • typing an address
  • selecting autocomplete suggestions
  • resolving a geocoded location

Both paths produce equivalent draft structures.


Radius Slider Variant

The custom slider supports:

  • touch taps
  • drag gestures
  • accessibility adjustable behavior

Internally it uses PanResponder rather than platform-native slider components.


Platform Rendering Variant

Marker rendering differs slightly on iOS:

IOS_MARKER_CENTER_OFFSET

This compensates for platform-specific marker anchoring behavior.


Region Initialization Variant

The modal accepts a caller-provided openingRegion.

If absent, it falls back to:

DEFAULT_REGION

This allows:

  • context-aware openings
  • persisted map state
  • location-aware defaults

Address Suggestion Lifecycle Variants

Autocomplete UI transitions through several states:

StateBehavior
idleno suggestions shown
searchingloading indicator visible
successsuggestions rendered
errorerror text displayed
selectedsuggestions cleared

Draft Presence Variant

Several UI behaviors depend on whether a draft exists.

No Draft

  • slider disabled
  • helper text shown
  • no map overlays rendered

Active Draft

  • marker visible
  • radius circle visible
  • slider enabled
  • save enabled

The modal supports optional close-button behavior.

Consumers may:

  • omit close controls entirely
  • customize accessibility labels
  • externally manage modal dismissal

Exports

DailyWorkAreaDraft

type DailyWorkAreaDraft = {
	centerLat: number;
	centerLon: number;
	radiusKm: number;
};

Represents a circular geographic work area centered at a coordinate.


DailyWorkAreaModalProps

Defines the full controlled API for the modal component.

Includes:

  • visibility state
  • draft state
  • address-search state
  • busy state
  • action callbacks
  • region initialization
  • persistence actions

The modal is fully controlled by the parent component.


DailyWorkAreaModal

export default function DailyWorkAreaModal(...)

Primary exported component.

Responsibilities include:

  • modal lifecycle management
  • map reset synchronization
  • safe-area integration
  • delegation into modal content rendering

Internal Components

DailyWorkAreaModalContent

Encapsulates the main modal UI and interaction logic.

Responsibilities include:

  • address search lifecycle
  • map interaction
  • suggestion rendering
  • draft mutation
  • action buttons
  • status rendering

RadiusSlider

Custom gesture-based slider component for selecting work area radius.

Features:

  • drag gestures
  • touch selection
  • accessibility adjustable semantics
  • quantized radius output
  • disabled-state handling

Internal Helper Functions

clampRadius(radiusKm)

Constrains radius values to supported limits.


quantizeRadius(radiusKm)

Rounds radius values to nearest 0.5 km.


regionsEqual(left, right)

Performs exact equality checks on map regions.

Used to avoid unnecessary map resets.


buildDraftFromPress(event, previous)

Builds a draft from a map tap event.


buildDraftFromAddress(...)

Builds a draft from a resolved address lookup.


formatRadiusLabel(radiusKm)

Formats human-readable slider labels.

Examples:

2 km radius
2.5 km radius

radiusToRatio(radiusKm)

Converts radius values into normalized slider ratios.


ratioToRadius(ratio)

Converts normalized slider ratios back into quantized radius values.