Empty + Error + Loading + Offline State Audit — Ideony Mobile
Empty + Error + Loading + Offline State Audit — Ideony Mobile
Section titled “Empty + Error + Loading + Offline State Audit — Ideony Mobile”Date: 2026-04-20
Scope: apps/mobile/app/ — 44 content screens (excluding _layout.tsx)
Summary
Section titled “Summary”| State | ✅ Full | 🟡 Partial/inline | ❌ Missing | N/A |
|---|---|---|---|---|
| Loading | 6 | 26 | 0 | 12 |
| Empty | 5 | 10 | 0 | 29 |
| Error | 6 | 34 | 2 | 2 |
| Offline | 0 | 0 | 44 | 0 |
Full EmptyState component usage: only 5 screens — results.tsx, dashboard.tsx, requests.tsx, (professional)/credentials.tsx, (admin)/credentials.tsx.
Full ErrorState component usage: only 6 screens (same 5 + professional/[id].tsx).
Full Skeleton loading: only 3 screens (results, professional/[id], dashboard).
NetInfo usage: zero — offline detection absent entirely.
Per-Screen Summary
Section titled “Per-Screen Summary”(welcome) group
Section titled “(welcome) group”| Screen | Loading | Empty | Error | Offline |
|---|---|---|---|---|
tour.tsx | N/A (delegates to WelcomeTour) | N/A | N/A | ❌ |
continue.tsx | 🟡 button ActivityIndicator only | N/A | 🟡 inline colors.error Text | ❌ |
(auth) group
Section titled “(auth) group”| Screen | Loading | Empty | Error | Offline |
|---|---|---|---|---|
sign-in.tsx | N/A (Clerk component) | N/A | N/A | ❌ |
sign-up.tsx | N/A (Clerk component) | N/A | N/A | ❌ |
Root app/index.tsx
Section titled “Root app/index.tsx”| Screen | Loading | Empty | Error | Offline |
|---|---|---|---|---|
index.tsx | 🟡 ActivityIndicator; no skeleton | N/A | ✅ inline Text + Button retry; 3-retry logic | ❌ |
(consumer) group
Section titled “(consumer) group”| Screen | Loading | Empty | Error | Offline |
|---|---|---|---|---|
index.tsx | 🟡 ActivityIndicator | ❌ | 🟡 inline Text | ❌ |
search.tsx | 🟡 ActivityIndicator | 🟡 inline “no results” Text | 🟡 inline Text | ❌ |
bookings.tsx | 🟡 ActivityIndicator | 🟡 EmptyState partial — custom inline | 🟡 inline Text | ❌ |
profile.tsx | 🟡 ActivityIndicator | N/A | 🟡 inline Text | ❌ |
triage.tsx | N/A (form) | N/A | 🟡 inline Text | ❌ |
sos.tsx | 🟡 ActivityIndicator | N/A | 🟡 inline Text | ❌ |
Root dynamic routes
Section titled “Root dynamic routes”| Screen | Loading | Empty | Error | Offline |
|---|---|---|---|---|
book/[professionalId].tsx | 🟡 ActivityIndicator | N/A | 🟡 inline Text | ❌ |
booking/[id].tsx | 🟡 ActivityIndicator | N/A | 🟡 inline Text | ❌ |
professional/[id].tsx | ✅ Skeleton components | N/A | ✅ ErrorState component | ❌ |
results.tsx | ✅ SkeletonList/SkeletonProfessionalCard | ✅ EmptyState component | ✅ ErrorState component | ❌ |
compare.tsx | 🟡 ActivityIndicator | N/A | 🟡 inline Text | ❌ |
quotes/[id].tsx | 🟡 ActivityIndicator | N/A | 🟡 inline Text | ❌ |
quotes/new.tsx | N/A (form) | N/A | 🟡 inline Text | ❌ |
review/[bookingId].tsx | N/A (form) | N/A | 🟡 inline Text | ❌ |
portfolio/index.tsx | 🟡 ActivityIndicator | 🟡 custom inline “no images” | 🟡 inline Text | ❌ |
chat/[bookingId].tsx | 🟡 ActivityIndicator | 🟡 custom inline “no messages” | 🟡 inline Text | ❌ |
settings.tsx | N/A | N/A | 🟡 inline | ❌ |
stripe-onboarding.tsx | N/A (action screen) | N/A | 🟡 Alert.alert | ❌ |
verification/index.tsx | 🟡 ActivityIndicator; RefreshControl present | 🟡 custom inline Text | ❌ isError not destructured | ❌ |
verification/submit.tsx | N/A (form) | N/A | 🟡 Alert.alert for upload; inline Text for piva | ❌ |
sos/ group
Section titled “sos/ group”| Screen | Loading | Empty | Error | Offline |
|---|---|---|---|---|
sos/index.tsx | N/A (action) | N/A | 🟡 inline Text | ❌ |
sos/[id]/countdown.tsx | 🟡 ActivityIndicator | N/A | 🟡 inline Text | ❌ |
sos/[id]/tracking.tsx | 🟡 ActivityIndicator | N/A | 🟡 inline Text | ❌ |
sos/[id]/complete.tsx | N/A | N/A | N/A | ❌ |
sos/[id]/cancelled.tsx | N/A | N/A | N/A | ❌ |
(professional) group
Section titled “(professional) group”| Screen | Loading | Empty | Error | Offline |
|---|---|---|---|---|
dashboard.tsx | ✅ Skeleton components | ✅ EmptyState component | ✅ ErrorState component | ❌ |
requests.tsx | ✅ SkeletonBookingCard | ✅ EmptyState component | ✅ ErrorState component | ❌ |
earnings.tsx | 🟡 ActivityIndicator; RefreshControl | 🟡 custom inline “no earnings” | ❌ isError not destructured | ❌ |
calendar.tsx | 🟡 ActivityIndicator | 🟡 custom inline “no events” | 🟡 inline Text | ❌ |
credentials.tsx | ✅ Skeleton | ✅ EmptyState component | ✅ ErrorState component | ❌ |
(professional)/onboarding/ group (9 screens)
Section titled “(professional)/onboarding/ group (9 screens)”All of index, area, profile, stripe, identity, credentials, pricing, portfolio, complete:
- Loading: 🟡 ActivityIndicator on buttons; no skeleton — appropriate for wizard steps
- Empty: 🟡 where applicable (credentials/portfolio use custom inline views, not EmptyState component)
- Error: 🟡 universally (inline
colors.errorText; no ErrorState component) - Offline: ❌ universally
(admin) group
Section titled “(admin) group”| Screen | Loading | Empty | Error | Offline |
|---|---|---|---|---|
(admin)/credentials.tsx | ✅ Skeleton | ✅ EmptyState component | ✅ ErrorState component | ❌ |
Cross-Cutting Findings
Section titled “Cross-Cutting Findings”Token usage:
- Semantic
colors.*tokens: ✅ nearly universal. Exception:onboarding/portfolio.tsxhardcodes"rgba(0,0,0,0.5)"and"#fff"on delete overlay button. @ideony/design-tokensshadows.sm/shadows.md: used inverification/index.tsx,stripe-onboarding.tsx,professional/[id].tsx— not universal.
Icon consistency split:
- Lucide React Native: dominant everywhere except onboarding wizard
- Phosphor React Native: ALL
(professional)/onboarding/steps exclusively - This split is intentional/consistent within each group — not a defect per se
i18n coverage on state messages:
- ✅ fully wrapped: all screens except
(welcome)/continue.tsx - 🟡
(welcome)/continue.tsx: Italian string fallbacks pattern (t("key", "Fallback italiano")) — strings work but circumvent translation pipeline
Offline: ❌ zero NetInfo usage anywhere in the codebase.
ErrorState component usage: only 6 screens: results.tsx, professional/[id].tsx, dashboard.tsx, requests.tsx, (professional)/credentials.tsx, (admin)/credentials.tsx
EmptyState component usage: only 5 screens: results.tsx, dashboard.tsx, requests.tsx, (professional)/credentials.tsx, (admin)/credentials.tsx
verification/index.tsx specific issue: isError not destructured from useQuery — if the /verification/me fetch fails, user sees a blank screen (only isLoading and data are used). RefreshControl passes refreshing={isLoading} which is always false after initial load (should be isRefetching).
earnings.tsx specific issue: Same pattern — isError not destructured, silent failure.
Top 10 Priority Fix List (by user impact)
Section titled “Top 10 Priority Fix List (by user impact)”-
verification/index.tsx—isErrorunhandled → blank screen on network failure; alsoRefreshControlbug (isLoading→isRefetching). High impact: professional verification is a trust-critical flow. -
earnings.tsx—isErrorunhandled → blank screen on failure. High impact: earnings data drives professional payout decisions. -
(consumer)/bookings.tsx— no EmptyState/ErrorState components; loading is ActivityIndicator only. High impact: core consumer screen. -
(consumer)/index.tsx(home feed) — no empty state at all; no skeleton loading. High impact: first screen a consumer sees after login. -
chat/[bookingId].tsx— no ErrorState; custom inline empty. High impact: real-time communication feature, errors must surface clearly. -
portfolio/index.tsx— no ErrorState; custom inline empty. Impact: professional’s portfolio showcase. -
(professional)/onboarding/credentials.tsxandonboarding/portfolio.tsx— custom inline empty views instead of EmptyState component; error states inline only. Impact: onboarding completion rate. -
(consumer)/search.tsx— no skeleton during initial load; no ErrorState. High impact: primary discovery surface. -
booking/[id].tsx— no ErrorState. Impact: booking management critical for both roles. -
(welcome)/continue.tsx— Italian string fallbacks bypass translation pipeline; error is inline (no ErrorState). Impact: first auth experience for all users.
Offline (global recommendation)
Section titled “Offline (global recommendation)”NetInfo is absent from the entire codebase. Recommend: single useNetworkStatus hook (wrap @react-native-community/netinfo) + global OfflineBanner component rendered in _layout.tsx → one fix covers all 44 screens.