Skip to content

About

React Native version of athan.uk (web app). IOS & Android app that additionally supports notifications, audio and more.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Latest commit

 

History

4,975 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Mosque icon

Athan.uk

A React Native app for Muslim prayer times in London, UK.

Web: athan.uk | iOS: ios.athan.uk | Android: athan.uk

Features

  • Six daily prayers plus five extra times (Midnight, Last Third, Suhoor, Duha, Friday Istijaba) with a live countdown to the next prayer.
  • The schedule rolls over after the last prayer of the day instead of midnight. Tomorrow sits one swipe away.
  • Fully offline after the first sync. The whole year is cached in MMKV, and an unreadable time shows --:--, never a guess.
  • Notifications: selectable athan audio (32 public recordings, each credited in the picker), two reminder slots per prayer, a 64-request scheduling budget and deterministic IDs so alarms never double-fire.
  • Home screen and Lock Screen widgets on iOS and Android: Light and Dark variants, self-refreshing, minute-ceil countdown labels. Widgets need a development or production build (iOS 16.4+), not Expo Go.
  • Qibla compass on a flat world map: a north-locked dial that taps when you line up with Makkah, drawing only headings the phone has vouched for.
  • Settings: large overlay font, Hijri date, Arabic prayer names, seconds, seasonal decorations, countdown bar, color picker.
  • Folding iPhone (Duo) support: held tall on the open inner display the app renders top-down across both panels with no letterbox, and there is no wide mode - the wide hold keeps the system's centered portrait frame.

Prayer times come from London Prayer Times. Reminder audio is recorded in-house. The Audacity projects for every athan and reminder sound are on the releases page.

Known limitations

  • On some Android phones (ColorOS / OxygenOS), at-time alerts can arrive about a minute late. The fix is built and proven on test devices; it ships with the next store release.
  • On a folding iPhone held wide, a process launched while the device is folded keeps the system's centered portrait frame after unfolding until its next relaunch. Held tall, the same process unfolds straight onto the full tall canvas.

Upcoming

  • Store release carrying the exact-alarm fix for ColorOS / OxygenOS phones (waits on the SDK 58 stable re-pin)

Athan audio sources

Every selectable athan sound comes from a public recording:

The 32 recordings
  1. Athan 1
  2. Athan 2
  3. Athan 3
  4. Athan 4
  5. Athan 5
  6. Athan 6
  7. Athan 7
  8. Athan 8
  9. Athan 9
  10. Athan 10
  11. Athan 11
  12. Athan 12
  13. Athan 13
  14. Athan 14
  15. Athan 15
  16. Athan 16
  17. Athan 17
  18. Athan 18
  19. Athan 19
  20. Athan 20
  21. Athan 21
  22. Athan 22
  23. Athan 23
  24. Athan 24
  25. Athan 25
  26. Athan 26
  27. Athan 27
  28. Athan 28
  29. Athan 29
  30. Athan 30
  31. Athan 31
  32. Athan 32

Stack

  • Expo SDK 58 (stable), React Native 0.88, React 19
  • TypeScript, strict mode
  • Expo Router, Jotai, MMKV, Reanimated 4
  • date-fns and date-fns-tz, with every date in the London timezone
  • Biome for lint and format, Jest at 100% coverage, Pino for logging
  • Native modules in modules/: qibla heading (Google Fused Orientation on Android, Core Location on iOS), TLS 1.3 for Android 9 and below, Android widget self-refresh

Repository layout

app/         Expo Router entry points and navigation
components/  UI components (prayer, countdown, overlay, sheets, modals)
hooks/       useQibla, useCountdown, useSchedule
stores/      Jotai atoms (schedule, notifications, countdown, sync, widgets)
shared/      utilities: constants, prayer, time, notifications, qibla, whatsNew
widgets/     iOS widget layouts and the Android Glance layout
modules/     native Expo modules: qiblaheading, tls13, widgetrefresh
device/      platform code: notifications, listeners, updates, tasks
api/         prayer times client; mocks/ holds dev and test data
e2e/         Maestro flows, device atlas, frame audit, baseline compare
board/       the local ticket board: the stack, its data and its snapshot

patches/ holds patch-package patches. ai/ holds the agent guide. android/ and ios/ are generated by npx expo prebuild and stay git-ignored.

How work happens here

This repository runs its own work on a local board: a self-hosted It's a Plan instance in board/. A ticket is an issue on that board, and its column is its state. The board holds the plan, the steps, the relations and the history.

Command What it does
/athan-next The lead reads the board, picks the runnable ticket, and routes it: plan, execute, audit, Done

The workers are agent files in .opencode/agents/ (athan-lane, athan-executor, athan-reviewer, athan-plan-griller) running as subagents with enforced permissions: the executor cannot push, the reviewers cannot edit. Every phase writes its state to the board as it lands, so any session can be closed and resumed by running the command again in a fresh one. The workflow is self-contained: cloning this repository brings the commands, the skills, the workers and the board's compose file with it.

