All files / src/components/home HomeHeader.tsx

100% Statements 12/12
100% Branches 9/9
100% Functions 3/3
100% Lines 11/11

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 1151x 1x 1x 1x 1x                     1x                 9x 9x   9x                                                                                   8x                                                                                       1x  
import React, { forwardRef, useMemo } from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import { Feather, Ionicons } from '@expo/vector-icons';
import SemLogo from '@/components/common/SemLogo';
import { useTheme } from '@/theme';
import type { Theme } from '@/theme';
 
interface HomeHeaderProps {
  /** Whether dark mode is currently active — determines the sun/moon icon. */
  isDark: boolean;
  onPressThemeToggle: () => void;
  onPressNotifications?: () => void;
  unreadNotificationCount?: number;
}
 
const HomeHeader = forwardRef<any, HomeHeaderProps>(function HomeHeader(
  {
    isDark,
    onPressThemeToggle,
    onPressNotifications,
    unreadNotificationCount = 0,
  },
  _ref,
) {
  const { theme } = useTheme();
  const styles = useMemo(() => makeStyles(theme), [theme]);
 
  return (
    <View style={styles.container}>
      <View style={styles.logoWrap}>
        <SemLogo height={52} color={theme.text} />
      </View>
 
      <View style={styles.rightWrap}>
        {/* Bell */}
        <TouchableOpacity
          style={styles.iconButton}
          activeOpacity={0.75}
          onPress={onPressNotifications}
          accessibilityRole="button"
          accessibilityLabel="Open notifications"
        >
          <Ionicons name="notifications-outline" size={22} color={theme.text} />
          {unreadNotificationCount > 0 ? (
            <View style={styles.badge}>
              <Text style={styles.badgeText}>
                {unreadNotificationCount > 99 ? '99+' : String(unreadNotificationCount)}
              </Text>
            </View>
          ) : null}
        </TouchableOpacity>
 
        {/* Theme toggle */}
        <TouchableOpacity
          style={styles.iconButton}
          activeOpacity={0.75}
          onPress={onPressThemeToggle}
          accessibilityRole="button"
          accessibilityLabel={isDark ? 'Switch to light mode' : 'Switch to dark mode'}
          testID="theme-toggle"
        >
          <Feather name={isDark ? 'sun' : 'moon'} size={20} color={theme.text} />
        </TouchableOpacity>
      </View>
    </View>
  );
});
 
function makeStyles(t: Theme) {
  return StyleSheet.create({
    container: {
      marginTop: 12,
      marginBottom: 10,
      flexDirection: 'row',
      justifyContent: 'space-between',
      alignItems: 'center',
    },
    logoWrap: {
      flexShrink: 0,
    },
    rightWrap: {
      flexDirection: 'row',
      alignItems: 'center',
      gap: 4,
    },
    iconButton: {
      width: 40,
      height: 40,
      borderRadius: 20,
      alignItems: 'center',
      justifyContent: 'center',
    },
    badge: {
      position: 'absolute',
      top: 4,
      right: 4,
      minWidth: 16,
      height: 16,
      borderRadius: 8,
      backgroundColor: t.notificationBadge,
      alignItems: 'center',
      justifyContent: 'center',
      paddingHorizontal: 3,
    },
    badgeText: {
      color: '#FFFFFF',
      fontSize: 9,
      fontWeight: '700',
      lineHeight: 12,
    },
  });
}
 
export default HomeHeader;