@applicaster/zapp-react-native-utils
Version:
Applicaster Zapp React Native utilities package
77 lines (62 loc) • 2.75 kB
text/typescript
import { createLogger } from "../../logger";
const { log_error } = createLogger({
category: "AccessibilityManager",
subsystem: "AppUtils",
});
/**
* Calculates the reading time for a given text based on word count
* @param text - The text to calculate the reading time for (string or number)
* @param wordsPerMinute - Words per minute reading speed (default: 140)
* @param minimumPause - Minimum pause time in milliseconds (default: 500)
* @param announcementDelay - Additional delay for announcement in milliseconds (default: 700)
* @returns The reading time in milliseconds
*/
export function calculateReadingTime(
text: string | number,
wordsPerMinute: number = 140,
minimumPause: number = 500,
announcementDelay: number = 700
): number {
if (typeof text !== "string" && typeof text !== "number") {
log_error(
`Invalid text input for reading time calculation got: ${
typeof text === "symbol" ? String(text) : text
}`
);
return 0;
}
const trimmed = typeof text === "number" ? String(text) : text.trim();
if (!trimmed) {
return 0;
}
// Count words (split on whitespace and punctuation, keep alnum boundaries)
const words = trimmed
.split(/(?<=\d)(?=[a-zA-Z])|(?<=[a-zA-Z])(?=\d)|[^\w\s]+|\s+/)
.filter(Boolean).length;
// Count spaces - multiple consecutive spaces add extra pause time
const spaceMatches: string[] = trimmed.match(/\s+/g) || [];
const totalSpaces = spaceMatches.reduce(
(sum: number, match: string) => sum + match.length,
0
);
const extraSpaces = Math.max(0, totalSpaces - (words - 1)); // words-1 is normal spacing
// Heuristic: punctuation increases TTS duration beyond word-based WPM.
// Commas typically introduce short pauses, sentence terminators longer ones.
const commaCount = (trimmed.match(/,/g) || []).length;
const semicolonCount = (trimmed.match(/;/g) || []).length;
const colonCount = (trimmed.match(/:/g) || []).length;
const dashCount = (trimmed.match(/\u2013|\u2014|-/g) || []).length; // – — -
const sentenceEndCount = (trimmed.match(/[.!?](?!\d)/g) || []).length;
const commaPauseMs = 220; // short prosody pause for ","
const midPauseMs = 260; // for ";", ":", dashes
const sentenceEndPauseMs = 420; // for ".", "!", "?"
const extraSpacePauseMs = 50; // per extra space beyond normal spacing
const punctuationPause =
commaCount * commaPauseMs +
(semicolonCount + colonCount + dashCount) * midPauseMs +
sentenceEndCount * sentenceEndPauseMs +
extraSpaces * extraSpacePauseMs;
const baseByWordsMs = (words / wordsPerMinute) * 60 * 1000;
const estimatedMs = Math.max(minimumPause, baseByWordsMs + punctuationPause);
return estimatedMs + announcementDelay;
}