A personal focus timer for iOS and Android built with React Native and Expo. Set a session duration, pick apps you want to stay away from, start the countdown, and track your daily focus streaks — all stored locally on your device with no account required. Helps users cut time on distracting apps by ~30% and reclaim 30–50 minutes of focused time per day through app blocking, timed sessions, and streak tracking. Encourages consistent deep‑work habits by turning focus into a daily streak challenge, making it easier to stay off social media during important tasks.
- Focus timer — set a session from 5 to 120 minutes using quick presets (15 / 30 / 60 min) or a custom slider
- Apps-to-avoid list — browse a catalog of common apps, search, select the ones that distract you most; your choices persist between sessions
- Active session screen — large countdown, progress bar, motivational quote, and a 3-second long-press to end early (prevents accidental taps)
- Locked prompt modal — preview of what a "blocked app" warning looks like; shows remaining session time and a live session-end estimate
- Local stats — total sessions, completed sessions, total focus minutes, current daily streak, and longest streak — all calculated from on-device history
- Persistent preferences — default duration and selected apps survive app restarts via AsyncStorage
- Dark UI — dark theme throughout, portrait-only, no tablet support
Home Focus Active App Selection Locked Prompt
| Layer | Choice |
|---|---|
| Framework | React Native 0.81 / Expo SDK 54 |
| Language | TypeScript (strict) |
| Navigation | React Navigation v7 (native stack) |
| Persistence | @react-native-async-storage/async-storage |
| SVG | react-native-svg (circular timer ring) |
| Slider | @react-native-community/slider |
| Web target | react-native-web + @expo/metro-runtime |
LockerApp/
├── App.tsx # Root: SafeAreaProvider + AppNavigator
├── index.ts # Expo entry point
├── app.json # Expo config (dark, portrait, slug)
├── assets/ # App icons, splash image, favicon
├── src/
│ ├── theme/
│ │ └── index.ts # Colors, spacing, fontSize, borderRadius
│ ├── types/
│ │ └── index.ts # AppInfo, FocusSession, UserPreferences, FocusStats, RootStackParamList
│ ├── data/
│ │ └── apps.ts # Dummy app catalog (15 apps, categories, icons)
│ ├── services/
│ │ └── storage.ts # AsyncStorage CRUD: preferences, sessions, stats, streak logic
│ ├── hooks/
│ │ └── useFocusSession.ts # React hook wrapping storage service
│ ├── navigation/
│ │ └── AppNavigator.tsx # Stack: Onboarding → Home → AppSelection / FocusActive / LockedPrompt
│ ├── components/
│ │ ├── CircularTimer.tsx # SVG ring timer
│ │ ├── PillBadge.tsx # Status pill (default / active / count variants)
│ │ ├── AppIcon.tsx # Rounded emoji icon with optional label
│ │ ├── ActionCard.tsx # Tappable card (primary / danger variants)
│ │ └── TimePresetButton.tsx # Preset duration pill button
│ └── screens/
│ ├── OnboardingScreen.tsx # Welcome + Get Started (skips if already seen)
│ ├── HomeScreen.tsx # Hub: stats bar, timer ring, presets, app row, Start Focus
│ ├── AppSelectionScreen.tsx # Sectioned list with search, Select All / Clear, Done saves to storage
│ ├── FocusActiveScreen.tsx # Countdown, progress bar, long-press end early, saves session
│ └── LockedPromptScreen.tsx # Modal: remaining time, "session ends at", action cards
└── database/
└── schema.sql # PostgreSQL schema (future backend reference — not connected)
All data lives in AsyncStorage under two keys:
| Key | Type | Description |
|---|---|---|
@lockerapp:preferences |
UserPreferences |
Default duration, selected app IDs, onboarding flag |
@lockerapp:sessions |
FocusSession[] |
Rolling history, capped at 200 entries |
Stats (streak, total minutes, completion rate) are computed on read from the sessions array — no separate storage needed.
- Node.js 20+
- npm or yarn
- Expo Go on your phone or a web browser
git clone https://github.com/<your-username>/LockerApp.git
cd LockerApp
npm install
# Web browser (no phone needed)
npm run web
# Expo Go on your phone (scan QR code)
npm startnpm run typecheckOnboarding
│ (first launch only)
▼
Home ──────────────────────────┐
│ │
│ [Select apps] │ [reload on focus]
▼ │
AppSelection ─── Done ─────────┘
│
Home ──── [Start Focus] ────► FocusActive
│
│ [Simulate blocked tap]
▼
LockedPrompt (modal)
│
└── goBack → FocusActive
└── popToTop → Home
The web export of LockerApp can be containerized and served via Nginx. This lets anyone run the app locally without installing Node.js or Expo.
What you get: The full UI running in a browser. AsyncStorage falls back to
localStorageon web, so preferences and session history still persist between page refreshes.
- Docker Desktop installed and running
# Build image and start on port 3000
npm run docker:up
# or manually:
docker compose up --buildThen open http://localhost:3000 in your browser.
npm run docker:build # builds image tagged "lockerapp"
npm run docker:run # runs on http://localhost:3000npm run docker:dev
# Metro bundler available at http://localhost:8081| File | Purpose |
|---|---|
Dockerfile |
Multi-stage build: Node 20 builds the static bundle → Nginx 1.27 serves it |
nginx.conf |
SPA routing fallback, 1-year asset caching, gzip, security headers |
docker-compose.yml |
lockerapp service (production) + lockerapp-dev service (dev, opt-in via --profile dev) |
.dockerignore |
Excludes node_modules, build outputs, .git, IDE files from the build context |
React Native compiles to native binaries per platform. Docker is a Linux container runtime — it has no iOS simulator or Android emulator. For native builds you need Xcode (Mac only) or EAS Build (cloud).
| Limitation | Reason |
|---|---|
| No push notifications when session ends | expo-notifications is a planned follow-up. |
| No backend / sync | All data is local. The database/schema.sql is a reference schema for a potential future backend. But I keep it simple, so people just need to download and use it, no need to log in. |
- Local notification when focus session completes
- Session history screen with daily summary chart
- Custom motivational quotes (user editable)
- Apple Screen Time integration (requires paid developer account + Mac/EAS Build)
- Android Digital Wellbeing API integration
MIT



