Overview
The Reading Tracker (الورد) helps you build a consistent Quran reading habit by tracking your daily progress and comparing it against customizable goals. The system automatically calculates progress based on Hizb divisions of the Quran.
How It Works
Set Daily Goal
Choose how many Ahzab (portions) you want to read daily
Read Quran
Navigate through the Mushaf normally as you read
Automatic Tracking
Progress is calculated automatically based on pages read since yesterday
Goal Completion
Receive notification when you complete your daily goal
Daily Reset
Progress resets automatically at midnight for a fresh start
Understanding Hizb Divisions
The Quran is divided into 60 Ahzab (حزب in singular) for easy tracking:
1 Hizb = 1/60th of the Quran
1 Hizb = Approximately 10 pages
8 Thumns (أثمان) = 1 Hizb
The tracker uses the Thumns system internally (8 Thumns = 1 Hizb) for more precise progress calculation.
Progress Calculation
Automatic Tracking
components/MushafPage.tsx
useEffect (() => {
if ( typeof currentPage === 'number' ) {
// Calculate thumns read between yesterday's page and current page
const numberOfThumn = calculateThumnsBetweenPages (
yesterdayPageValue . value ,
currentPage ,
thumnData ,
);
// Update the progress state (convert thumns to ahzab)
setdailyTrackerCompletedValue ({
value: numberOfThumn / 8 ,
date: new Date (). toDateString (),
});
}
}, [ currentPage , yesterdayPageValue , thumnData ]);
Progress Calculation Logic
export function calculateThumnsBetweenPages (
startPage : number ,
endPage : number ,
thumnData : ThumnData [],
) : number {
// Count how many Thumns (eighths) are between two pages
// Returns the number of Thumns completed
}
Progress is calculated by comparing your current page with the last page you read yesterday, counting all Thumns in between.
Goal Management
Setting Goals
const incrementDailyGoal = () => setdailyTrackerGoalValue (( prev ) => prev + 1 );
const decrementDailyGoal = () =>
setdailyTrackerGoalValue (( prev ) => Math . max ( 1 , prev - 1 ));
Goals are adjusted in full Hizb increments:
< ThemedView style = { styles . controls } >
< TouchableOpacity onPress = { decrementDailyGoal } >
< Feather name = "minus" size = { 20 } color = { primaryColor } />
</ TouchableOpacity >
< ThemedText style = { styles . controlValue } >
{ getHizbText ( dailyTrackerGoalValue ) }
</ ThemedText >
< TouchableOpacity onPress = { incrementDailyGoal } >
< Feather name = "plus" size = { 20 } color = { primaryColor } />
</ TouchableOpacity >
</ ThemedView >
Arabic Pluralization
The tracker uses proper Arabic grammar for displaying counts:
const getHizbText = ( count : number ) => {
const hizbCount = count ;
if ( hizbCount === 0 ) return '0 أحزاب' ;
if ( hizbCount === 1 ) return 'حزب واحد' ;
if ( hizbCount === 2 ) return 'حزبين' ;
if ( hizbCount >= 3 && hizbCount <= 10 ) return ` ${ hizbCount } أحزاب` ;
return ` ${ hizbCount } حزباً` ;
};
Progress Display
Visual Progress Bar
const dailyProgress =
dailyTrackerGoalValue > 0
? Math . min (
100 ,
( dailyTrackerCompletedValue . value / 8 / ( dailyTrackerGoalValue / 8 )) *
100 ,
)
: 0 ;
< ThemedView style = { styles . progressContainer } >
< ThemedView
style = { [
styles . progressBar ,
{ width: ` ${ dailyProgress } %` , backgroundColor: primaryColor },
] }
/>
< ThemedText style = { styles . progressText } >
{ dailyProgress . toFixed ( 1 ) } %
</ ThemedText >
</ ThemedView >
< ThemedText style = { styles . infoText } >
قراءة { ' ' }
{ Number . isInteger ( dailyTrackerCompletedValue . value )
? getHizbText ( dailyTrackerCompletedValue . value )
: ` ${ dailyTrackerCompletedValue . value . toFixed ( 1 ) } حزباً` }{ ' ' }
من أصل { getHizbText ( dailyTrackerGoalValue ) }
</ ThemedText >
The progress bar shows your completion percentage, with text that changes color based on progress (white text when >50% for better visibility).
Notifications
Goal Completion Notification
components/MushafPage.tsx
useEffect (() => {
progressValue === 1 && setShowGoalNotification ( true );
}, [ progressValue ]);
useEffect (() => {
if ( showTrackerNotificationValue && showGoalNotification ) {
notify (
'تم إكمال الورد اليومي بنجاح' ,
'tracker_goal_notification' ,
'neutral' ,
);
}
}, [ notify , showGoalNotification , showTrackerNotificationValue ]);
When you reach 100% of your goal:
A notification appears: “تم إكمال الورد اليومي بنجاح”
Notification auto-dismisses after 3 seconds
Only shown if notifications are enabled in settings
Notification Settings
export const showTrackerNotification = createAtomWithStorage < boolean >(
'ShowTrackerNotification' ,
false ,
);
You can enable or disable goal completion notifications in the app settings.
Daily Reset System
Automatic Reset at Midnight
type DailyTrackerProgress = {
value : number ;
date : string ;
};
export const dailyTrackerCompleted =
createAtomWithStorage < DailyTrackerProgress >( 'DailyTrackerCompleted' , {
value: 0 ,
date: new Date (). toDateString (),
});
observe (( get , set ) => {
( async () => {
const stored = await get ( dailyTrackerCompleted );
const today = new Date (). toDateString ();
if ( stored . date !== today ) {
set ( dailyTrackerCompleted , { value: 0 , date: today });
}
})();
});
Yesterday Page Tracking
type PageWithDate = {
value : number ;
date : string ;
};
export const yesterdayPage = createAtomWithStorage < PageWithDate >(
'YesterdayPage' ,
{
value: 1 ,
date: new Date (). toDateString (),
},
);
observe (( get , set ) => {
( async () => {
const today = new Date (). toDateString ();
const saved = await get ( yesterdayPage );
const lastPage = await get ( currentSavedPage );
if ( saved . date !== today ) {
set ( yesterdayPage , { value: lastPage , date: today });
}
})();
});
At midnight, the app compares the stored date with today’s date. If they differ, it:
Resets your progress to 0
Updates “yesterday’s page” to your last saved page
Saves the new date to prevent multiple resets
Manual Reset
Reset Confirmation Modal
const performReset = async () => {
if ( typeof savedPage === 'number' && savedPage > 0 ) {
setYesterdayPageValue ({
value: savedPage ,
date: new Date (). toDateString (),
});
}
setdailyTrackerCompletedValue ({
value: 0 ,
date: new Date (). toDateString (),
});
// Update Android widget
await updateAndroidWidget ();
setConfirmModalVisible ( false );
};
Users can manually reset their progress:
Opens a confirmation modal to prevent accidental resets
Updates yesterday’s page to current saved page
Resets completed value to 0
Syncs with Android widget
Manual reset is useful if you want to start tracking from a new point without waiting for midnight.
Persistent Storage
Goal Storage
export const dailyTrackerGoal = createAtomWithStorage < number >(
'DailyTrackerGoal' ,
1 , // Default: 1 Hizb per day
);
Progress Storage
All tracker data is persisted:
dailyTrackerGoal - Your daily goal (in Ahzab)
dailyTrackerCompleted - Progress with date stamp
yesterdayPage - Last page from previous day with date
showTrackerNotification - Notification preference
Persistent Goals Your daily goal is saved and persists across app sessions
Date Tracking Progress includes timestamps to enable automatic daily resets
Widget Sync Android widget updates when you reset or complete goals
Automatic Calculation No manual logging needed - progress calculates as you read
Integration with Reading
The tracker seamlessly integrates with normal Mushaf navigation:
components/MushafPage.tsx
const { currentPage , setCurrentPage } = useCurrentPage ();
// Progress updates automatically as you change pages
useEffect (() => {
// Calculate and update progress based on current page
}, [ currentPage ]);
You don’t need to do anything special to track your reading - just navigate the Mushaf normally and your progress updates automatically!