Reading Analytics
Comprehensive reading analytics system that tracks progress, sessions, reading speed, and generates personalized insights.
All analytics features are completely FREE!
Overview
The Analytics system provides:
- Reading session tracking
- Progress monitoring
- Time spent calculation
- Reading speed analytics
- Engagement scoring
- Page heatmaps
- Personalized insights
- Export analytics data
Quick Start
import { AnalyticsManager } from 'react-native-pdf-jsi/src/analytics/AnalyticsManager';
import { useAnalytics } from 'react-native-pdf-jsi/src/analytics/hooks/useAnalytics';
// Using the manager
const manager = new AnalyticsManager();
await manager.initialize();
const analytics = await manager.getAnalytics('my-pdf-id');
console.log('Completion:', analytics.readingMetrics.completionRate + '%');
console.log('Time spent:', analytics.timeAnalytics.totalTimeFormatted);
console.log('Reading speed:', analytics.readingMetrics.pagesPerHour, 'pages/hour');
// Or use the React hook
const { analytics, updateProgress } = useAnalytics('my-pdf-id');
Reading Metrics
Track key reading statistics:
const analytics = await manager.getAnalytics(pdfId);
const metrics = analytics.readingMetrics;
console.log('Pages read:', metrics.totalPagesRead);
console.log('Total pages:', metrics.totalPages);
console.log('Completion:', metrics.completionRate + '%');
console.log('Pages per hour:', metrics.pagesPerHour);
console.log('Minutes per page:', metrics.minutesPerPage);
console.log('Reading speed:', metrics.wordsPerMinute, 'WPM');
console.log('Est. total time:', metrics.estimatedTotalTime);
console.log('Est. remaining:', metrics.estimatedTimeRemaining);
Reading Metrics Object:
{
pagesPerHour: number;
minutesPerPage: number;
completionRate: number; // 0-100
wordsPerMinute: number; // Estimated WPM
totalPagesRead: number;
totalPages: number;
timeSpent: number; // Seconds
estimatedTotalTime: number; // Seconds
estimatedTimeRemaining: number; // Seconds
}
Time Analytics
Detailed time tracking:
const timeAnalytics = analytics.timeAnalytics;
console.log('Total time:', timeAnalytics.totalTimeFormatted); // "2h 30m"
console.log('Session count:', timeAnalytics.totalSessions);
console.log('Avg session:', timeAnalytics.avgSessionDuration, 'minutes');
console.log('Last session:', new Date(timeAnalytics.lastSession));
console.log('Reading streak:', timeAnalytics.readingStreak, 'days');
Time Analytics Object:
{
totalTime: number; // Seconds
totalTimeFormatted: string; // "2h 30m 15s"
totalSessions: number;
avgSessionDuration: number; // Minutes
longestSession: number; // Minutes
shortestSession: number; // Minutes
lastSession: number; // Timestamp
readingStreak: number; // Days
timeDistribution: object; // Hour-by-hour breakdown
}
Engagement Metrics
Measure user engagement with the document:
const engagement = analytics.engagementMetrics;
console.log('Total bookmarks:', engagement.totalBookmarks);
console.log('Bookmarked pages:', engagement.uniqueBookmarkedPages);
console.log('Bookmark rate:', engagement.bookmarkRate + '%');
console.log('Engagement score:', engagement.engagementScore, '/100');
Engagement Score Calculation:
- Completion contributes 40 points
- Bookmarks contribute 30 points
- Session frequency contributes 30 points
- Total: 0-100
Page Analytics
Analyze reading patterns and page access:
const pageAnalytics = analytics.pageAnalytics;
// Most bookmarked pages
pageAnalytics.mostBookmarkedPages.forEach(p => {
console.log(`Page ${p.page}: ${p.bookmarks} bookmarks`);
});
// Reading gaps (unread sections)
pageAnalytics.readingGaps.forEach(gap => {
console.log(`Unread: pages ${gap.start}-${gap.end}`);
});
// Reading pattern
console.log('Pattern:', pageAnalytics.readingPattern);
// "sequential", "random", "chapter-by-chapter", etc.
// Page heatmap
Object.entries(pageAnalytics.heatmap).forEach(([page, count]) => {
console.log(`Page ${page}: visited ${count} times`);
});
Insights Generation
Get personalized reading insights:
const insights = analytics.insights;
insights.forEach(insight => {
console.log(`[${insight.type}] ${insight.title}`);
console.log(` ${insight.message}`);
console.log(` Priority: ${insight.priority}`);
});
Insight Types:
completion- Progress-based insightsspeed- Reading speed insightsengagement- Engagement insightspattern- Reading pattern insightsrecommendation- Personalized recommendations
Example Insights:
[completion] Great Progress!
You've completed 75% of this document.
Priority: high
[speed] Fast Reader
Your reading speed is above average at 280 WPM.
Priority: medium
[engagement] Active Learner
You've created 15 bookmarks - excellent engagement!
Priority: high
Complete Example: Analytics Dashboard
import React, { useState, useEffect } from 'react';
import { View, Text, ScrollView, RefreshControl } from 'react-native';
import { useAnalytics } from 'react-native-pdf-jsi/src/analytics/hooks/useAnalytics';
const AnalyticsDashboard = ({ pdfId }) => {
const { analytics, loading, error, refresh } = useAnalytics(pdfId);
if (loading) {
return <ActivityIndicator size="large" />;
}
if (error) {
return <Text>Error loading analytics</Text>;
}
if (!analytics) {
return <Text>No analytics data available</Text>;
}
const { readingMetrics, engagementMetrics, timeAnalytics, insights } = analytics;
return (
<ScrollView
style={{ flex: 1 }}
refreshControl={
<RefreshControl refreshing={loading} onRefresh={refresh} />
}
>
{/* Progress Overview */}
<View style={{ padding: 20, backgroundColor: '#6366F1' }}>
<Text style={{ color: 'white', fontSize: 24, fontWeight: 'bold' }}>
{readingMetrics.completionRate}%
</Text>
<Text style={{ color: 'white' }}>Complete</Text>
<Text style={{ color: 'white', marginTop: 10 }}>
{readingMetrics.totalPagesRead} of {readingMetrics.totalPages} pages
</Text>
</View>
{/* Reading Stats */}
<View style={{ padding: 20 }}>
<Text style={{ fontSize: 18, fontWeight: 'bold', marginBottom: 15 }}>
Reading Statistics
</Text>
<StatCard
label="Time Spent"
value={timeAnalytics.totalTimeFormatted}
icon="⏱️"
/>
<StatCard
label="Reading Speed"
value={`${readingMetrics.pagesPerHour} pages/hour`}
icon="⚡"
/>
<StatCard
label="Sessions"
value={timeAnalytics.totalSessions}
icon="📚"
/>
<StatCard
label="Bookmarks"
value={engagementMetrics.totalBookmarks}
icon="🔖"
/>
</View>
{/* Insights */}
<View style={{ padding: 20 }}>
<Text style={{ fontSize: 18, fontWeight: 'bold', marginBottom: 15 }}>
Insights
</Text>
{insights.slice(0, 3).map((insight, index) => (
<View
key={index}
style={{
padding: 15,
marginBottom: 10,
backgroundColor: '#f5f5f5',
borderRadius: 8,
borderLeftWidth: 4,
borderLeftColor: getPriorityColor(insight.priority)
}}
>
<Text style={{ fontWeight: 'bold' }}>{insight.title}</Text>
<Text style={{ color: '#666', marginTop: 5 }}>{insight.message}</Text>
</View>
))}
</View>
{/* Most Bookmarked Pages */}
{analytics.pageAnalytics.mostBookmarkedPages.length > 0 && (
<View style={{ padding: 20 }}>
<Text style={{ fontSize: 18, fontWeight: 'bold', marginBottom: 15 }}>
Most Bookmarked Pages
</Text>
{analytics.pageAnalytics.mostBookmarkedPages.map((page, index) => (
<View
key={index}
style={{
flexDirection: 'row',
justifyContent: 'space-between',
padding: 10,
marginBottom: 5,
backgroundColor: '#f9f9f9',
borderRadius: 5
}}
>
<Text>Page {page.page}</Text>
<Text style={{ color: '#6366F1', fontWeight: 'bold' }}>
{page.bookmarks} bookmarks
</Text>
</View>
))}
</View>
)}
</ScrollView>
);
};
const StatCard = ({ label, value, icon }) => (
<View style={{
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
padding: 15,
marginBottom: 10,
backgroundColor: '#f9f9f9',
borderRadius: 8
}}>
<View>
<Text style={{ fontSize: 12, color: '#666' }}>{label}</Text>
<Text style={{ fontSize: 18, fontWeight: 'bold', marginTop: 5 }}>
{value}
</Text>
</View>
<Text style={{ fontSize: 30 }}>{icon}</Text>
</View>
);
const getPriorityColor = (priority) => {
switch (priority) {
case 'high': return '#FF6B6B';
case 'medium': return '#FFD93D';
case 'low': return '#4ECDC4';
default: return '#999';
}
};
export default AnalyticsDashboard;
useAnalytics Hook
React hook for easy analytics integration:
import { useAnalytics } from 'react-native-pdf-jsi/src/analytics/hooks/useAnalytics';
const MyComponent = ({ pdfId }) => {
const {
analytics,
loading,
error,
refresh,
updateProgress,
trackSession
} = useAnalytics(pdfId, {
autoRefresh: true, // Auto-refresh every 30 seconds
refreshInterval: 30000
});
// Update progress on page change
const handlePageChange = (page, totalPages) => {
updateProgress({ currentPage: page, totalPages });
};
// Track reading session
useEffect(() => {
trackSession('start');
return () => trackSession('end');
}, []);
return (
<View>
{analytics && (
<Text>
Reading: {analytics.readingMetrics.completionRate}% complete
</Text>
)}
</View>
);
};
Export Analytics
Export analytics data for external analysis:
const exportData = await manager.exportAnalytics('my-pdf-id');
console.log('Export data:', exportData);
// Can be saved to file, sent to server, etc.
// Save to file
import RNFS from 'react-native-fs';
const exportPath = `${RNFS.DocumentDirectoryPath}/analytics.json`;
await RNFS.writeFile(exportPath, JSON.stringify(exportData, null, 2));
console.log('Analytics exported to:', exportPath);
Reading Predictions
Get predictions based on current reading patterns:
const predictions = analytics.predictions;
console.log('Estimated finish date:', predictions.estimatedFinishDate);
console.log('Estimated remaining time:', predictions.estimatedRemainingTime);
console.log('Optimal reading schedule:', predictions.suggestedSchedule);
Predictions Object:
{
estimatedFinishDate: string; // ISO date
estimatedRemainingTime: number; // Minutes
suggestedPagesPerDay: number;
suggestedSchedule: string; // "Read 15 pages/day for 10 days"
}
Real-Time Progress Tracking
Track progress as user reads:
import { BookmarkManager } from 'react-native-pdf-jsi/src/bookmarks/BookmarkManager';
const PDFReader = ({ pdfId }) => {
const [currentPage, setCurrentPage] = useState(1);
const [sessionStart] = useState(Date.now());
const manager = new BookmarkManager();
const handlePageChange = async (page, totalPages) => {
setCurrentPage(page);
// Calculate time spent
const timeSpent = Math.floor((Date.now() - sessionStart) / 1000);
// Update progress
await manager.updateProgress(pdfId, {
currentPage: page,
totalPages: totalPages,
lastReadAt: Date.now()
});
// Update session
await manager.updateSession(pdfId, {
duration: timeSpent,
pagesRead: page
});
};
return (
<Pdf
source={{ uri: pdfPath }}
onPageChanged={(page, total) => handlePageChange(page, total)}
style={{ flex: 1 }}
/>
);
};
Session Tracking
Track individual reading sessions:
// Start session
const sessionId = await manager.startSession(pdfId);
// Update session during reading
await manager.updateSession(pdfId, {
duration: timeElapsed,
pagesRead: currentPage
});
// End session
await manager.endSession(pdfId, sessionId);
// Get session history
const sessions = await manager.getSessions(pdfId);
sessions.forEach(session => {
console.log('Session on:', new Date(session.startTime));
console.log('Duration:', session.duration, 'seconds');
console.log('Pages read:', session.pagesRead);
});
Engagement Score
Get engagement score (0-100):
const engagementMetrics = analytics.engagementMetrics;
console.log('Engagement score:', engagementMetrics.engagementScore, '/100');
console.log('Total bookmarks:', engagementMetrics.totalBookmarks);
console.log('Bookmark rate:', engagementMetrics.bookmarkRate + '%');
Score Breakdown:
- 40 points: Reading completion (0-100%)
- 30 points: Bookmark usage (more bookmarks = higher score)
- 30 points: Session frequency (regular reading sessions)
Interpretation:
- 80-100: Highly engaged reader
- 60-79: Moderately engaged
- 40-59: Casual reader
- 0-39: Low engagement
Page Heatmap
Visualize page access frequency:
const heatmap = analytics.pageAnalytics.heatmap;
// Heatmap: { page: visitCount }
// { 1: 5, 5: 3, 10: 8, ... }
// Find most visited pages
const mostVisited = Object.entries(heatmap)
.sort((a, b) => b[1] - a[1])
.slice(0, 5);
mostVisited.forEach(([page, count]) => {
console.log(`Page ${page}: visited ${count} times`);
});
Reading Pattern Analysis
Identify reading patterns:
const pattern = analytics.pageAnalytics.readingPattern;
console.log('Reading pattern:', pattern);
// Possible values:
// - "sequential" - Reading in order
// - "random" - Jumping around
// - "chapter-by-chapter" - Reading by sections
// - "selective" - Reading specific pages
Reading Gaps
Find unread sections:
const gaps = analytics.pageAnalytics.readingGaps;
gaps.forEach(gap => {
console.log(`Unread section: pages ${gap.start} to ${gap.end}`);
console.log(` Pages: ${gap.pages.length}`);
});
// Suggest reading gaps to user
if (gaps.length > 0) {
Alert.alert(
'Unread Sections',
`You have ${gaps.length} unread sections. Would you like to continue reading?`
);
}
Complete Analytics Example
Full implementation with all features:
import React, { useState, useEffect } from 'react';
import { View, Text, ScrollView } from 'react-native';
import { AnalyticsManager } from 'react-native-pdf-jsi/src/analytics/AnalyticsManager';
import { CircularProgress, StatCard } from 'react-native-pdf-jsi/src/analytics/components/SimpleCharts';
const ComprehensiveAnalytics = ({ pdfId }) => {
const [analytics, setAnalytics] = useState(null);
const [manager] = useState(() => new AnalyticsManager());
useEffect(() => {
loadAnalytics();
const interval = setInterval(loadAnalytics, 30000); // Refresh every 30s
return () => clearInterval(interval);
}, [pdfId]);
const loadAnalytics = async () => {
try {
await manager.initialize();
const data = await manager.getAnalytics(pdfId);
setAnalytics(data);
} catch (error) {
console.error('Analytics error:', error);
}
};
if (!analytics) {
return <Text>Loading analytics...</Text>;
}
const { readingMetrics, timeAnalytics, engagementMetrics, insights } = analytics;
return (
<ScrollView style={{ flex: 1, backgroundColor: '#f5f5f5' }}>
{/* Progress Ring */}
<View style={{ alignItems: 'center', padding: 30, backgroundColor: 'white' }}>
<CircularProgress
percentage={readingMetrics.completionRate}
size={150}
strokeWidth={12}
color="#6366F1"
/>
<Text style={{ marginTop: 20, fontSize: 24, fontWeight: 'bold' }}>
{readingMetrics.completionRate}% Complete
</Text>
<Text style={{ color: '#666' }}>
{readingMetrics.totalPagesRead} of {readingMetrics.totalPages} pages
</Text>
</View>
{/* Stats Grid */}
<View style={{ padding: 20 }}>
<View style={{ flexDirection: 'row', flexWrap: 'wrap' }}>
<StatCard
label="Time Spent"
value={timeAnalytics.totalTimeFormatted}
icon="⏱️"
style={{ width: '48%', margin: '1%' }}
/>
<StatCard
label="Reading Speed"
value={`${readingMetrics.pagesPerHour}/hr`}
icon="⚡"
style={{ width: '48%', margin: '1%' }}
/>
<StatCard
label="Sessions"
value={timeAnalytics.totalSessions}
icon="📚"
style={{ width: '48%', margin: '1%' }}
/>
<StatCard
label="Engagement"
value={`${engagementMetrics.engagementScore}/100`}
icon="🎯"
style={{ width: '48%', margin: '1%' }}
/>
</View>
</View>
{/* Insights */}
<View style={{ padding: 20 }}>
<Text style={{ fontSize: 18, fontWeight: 'bold', marginBottom: 15 }}>
Insights
</Text>
{insights.map((insight, index) => (
<View
key={index}
style={{
padding: 15,
marginBottom: 10,
backgroundColor: 'white',
borderRadius: 8,
borderLeftWidth: 4,
borderLeftColor: getPriorityColor(insight.priority)
}}
>
<Text style={{ fontWeight: 'bold', marginBottom: 5 }}>
{insight.title}
</Text>
<Text style={{ color: '#666' }}>{insight.message}</Text>
</View>
))}
</View>
{/* Most Bookmarked Pages */}
{analytics.pageAnalytics.mostBookmarkedPages.length > 0 && (
<View style={{ padding: 20 }}>
<Text style={{ fontSize: 18, fontWeight: 'bold', marginBottom: 15 }}>
Most Bookmarked
</Text>
{analytics.pageAnalytics.mostBookmarkedPages.map((page, index) => (
<View
key={index}
style={{
flexDirection: 'row',
justifyContent: 'space-between',
padding: 15,
marginBottom: 5,
backgroundColor: 'white',
borderRadius: 5
}}
>
<Text>Page {page.page}</Text>
<Text style={{ color: '#6366F1', fontWeight: 'bold' }}>
{page.bookmarks} bookmarks
</Text>
</View>
))}
</View>
)}
</ScrollView>
);
};
const getPriorityColor = (priority) => {
const colors = {
high: '#FF6B6B',
medium: '#FFD93D',
low: '#4ECDC4'
};
return colors[priority] || '#999';
};
export default ComprehensiveAnalytics;
Export Analytics Data
const exportedData = await manager.exportAnalytics('my-pdf-id');
// Exported data includes all analytics
const data = {
pdfId: 'my-pdf-id',
readingMetrics: { ... },
timeAnalytics: { ... },
engagementMetrics: { ... },
pageAnalytics: { ... },
insights: [ ... ],
bookmarks: [ ... ],
sessions: [ ... ],
exportedAt: '2025-01-06T...'
};
// Use for:
// - Backup
// - Cloud sync
// - External analysis
// - Sharing with study groups
Best Practices
1. Initialize Once
const [manager] = useState(() => new AnalyticsManager());
useEffect(() => {
manager.initialize();
}, []);
2. Update Progress Frequently
// Update on every page change
onPageChanged={(page, total) => {
manager.updateProgress(pdfId, { currentPage: page, totalPages: total });
}}
3. Track Sessions
useEffect(() => {
manager.startSession(pdfId);
return () => manager.endSession(pdfId);
}, [pdfId]);
4. Refresh Analytics Periodically
useEffect(() => {
const interval = setInterval(() => {
refresh();
}, 30000); // Every 30 seconds
return () => clearInterval(interval);
}, []);