← back to directedbybjk-stack__peel-app

Function bodies 41 total

All specs Real LLM only Function bodies
Root function · typescript · L7-L28 (22 LOC)
app/+html.tsx
export default function Root({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta httpEquiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />

        {/* 
          Disable body scrolling on web. This makes ScrollView components work closer to how they do on native. 
          However, body scrolling is often nice to have for mobile web. If you want to enable it, remove this line.
        */}
        <ScrollViewStyleReset />

        {/* Using raw CSS styles as an escape-hatch to ensure the background color never flickers in dark-mode. */}
        <style dangerouslySetInnerHTML={{ __html: responsiveBackground }} />
        {/* Add any additional <head> elements that you want globally available on web... */}
      </head>
      <body>{children}</body>
    </html>
  );
}
RootLayout function · typescript · L18-L58 (41 LOC)
app/_layout.tsx
export default function RootLayout() {
  const [loaded, error] = useFonts({
    SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
  });
  const [onboardingDone, setOnboardingDone] = useState<boolean | null>(null);
  const colorScheme = useColorScheme();
  const segments = useSegments();

  useEffect(() => {
    if (error) throw error;
  }, [error]);

  useEffect(() => {
    isOnboardingComplete().then(setOnboardingDone);
  }, []);

  useEffect(() => {
    if (!loaded || onboardingDone === null) return;
    SplashScreen.hideAsync();

    const inOnboarding = segments[0] === 'onboarding';

    if (!onboardingDone && !inOnboarding) {
      router.replace('/onboarding');
    } else if (onboardingDone && inOnboarding) {
      router.replace('/(tabs)');
    }
  }, [loaded, onboardingDone, segments]);

  if (!loaded || onboardingDone === null) {
    return null;
  }

  return (
    <OnboardingContext.Provider value={{ done: onboardingDone }}>
      <ThemeProvider value={colorScheme
ModalScreen function · typescript · L7-L18 (12 LOC)
app/modal.tsx
export default function ModalScreen() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Modal</Text>
      <View style={styles.separator} lightColor="#eee" darkColor="rgba(255,255,255,0.1)" />
      <EditScreenInfo path="app/modal.tsx" />

      {/* Use a light status bar on iOS to account for the black space above the modal */}
      <StatusBar style={Platform.OS === 'ios' ? 'light' : 'auto'} />
    </View>
  );
}
NotFoundScreen function · typescript · L6-L19 (14 LOC)
app/+not-found.tsx
export default function NotFoundScreen() {
  return (
    <>
      <Stack.Screen options={{ title: 'Oops!' }} />
      <View style={styles.container}>
        <Text style={styles.title}>This screen doesn't exist.</Text>

        <Link href="/" style={styles.link}>
          <Text style={styles.linkText}>Go to home screen!</Text>
        </Link>
      </View>
    </>
  );
}
AllergiesScreen function · typescript · L20-L82 (63 LOC)
app/onboarding/allergies.tsx
export default function AllergiesScreen() {
  const { goals } = useLocalSearchParams<{ goals: string }>();
  const [selected, setSelected] = useState<string[]>([]);

  const toggle = (id: string) => {
    setSelected((prev) =>
      prev.includes(id) ? prev.filter((a) => a !== id) : [...prev, id]
    );
  };

  const handleContinue = () => {
    router.push({
      pathname: '/onboarding/demo',
      params: { goals, allergies: selected.join(',') },
    });
  };

  return (
    <View style={styles.container}>
      <StatusBar style="dark" />
      <View style={styles.header}>
        <View style={styles.progress}>
          <View style={[styles.progressBar, { width: '66%' }]} />
        </View>
        <Text style={styles.step}>2 of 3</Text>
        <Text style={styles.title}>Any allergies or{'\n'}sensitivities?</Text>
        <Text style={styles.subtitle}>We'll flag these in every scan</Text>
      </View>

      <ScrollView style={styles.list} contentContainerStyle={styles.listConten
DemoScreen function · typescript · L16-L157 (142 LOC)
app/onboarding/demo.tsx
export default function DemoScreen() {
  const { goals, allergies } = useLocalSearchParams<{ goals: string; allergies: string }>();
  const [product, setProduct] = useState<ProductData | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function tryBarcodes() {
      for (const barcode of DEMO_BARCODES) {
        const p = await lookupProduct(barcode);
        if (p) {
          setProduct(p);
          setLoading(false);
          return;
        }
      }
      setLoading(false);
    }
    tryBarcodes();
  }, []);

  const handleStart = async () => {
    await savePreferences({
      goal: goals || '',
      allergies: (allergies || '').split(',').filter(Boolean),
    });
    await setOnboardingComplete();
    router.replace('/(tabs)');
  };

  const getScoreColor = (score: number) => {
    if (score >= 80) return brand.score.excellent;
    if (score >= 60) return brand.score.good;
    if (score >= 30) return brand.score.limit;
    return bra
tryBarcodes function · typescript · L22-L32 (11 LOC)
app/onboarding/demo.tsx
    async function tryBarcodes() {
      for (const barcode of DEMO_BARCODES) {
        const p = await lookupProduct(barcode);
        if (p) {
          setProduct(p);
          setLoading(false);
          return;
        }
      }
      setLoading(false);
    }
If a scraper extracted this row, it came from Repobility (https://repobility.com)
GoalsScreen function · typescript · L16-L89 (74 LOC)
app/onboarding/goals.tsx
export default function GoalsScreen() {
  const [selected, setSelected] = useState<string[]>([]);

  const toggle = (id: string) => {
    setSelected((prev) =>
      prev.includes(id) ? prev.filter((g) => g !== id) : [...prev, id]
    );
  };

  return (
    <View style={styles.container}>
      <StatusBar style="dark" />
      <View style={styles.header}>
        <View style={styles.progress}>
          <View style={[styles.progressBar, { width: '33%' }]} />
        </View>
        <Text style={styles.step}>1 of 3</Text>
        <Text style={styles.title}>What matters most{'\n'}to you?</Text>
        <Text style={styles.subtitle}>Select all that apply</Text>
      </View>

      <ScrollView style={styles.list} contentContainerStyle={styles.listContent} showsVerticalScrollIndicator={false}>
        {GOALS.map((goal) => {
          const isSelected = selected.includes(goal.id);
          return (
            <Pressable
              key={goal.id}
              testID={`goal-${goal.id}`}
WelcomeScreen function · typescript · L8-L59 (52 LOC)
app/onboarding/index.tsx
export default function WelcomeScreen() {
  return (
    <View style={styles.container}>
      <StatusBar style="dark" />
      <View style={styles.content}>
        <View style={styles.logoContainer}>
          <View style={styles.logoIcon}>
            <Text style={styles.logoEmoji}>P</Text>
          </View>
          <Text style={styles.logoText}>Peel</Text>
        </View>
        <Text style={styles.tagline}>Peel back the label.</Text>
        <Text style={styles.subtitle}>
          Scan any product to instantly see what's really inside — seed oils, additives, toxins, and more.
        </Text>

        <View style={styles.socialProof}>
          <View style={styles.proofItem}>
            <Text style={styles.proofNumber}>3M+</Text>
            <Text style={styles.proofLabel}>Products</Text>
          </View>
          <View style={styles.proofDivider} />
          <View style={styles.proofItem}>
            <Text style={styles.proofNumber}>100%</Text>
            <Text style={st
OnboardingLayout function · typescript · L3-L7 (5 LOC)
app/onboarding/_layout.tsx
export default function OnboardingLayout() {
  return (
    <Stack screenOptions={{ headerShown: false, animation: 'slide_from_right' }} />
  );
}
ProductDetailScreen function · typescript · L8-L193 (186 LOC)
app/product/[barcode].tsx
export default function ProductDetailScreen() {
  const { barcode } = useLocalSearchParams<{ barcode: string }>();
  const [product, setProduct] = useState<ProductData | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(false);

  useEffect(() => {
    if (!barcode) return;
    lookupProduct(barcode).then(async (p) => {
      if (p) {
        setProduct(p);
        await addToScanHistory({
          barcode: p.barcode,
          productName: p.productName,
          brand: p.brand,
          score: p.score,
          imageUrl: p.imageUrl,
          scannedAt: new Date().toISOString(),
        });
      } else {
        setError(true);
      }
      setLoading(false);
    });
  }, [barcode]);

  const getScoreColor = (score: number) => {
    if (score >= 80) return brand.score.excellent;
    if (score >= 60) return brand.score.good;
    if (score >= 30) return brand.score.limit;
    return brand.score.avoid;
  };

  const handleShare = asy
HistoryScreen function · typescript · L8-L93 (86 LOC)
app/(tabs)/history.tsx
export default function HistoryScreen() {
  const [history, setHistory] = useState<ScanHistoryItem[]>([]);

  useFocusEffect(
    useCallback(() => {
      getScanHistory().then(setHistory);
    }, [])
  );

  const getScoreColor = (score: number) => {
    if (score >= 80) return brand.score.excellent;
    if (score >= 60) return brand.score.good;
    if (score >= 30) return brand.score.limit;
    return brand.score.avoid;
  };

  const getScoreLabel = (score: number) => {
    if (score >= 80) return 'Excellent';
    if (score >= 60) return 'Good';
    if (score >= 30) return 'Limit';
    return 'Avoid';
  };

  const formatDate = (dateStr: string) => {
    const d = new Date(dateStr);
    const now = new Date();
    const diff = now.getTime() - d.getTime();
    const mins = Math.floor(diff / 60000);
    if (mins < 1) return 'Just now';
    if (mins < 60) return `${mins}m ago`;
    const hours = Math.floor(mins / 60);
    if (hours < 24) return `${hours}h ago`;
    const days = Math.fl
HomeScreen function · typescript · L11-L123 (113 LOC)
app/(tabs)/index.tsx
export default function HomeScreen() {
  const [history, setHistory] = useState<ScanHistoryItem[]>([]);
  const [dailyScans, setDailyScans] = useState(0);

  useFocusEffect(
    useCallback(() => {
      const load = async () => {
        const h = await getScanHistory();
        setHistory(h.slice(0, 10));
        const count = await getDailyScanCount();
        setDailyScans(count);
      };
      load();
    }, [])
  );

  const getScoreColor = (score: number) => {
    if (score >= 80) return brand.score.excellent;
    if (score >= 60) return brand.score.good;
    if (score >= 30) return brand.score.limit;
    return brand.score.avoid;
  };

  const getScoreLabel = (score: number) => {
    if (score >= 80) return 'Excellent';
    if (score >= 60) return 'Good';
    if (score >= 30) return 'Limit';
    return 'Avoid';
  };

  return (
    <ScrollView style={styles.container} contentContainerStyle={styles.content}>
      <StatusBar style="auto" />

      <View style={styles.header}>
 
getTimeOfDay function · typescript · L125-L130 (6 LOC)
app/(tabs)/index.tsx
function getTimeOfDay() {
  const hour = new Date().getHours();
  if (hour < 12) return 'morning';
  if (hour < 17) return 'afternoon';
  return 'evening';
}
TabLayout function · typescript · L9-L93 (85 LOC)
app/(tabs)/_layout.tsx
export default function TabLayout() {
  const colorScheme = useColorScheme() ?? 'light';

  return (
    <Tabs
      screenOptions={{
        tabBarActiveTintColor: brand.primary,
        tabBarInactiveTintColor: Colors[colorScheme].tabIconDefault,
        tabBarStyle: {
          backgroundColor: Colors[colorScheme].background,
          borderTopColor: Colors[colorScheme].border,
          height: 88,
          paddingBottom: 30,
          paddingTop: 8,
        },
        tabBarLabelStyle: {
          fontSize: 11,
          fontWeight: '600',
        },
        headerStyle: {
          backgroundColor: Colors[colorScheme].background,
        },
        headerTintColor: Colors[colorScheme].text,
        headerTitleStyle: {
          fontWeight: '700',
        },
      }}
    >
      <Tabs.Screen
        name="index"
        options={{
          title: 'Home',
          tabBarIcon: ({ color }) => (
            <SymbolView name={{ ios: 'house.fill', android: 'home', web: 'home' }} tin
Repobility · open methodology · https://repobility.com/research/
ProfileScreen function · typescript · L7-L141 (135 LOC)
app/(tabs)/profile.tsx
export default function ProfileScreen() {
  const [prefs, setPrefs] = useState<UserPreferences | null>(null);
  const [totalScans, setTotalScans] = useState(0);
  const [darkMode, setDarkMode] = useState(false);

  useFocusEffect(
    useCallback(() => {
      getPreferences().then(setPrefs);
      getScanHistory().then((h) => setTotalScans(h.length));
    }, [])
  );

  const goalLabels: Record<string, string> = {
    seed_oils: 'Avoid Seed Oils',
    clean_family: 'Feed Family Clean',
    reduce_processed: 'Reduce Processed Food',
    general_health: 'General Health',
    allergies: 'Manage Allergies',
    weight: 'Watch What I Eat',
  };

  const allergyLabels: Record<string, string> = {
    gluten: 'Gluten', dairy: 'Dairy', nuts: 'Tree Nuts', peanuts: 'Peanuts',
    soy: 'Soy', shellfish: 'Shellfish', eggs: 'Eggs', fish: 'Fish',
    wheat: 'Wheat', sesame: 'Sesame',
  };

  const goals = (prefs?.goal || '').split(',').filter(Boolean);
  const allergies = (prefs?.allergies || []).fi
ScanScreen function · typescript · L11-L108 (98 LOC)
app/(tabs)/scan.tsx
export default function ScanScreen() {
  const [permission, requestPermission] = useCameraPermissions();
  const [scanned, setScanned] = useState(false);
  const [flashOn, setFlashOn] = useState(false);
  const lastScannedRef = useRef<string>('');

  const handleBarCodeScanned = async (result: BarcodeScanningResult) => {
    const barcode = result.data;
    if (scanned || barcode === lastScannedRef.current) return;

    setScanned(true);
    lastScannedRef.current = barcode;

    const count = await getDailyScanCount();
    if (count >= FREE_SCAN_LIMIT) {
      Alert.alert(
        'Daily Limit Reached',
        'You\'ve used all 10 free scans today. Upgrade to Peel Pro for unlimited scanning.',
        [
          { text: 'Maybe Later', onPress: () => setScanned(false) },
          { text: 'Upgrade', onPress: () => { setScanned(false); /* TODO: paywall */ } },
        ]
      );
      return;
    }

    await incrementScanCount();
    router.push(`/product/${barcode}`);

    // Allow 
SearchScreen function · typescript · L15-L105 (91 LOC)
app/(tabs)/search.tsx
export default function SearchScreen() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState<SearchResult[]>([]);
  const [loading, setLoading] = useState(false);
  const [searched, setSearched] = useState(false);

  const handleSearch = useCallback(async () => {
    if (!query.trim()) return;
    setLoading(true);
    setSearched(true);
    const res = await searchProducts(query.trim());
    setResults(res);
    setLoading(false);
  }, [query]);

  return (
    <View style={styles.container}>
      <View style={styles.header}>
        <Text style={styles.title}>Search</Text>
        <View style={styles.searchBar}>
          <Text style={styles.searchIcon}>Search</Text>
          <TextInput
            testID="search-input"
            style={styles.searchInput}
            placeholder="Search by product or brand..."
            placeholderTextColor="#9CA3AF"
            value={query}
            onChangeText={setQuery}
            onSubmitEditing={handle
TabTwoScreen function · typescript · L6-L14 (9 LOC)
app/(tabs)/two.tsx
export default function TabTwoScreen() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Tab Two</Text>
      <View style={styles.separator} lightColor="#eee" darkColor="rgba(255,255,255,0.1)" />
      <EditScreenInfo path="app/(tabs)/two.tsx" />
    </View>
  );
}
EditScreenInfo function · typescript · L10-L47 (38 LOC)
components/EditScreenInfo.tsx
export default function EditScreenInfo({ path }: { path: string }) {
  return (
    <View>
      <View style={styles.getStartedContainer}>
        <Text
          style={styles.getStartedText}
          lightColor="rgba(0,0,0,0.8)"
          darkColor="rgba(255,255,255,0.8)">
          Open up the code for this screen:
        </Text>

        <View
          style={[styles.codeHighlightContainer, styles.homeScreenFilename]}
          darkColor="rgba(255,255,255,0.05)"
          lightColor="rgba(0,0,0,0.05)">
          <MonoText>{path}</MonoText>
        </View>

        <Text
          style={styles.getStartedText}
          lightColor="rgba(0,0,0,0.8)"
          darkColor="rgba(255,255,255,0.8)">
          Change any of the text, save the file, and your app will automatically update.
        </Text>
      </View>

      <View style={styles.helpContainer}>
        <ExternalLink
          style={styles.helpLink}
          href="https://docs.expo.io/get-started/create-a-new-app/#opening
ExternalLink function · typescript · L6-L24 (19 LOC)
components/ExternalLink.tsx
export function ExternalLink(
  props: Omit<React.ComponentProps<typeof Link>, 'href'> & { href: string }
) {
  return (
    <Link
      target="_blank"
      {...props}
      href={props.href}
      onPress={(e) => {
        if (Platform.OS !== 'web') {
          // Prevent the default behavior of linking to the default browser on native.
          e.preventDefault();
          // Open the link in an in-app browser.
          WebBrowser.openBrowserAsync(props.href as string);
        }
      }}
    />
  );
}
MonoText function · typescript · L3-L5 (3 LOC)
components/StyledText.tsx
export function MonoText(props: TextProps) {
  return <Text {...props} style={[props.style, { fontFamily: 'SpaceMono' }]} />;
}
useThemeColor function · typescript · L19-L31 (13 LOC)
components/Themed.tsx
export function useThemeColor(
  props: { light?: string; dark?: string },
  colorName: keyof typeof Colors.light & keyof typeof Colors.dark
) {
  const theme = useColorScheme();
  const colorFromProps = props[theme];

  if (colorFromProps) {
    return colorFromProps;
  } else {
    return Colors[theme][colorName];
  }
}
Citation: Repobility (2026). State of AI-Generated Code. https://repobility.com/research/
Text function · typescript · L33-L38 (6 LOC)
components/Themed.tsx
export function Text(props: TextProps) {
  const { style, lightColor, darkColor, ...otherProps } = props;
  const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text');

  return <DefaultText style={[{ color }, style]} {...otherProps} />;
}
View function · typescript · L40-L45 (6 LOC)
components/Themed.tsx
export function View(props: ViewProps) {
  const { style, lightColor, darkColor, ...otherProps } = props;
  const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');

  return <DefaultView style={[{ backgroundColor }, style]} {...otherProps} />;
}
useClientOnlyValue function · typescript · L2-L4 (3 LOC)
components/useClientOnlyValue.ts
export function useClientOnlyValue<S, C>(server: S, client: C): S | C {
  return client;
}
useClientOnlyValue function · typescript · L5-L12 (8 LOC)
components/useClientOnlyValue.web.ts
export function useClientOnlyValue<S, C>(server: S, client: C): S | C {
  const [value, setValue] = React.useState<S | C>(server);
  React.useEffect(() => {
    setValue(client);
  }, [client]);

  return value;
}
useColorScheme function · typescript · L6-L8 (3 LOC)
components/useColorScheme.web.ts
export function useColorScheme() {
  return 'light';
}
lookupProduct function · typescript · L45-L180 (136 LOC)
lib/openfoodfacts.ts
export async function lookupProduct(barcode: string): Promise<ProductData | null> {
  try {
    const res = await fetch(
      `https://world.openfoodfacts.org/api/v2/product/${barcode}.json`
    );
    if (!res.ok) return null;
    const data = await res.json();
    if (data.status !== 1 || !data.product) return null;

    const p = data.product;
    const ingredientsText = (p.ingredients_text || '').toLowerCase();
    const ingredientsList: string[] = (p.ingredients || []).map(
      (i: any) => i.text || i.id || ''
    );

    // Detect seed oils
    const seedOilsFound = SEED_OILS.filter((oil) =>
      ingredientsText.includes(oil)
    );
    const hasSeedOils = seedOilsFound.length > 0;

    // Detect bad additives
    const additivesFound = BAD_ADDITIVES.filter((add) =>
      ingredientsText.includes(add)
    );
    const hasAdditives = additivesFound.length > 0;

    // Get additives from OFF data
    const offAdditives: string[] = (p.additives_tags || []).map((t: string) =>
   
generateAnalysis function · typescript · L182-L237 (56 LOC)
lib/openfoodfacts.ts
function generateAnalysis(
  name: string,
  score: number,
  label: string,
  hasSeedOils: boolean,
  seedOils: string[],
  hasAdditives: boolean,
  additives: string[],
  processing: string,
  ingredients: string
): string {
  const parts: string[] = [];

  if (score >= 80) {
    parts.push(
      `${name} is an excellent choice.`
    );
  } else if (score >= 60) {
    parts.push(
      `${name} is a solid choice with some minor concerns.`
    );
  } else if (score >= 30) {
    parts.push(
      `${name} has some ingredients worth being mindful of.`
    );
  } else {
    parts.push(
      `${name} has several ingredients that may not align with clean eating goals.`
    );
  }

  if (hasSeedOils) {
    parts.push(
      `It contains ${seedOils.join(' and ')}, which are processed seed oils linked to inflammation.`
    );
  }

  if (processing === 'High') {
    parts.push(
      'This is an ultra-processed product with a high level of industrial processing.'
    );
  }

  if (hasAdditiv
searchProducts function · typescript · L239-L248 (10 LOC)
lib/openfoodfacts.ts
export async function searchProducts(
  query: string
): Promise<
  Array<{
    barcode: string;
    productName: string;
    brand: string;
    imageUrl?: string;
    nutriscoreGrade?: string;
  }>
Want this analysis on your repo? https://repobility.com/scan/
getItem function · typescript · L24-L27 (4 LOC)
lib/storage.ts
async function getItem(key: string): Promise<string | null> {
  if (Platform.OS === 'web') return localStorage.getItem(key);
  return SecureStore.getItemAsync(key);
}
setItem function · typescript · L29-L35 (7 LOC)
lib/storage.ts
async function setItem(key: string, value: string): Promise<void> {
  if (Platform.OS === 'web') {
    localStorage.setItem(key, value);
    return;
  }
  return SecureStore.setItemAsync(key, value);
}
isOnboardingComplete function · typescript · L37-L40 (4 LOC)
lib/storage.ts
export async function isOnboardingComplete(): Promise<boolean> {
  const val = await getItem(ONBOARDING_KEY);
  return val === 'true';
}
setOnboardingComplete function · typescript · L42-L44 (3 LOC)
lib/storage.ts
export async function setOnboardingComplete(): Promise<void> {
  await setItem(ONBOARDING_KEY, 'true');
}
savePreferences function · typescript · L46-L48 (3 LOC)
lib/storage.ts
export async function savePreferences(prefs: UserPreferences): Promise<void> {
  await setItem(PREFERENCES_KEY, JSON.stringify(prefs));
}
getPreferences function · typescript · L50-L54 (5 LOC)
lib/storage.ts
export async function getPreferences(): Promise<UserPreferences | null> {
  const val = await getItem(PREFERENCES_KEY);
  if (!val) return null;
  return JSON.parse(val);
}
getScanHistory function · typescript · L56-L60 (5 LOC)
lib/storage.ts
export async function getScanHistory(): Promise<ScanHistoryItem[]> {
  const val = await getItem(SCAN_HISTORY_KEY);
  if (!val) return [];
  return JSON.parse(val);
}
addToScanHistory function · typescript · L62-L68 (7 LOC)
lib/storage.ts
export async function addToScanHistory(item: ScanHistoryItem): Promise<void> {
  const history = await getScanHistory();
  history.unshift(item);
  // Keep last 500 scans
  if (history.length > 500) history.length = 500;
  await setItem(SCAN_HISTORY_KEY, JSON.stringify(history));
}
If a scraper extracted this row, it came from Repobility (https://repobility.com)
getDailyScanCount function · typescript · L70-L80 (11 LOC)
lib/storage.ts
export async function getDailyScanCount(): Promise<number> {
  const today = new Date().toISOString().split('T')[0];
  const savedDate = await getItem(SCAN_DATE_KEY);
  if (savedDate !== today) {
    await setItem(SCAN_DATE_KEY, today);
    await setItem(SCAN_COUNT_KEY, '0');
    return 0;
  }
  const count = await getItem(SCAN_COUNT_KEY);
  return count ? parseInt(count, 10) : 0;
}
incrementScanCount function · typescript · L82-L87 (6 LOC)
lib/storage.ts
export async function incrementScanCount(): Promise<number> {
  const count = await getDailyScanCount();
  const newCount = count + 1;
  await setItem(SCAN_COUNT_KEY, String(newCount));
  return newCount;
}