Tapost (from Tagalog "tapos" = done, blended with "post") is a task-and-alarm productivity mobile & web application.
Unlike traditional task planners where alarms must be configured separately in a phone's clock app, Tapost builds the session alarm directly into each task:
- Reservation: User schedules a task with a
reserved_startandreserved_endtime. The task stays in apendingstate. - Deliberate Start: Starting a session requires a explicit manual tap on "Start".
- Full Planned Length: Upon tapping Start, the timer runs for the task's full planned duration (
reserved_end - reserved_start), regardless of when Start was tapped. - Local Session Alarm: When the countdown completes, Tapost fires an on-screen alert, custom audio chime, vibration, and local notification.
- Prompt: The user can Mark Done, Snooze (adds 5 min), or Dismiss.
When a task session timer ends, Tapost triggers an audible alarm, repeating vibration, and a full-screen alert. The table below details which platform guarantees are fully met out-of-the-box in Expo managed workflow vs. which require a native config plugin or bare workflow:
| Requirement / Guarantee | Android Expo Managed | Android Bare / Notifee Plugin | iOS Expo Managed | Status & Implementation Details |
|---|---|---|---|---|
High Importance Channel (IMPORTANCE_MAX / HIGH) |
✅ Fully Met | ✅ Fully Met | N/A | Created via notificationService.ts with custom alarm_chime.wav raw sound resource and max importance. |
Exact Alarm Triggers under Doze Mode (SCHEDULE_EXACT_ALARM, USE_EXACT_ALARM) |
✅ Fully Met | ✅ Fully Met | ✅ Best Effort | Declared in app.json permissions list. Prevents 5–15 min Doze mode delays on Android 12+. |
Battery Optimization Exemption (REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) |
✅ Fully Met via Intent | ✅ Fully Met via Intent | N/A | Interactive rationale modal in Settings & Session screen requesting exemption from OEM battery suppressors (Samsung, Xiaomi, etc.). |
Full-Screen Activity over Lock Screen (android:fullScreenIntent) |
✅ Fully Met (Notifee.displayNotification) |
expo-notifications displays heads-up alerts. Launching full-screen Intent over lock screen requires Notifee or custom Expo config plugin (@notifee/react-native). |
||
| Continuous Sound & Vibration Loop | ✅ Fully Met | ✅ Fully Met | ✅ Fully Met | Audio & Vibration repeat continuously via audioService & notificationService until user taps Stop, Snooze, or Mark Done. |
| Alarm when App Force-Killed | ✅ Fully Met via Foreground Service | ❌ OS Restriction | iOS sandbox strictly forbids JS execution when app is force-closed. Android uses native AlarmManager exact alarm intent. |
- Framework: React / React Native Expo architecture with Vite preview support
- State Engine: Zustand (
stores/taskStore.ts) with reactive tick interval & audio synth triggers - Database Layer: Isolated Repository pattern (
services/taskRepository.ts) wrapping local SQLite / IndexedDB persistence - Validation: React Hook Form + Zod schema validation
- Styling: Option A Palette (
#0F6E56primary deep teal,#E1F5EElight surface,#993C1Dcoral alert accent) with NativeWind / Tailwind CSS
To run or build locally on Android via Android Studio / Expo prebuild:
npx expo prebuild --platform android
npx expo run:androidIn Android 12+ (API level 31+), Android restricts exact alarm triggers by default to optimize battery usage.
To ensure alarms fire precisely to the second when a session ends, add the following to app.json:
{
"expo": {
"plugins": [
[
"expo-notifications",
{
"sounds": ["./assets/alarm_chime.wav"]
}
]
],
"android": {
"permissions": [
"SCHEDULE_EXACT_ALARM",
"USE_EXACT_ALARM",
"VIBRATE",
"POST_NOTIFICATIONS"
]
}
}
}On iOS, local notifications scheduled via expo-notifications will fire if the app is foregrounded or suspended in memory. However, if the user force-quits the app from the iOS App Switcher, scheduled JS intervals and local Web Audio triggers cannot execute until the app is reopened. This is a known iOS operating system sandbox constraint.
- Dev Server:
npm run dev(Runs on http://localhost:3000) - Build:
npm run build