Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as Font from 'expo-font';
import * as Notifications from 'expo-notifications';
import * as SplashScreen from 'expo-splash-screen';
import { StatusBar } from 'expo-status-bar';
Expand Down Expand Up @@ -28,6 +27,11 @@ import {
subscribeToCacheStatus,
} from './src/services/api';
import { warmCriticalCaches } from './src/services/cacheWarming';
import {
CRITICAL_FONTS,
fontService,
SECONDARY_FONTS,
} from './src/services/fontService';
import { crashReportingService } from './src/services/crashReporting';
import { featureCapabilities } from './src/services/featureCapabilities';
import { inAppReviewService } from './src/services/inAppReview';
Expand Down Expand Up @@ -197,11 +201,10 @@ const App = () => {
useEffect(() => {
async function prepareApp() {
try {
// 1. Load fonts
await Font.loadAsync({
'Inter-Regular': require('./assets/fonts/Inter-Regular.ttf'),
'Inter-Bold': require('./assets/fonts/Inter-Bold.ttf'),
});
// 1. Load critical fonts (used on first screen) synchronously before splash hides
const fontStart = Date.now();
await fontService.loadFonts(CRITICAL_FONTS);
console.log(`[App] Critical fonts loaded in ${Date.now() - fontStart}ms`);

// 2. Version-based cache invalidation: clear stale caches on app/data version bump
const appVersion = require('./package.json').version as string;
Expand All @@ -220,6 +223,16 @@ const App = () => {
prepareApp();
}, []);

useEffect(() => {
if (!appIsReady) return;

InteractionManager.runAfterInteractions(async () => {
const start = Date.now();
await fontService.loadFonts(SECONDARY_FONTS);
console.log(`[App] Secondary fonts loaded in ${Date.now() - start}ms`);
});
}, [appIsReady]);

useEffect(() => {
// ===== CRITICAL PATH — runs immediately =====
// These tasks are essential for core app functionality and must complete
Expand Down
44 changes: 42 additions & 2 deletions src/services/fontService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import { Asset } from 'expo-asset';
import { loadAsync } from 'expo-font';
import * as Font from 'expo-font';
import { Platform } from 'react-native';

// Font metadata interface
Expand Down Expand Up @@ -151,7 +151,7 @@ class FontService {

try {
// Load the font
await loadAsync({ [name]: source });
await Font.loadAsync({ [name]: source });
this.loadedFonts.add(name);

// Update metadata
Expand Down Expand Up @@ -256,6 +256,20 @@ class FontService {
};
}

// Load an array of font entries with timing tracking
async loadFonts(fonts: { name: string; source: string | number }[]): Promise<void> {
const start = Date.now();
const entries = fonts.map(f => ({ [f.name]: f.source }));
const flat = Object.assign({}, ...entries);
await Font.loadAsync(flat);
entries.forEach(e => {
const name = Object.keys(e)[0];
this.loadedFonts.add(name);
});
const elapsed = Date.now() - start;
console.log(`[FontService] Loaded ${fonts.length} font(s) in ${elapsed}ms`);
}

// Preload critical fonts
async preloadCriticalFonts(fonts: { name: string; source: string | number }[]): Promise<{
loaded: string[];
Expand Down Expand Up @@ -294,6 +308,32 @@ class FontService {
// Singleton instance
export const fontService = new FontService();

// Font entry for loading
export interface FontEntry {
name: string;
source: any;
}

// All available font variants mapped to asset sources
export const FONTS = {
'Inter-Regular': require('../../assets/fonts/Inter-Regular.ttf'),
'Inter-Bold': require('../../assets/fonts/Inter-Bold.ttf'),
'Inter-Medium': require('../../assets/fonts/Inter-Medium.ttf'),
'Inter-SemiBold': require('../../assets/fonts/Inter-SemiBold.ttf'),
} as const;

// Fonts required for the initial screen render (loaded before splash hide)
export const CRITICAL_FONTS: FontEntry[] = [
{ name: 'Inter-Regular', source: FONTS['Inter-Regular'] },
{ name: 'Inter-Medium', source: FONTS['Inter-Medium'] },
{ name: 'Inter-Bold', source: FONTS['Inter-Bold'] },
];

// Fonts loaded lazily after initial render completes
export const SECONDARY_FONTS: FontEntry[] = [
{ name: 'Inter-SemiBold', source: FONTS['Inter-SemiBold'] },
];

// Utility functions for font optimization
export const fontOptimization = {
// Calculate font subset based on character usage
Expand Down
Loading