The board

cd board && docker compose --env-file env up -d

The board is at http://localhost:3001 and the API at http://localhost:3000. Six containers run, two are one-shot setup steps, and two are listed but never start because nothing here uses webhooks, notifications or Telegram. board/README.md documents all of it.

board/data/ holds the live database and attachments and is ignored by git. board/snapshot/board.sql is the committed copy, and it is what makes the board recoverable:

board/snapshot.sh     # write snapshot/board.sql at the end of a run
board/restore.sh      # rebuild the board from it, on any machine

Reading the board: a ticket is one unit of work, a step is a plan step. The columns are Backlog, Deferred, Planning, Ready, In progress, Blocked, Audit, Awaiting Owner, Upstream and Done. The Tickets view shows the ticket number, its priority, its labels, its lane and how long it has sat where it is. Nothing is edited through the board by hand: agents write it through MCP.

Development

Prerequisites: Node.js 24+, Yarn, Xcode with an iOS simulator, Android Studio. The workflow commands need OpenCode or any agent that reads .opencode/ and .agents/.

Command Purpose
yarn reset clean, install, set up husky, start Metro
yarn ios / yarn android build and run on the simulator or emulator
yarn validate tsc, Biome and the full Jest suite with coverage: the before-done gate
yarn format Biome format and lint fixes
yarn test:tz the Jest suite across four timezones, for anything touching dates
yarn check:device <serial> audit scheduled alarms on a connected Android phone
build-prod.zsh / build-mock.zsh local device builds carrying env vars and prebuild order
maestro test e2e/flows/<flow>.yaml run a UI flow (needs export PATH="$HOME/.maestro/bin:$PATH")

Add a dependency with npx expo install <package>, never with --fix. This project intentionally runs newer jest and typescript than Expo pins.

Build values

The committed config carries placeholders only: app.json names com.example.athan and a matching widget app group, and .env.example repeats them. The published store ids are injected at build time and never committed, because a clone has to be able to deploy this as its own app:

Variable What it replaces Default
EXPO_APP_ID the Android package com.example.athan
EXPO_BUNDLE_ID the iOS bundle id com.example.athan
EXPO_APP_GROUP the widget app group group.com.example.athan
EXPO_EAS_PROJECT_ID extra.eas.projectId none
EXPO_PUBLIC_ANDROID_PACKAGE / EXPO_PUBLIC_IOS_BUNDLE_ID the ids the app reads at runtime the same placeholders

$HOME/athan-gitree/bin/build-prod.zsh and build-mock.zsh export the live values before npx expo prebuild, so a device build writes the published ids into the git-ignored native trees and badges the package it always has. A local iOS build does the same by exporting them in the shell that runs npx expo prebuild -p ios. Maestro flows take APP_ID (-e APP_ID=<package>), and the e2e scripts fall back to the placeholder.

The privacy gate

scripts/privacy-manifest.json holds, base64-encoded, the terms this repository may never carry: the owner's names, the home-path username and tool-session scratchpad paths. Three gates read it, all deterministic: .husky/pre-commit refuses a staged line or a commit identity that carries a term; yarn validate includes a suite test that scans every tracked file, and the pre-push hook runs yarn validate; board/snapshot.sh refuses to write a dump that carries one. AGENTS.md holds the placeholder vocabulary the shapes use for device and location identifiers.

Testing

  • Jest runs two projects: logic suites (.ts) and component suites (.tsx) with React Native Testing Library.
  • Coverage holds at 100%, enforced by global thresholds on statements, branches, functions and lines. A change covers every line it touches, red before green.
  • Tests never read the real clock. Time is mocked with jest.useFakeTimers({ now }).
  • Date work runs yarn test:tz across the timezone matrix.
  • e2e/README.md documents the Maestro flows, the measurement scripts and their gotchas.

Release ritual (What's New)

Every store release, edit WHATS_NEW in shared/whatsNew.ts:

  1. Stamp the items shipping in this release with the release version: new abilities, removals, behavior changes users notice. Park a draft at version: null while writing it - the contract suite stays red until it is stamped or removed, which is the ship gate. Item stamps no longer cap or gate what shows: the modal carries the newest 20 archived items (owner orders 2026-10-20 and 2026-10-21), and the archive prunes oldest-first only past 100 entries.
  2. Bump WHATS_NEW.version to the store version being submitted. The modal shows nothing unless it equals the installed version.
  3. Ship silent by leaving WHATS_NEW.version unmoved, or setting WHATS_NEW to null.

The debut modal (2.3.0) opens with every feature the app has ever shipped, because the modal itself is new to the store.

A contract test (shared/__tests__/whatsNew.test.ts) guards the shape. EXPO_PUBLIC_WHATS_NEW_PREVIEW=1 in .env forces the modal on cold launch in dev builds.

Further reading

About

React Native version of athan.uk (web app). IOS & Android app that additionally supports notifications, audio and more.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages