← back to ajthal__noomi-bodi

Function bodies 299 total

All specs Real LLM only Function bodies
MainActivity class · kotlin · L8-L22 (15 LOC)
android/app/src/main/java/com/noomibodi/MainActivity.kt
class MainActivity : ReactActivity() {

  /**
   * Returns the name of the main component registered from JavaScript. This is used to schedule
   * rendering of the component.
   */
  override fun getMainComponentName(): String = "NoomiBodi"

  /**
   * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
   * which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
   */
  override fun createReactActivityDelegate(): ReactActivityDelegate =
      DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)
}
MainApplication class · kotlin · L10-L27 (18 LOC)
android/app/src/main/java/com/noomibodi/MainApplication.kt
class MainApplication : Application(), ReactApplication {

  override val reactHost: ReactHost by lazy {
    getDefaultReactHost(
      context = applicationContext,
      packageList =
        PackageList(this).packages.apply {
          // Packages that cannot be autolinked yet can be added manually here, for example:
          // add(MyReactNativePackage())
        },
    )
  }

  override fun onCreate() {
    super.onCreate()
    loadReactNative(this)
  }
}
MainTabs function · typescript · L73-L173 (101 LOC)
App.tsx
function MainTabs({ showAdmin }: { showAdmin: boolean }) {
  const { colors } = useTheme();
  const [currentIndex, setCurrentIndex] = useState(0);
  const [unreadCount, setUnreadCount] = useState(0);
  const tabNavRef = React.useRef<any>(null);

  const groups = useMemo(
    () => (showAdmin ? [...STANDARD_GROUPS, ADMIN_GROUP] : STANDARD_GROUPS),
    [showAdmin],
  );

  const allScreens = useMemo(() => groups.flatMap(g => g.screens), [groups]);

  const activeGroup = useMemo(() => {
    const screenName = allScreens[currentIndex] ?? allScreens[0];
    return groups.find(g => g.screens.includes(screenName))?.group ?? 'Home';
  }, [currentIndex, allScreens, groups]);

  const subTabConfig = useMemo(() => {
    const g = groups.find(gr => gr.group === activeGroup);
    if (!g || g.screens.length <= 1) return null;
    const groupStartIndex = allScreens.indexOf(g.screens[0]);
    const activeSubIndex = currentIndex - groupStartIndex;
    if (activeGroup === 'Meals') {
      const icons = 
AppInner function · typescript · L177-L384 (208 LOC)
App.tsx
function AppInner() {
  const { isDark, colors } = useTheme();
  const { user, isLoading: isAuthLoading, signOut } = useAuth();
  const { isImpersonating, isSwitching } = useImpersonation();
  const [initialCheckDone, setInitialCheckDone] = useState(false);
  const [screen, setScreen] = useState<AppScreen>('loading');
  const [signInFromOnboarding, setSignInFromOnboarding] = useState(false);
  const [userRole, setUserRole] = useState<UserRole | null>(null);
  const { isOnline, pendingCount } = useOfflineSync();
  const navigationRef = React.useRef<any>(null);
  useNotifications(screen === 'main' && !!user, navigationRef);

  useEffect(() => {
    // @ts-ignore - loadFont is available at runtime
    Ionicons.loadFont?.();
  }, []);

  // Reset the initial check when impersonation switching finishes so the
  // profile/role is re-evaluated for the newly signed-in user.
  const wasSwitchingRef = React.useRef(false);
  useEffect(() => {
    if (isSwitching) {
      wasSwitchingRef.current 
App function · typescript · L386-L402 (17 LOC)
App.tsx
function App() {
  return (
    <SafeAreaProvider>
      <ErrorBoundary>
        <ThemeProvider>
          <AuthProvider>
            <ImpersonationProvider>
              <ChatProvider>
                <AppInner />
              </ChatProvider>
            </ImpersonationProvider>
          </AuthProvider>
        </ThemeProvider>
      </ErrorBoundary>
    </SafeAreaProvider>
  );
}
AppDelegate class · swift · L9-L56 (48 LOC)
ios/NoomiBodi/AppDelegate.swift
class AppDelegate: UIResponder, UIApplicationDelegate {
  var window: UIWindow?

  var reactNativeDelegate: ReactNativeDelegate?
  var reactNativeFactory: RCTReactNativeFactory?

  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
  ) -> Bool {
    FirebaseApp.configure()
    UNUserNotificationCenter.current().delegate = self

    let delegate = ReactNativeDelegate()
    let factory = RCTReactNativeFactory(delegate: delegate)
    delegate.dependencyProvider = RCTAppDependencyProvider()

    reactNativeDelegate = delegate
    reactNativeFactory = factory

    window = UIWindow(frame: UIScreen.main.bounds)

    factory.startReactNative(
      withModuleName: "NoomiBodi",
      in: window,
      launchOptions: launchOptions
    )

    return true
  }

  func application(
    _ app: UIApplication,
    open url: URL,
    options: [UIApplication.OpenURLOptionsKey: Any] = [:]
  ) -> Bool {
    Noti
ReactNativeDelegate class · swift · L68-L80 (13 LOC)
ios/NoomiBodi/AppDelegate.swift
class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate {
  override func sourceURL(for bridge: RCTBridge) -> URL? {
    self.bundleURL()
  }

  override func bundleURL() -> URL? {
#if DEBUG
    RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
#else
    Bundle.main.url(forResource: "main", withExtension: "jsbundle")
#endif
  }
}
Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
ActivityFeedCard function · typescript · L19-L48 (30 LOC)
src/components/ActivityFeedCard.tsx
export default function ActivityFeedCard({ activity, onPress }: ActivityFeedCardProps) {
  const { colors } = useTheme();

  const streakDays = activity.activityData?.streak_days ?? 0;
  const emoji = getStreakEmoji(streakDays);
  const text = getStreakText(streakDays);
  const timeAgo = getTimeAgo(activity.createdAt);

  return (
    <TouchableOpacity
      style={[s.container, { backgroundColor: colors.surface, borderColor: colors.border }]}
      onPress={onPress}
      activeOpacity={0.7}
    >
      {activity.profilePictureUrl ? (
        <Image source={{ uri: activity.profilePictureUrl }} style={s.avatar} />
      ) : (
        <View style={[s.avatarPlaceholder, { backgroundColor: colors.inputBg }]}>
          <Ionicons name="person" size={18} color={colors.textTertiary} />
        </View>
      )}
      <View style={s.content}>
        <Text style={[s.text, { color: colors.text }]}>
          <Text style={s.bold}>@{activity.username || 'user'}</Text> {text} {emoji}
        </Tex
getTimeAgo function · typescript · L50-L60 (11 LOC)
src/components/ActivityFeedCard.tsx
function getTimeAgo(dateStr: string): string {
  const diff = Date.now() - new Date(dateStr).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.floor(hours / 24);
  if (days < 7) return `${days}d ago`;
  return `${Math.floor(days / 7)}w ago`;
}
AIMealBuilderModal function · typescript · L55-L254 (200 LOC)
src/components/AIMealBuilderModal.tsx
export default function AIMealBuilderModal({ visible, onGenerated, onCancel }: Props) {
  const { colors } = useTheme();
  const [description, setDescription] = useState('');
  const [selectedTags, setSelectedTags] = useState<Set<string>>(new Set());
  const [loading, setLoading] = useState(false);
  const [result, setResult] = useState<{ mealData: MealData; recipe: string } | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [pendingImage, setPendingImage] = useState<PendingImage | null>(null);

  const toggleTag = (value: string) => {
    setSelectedTags(prev => {
      const next = new Set(prev);
      if (next.has(value)) next.delete(value);
      else next.add(value);
      return next;
    });
  };

  const buildQuery = (): string => {
    const parts: string[] = [];
    if (selectedTags.size > 0) parts.push(Array.from(selectedTags).join(', '));
    if (description.trim()) parts.push(description.trim());
    if (parts.length === 0) return 'a healthy 
MacroPill function · typescript · L256-L265 (10 LOC)
src/components/AIMealBuilderModal.tsx
function MacroPill({ label, value, unit, color, labelColor }: {
  label: string; value: number; unit?: string; color: string; labelColor: string;
}) {
  return (
    <View style={[s.pill, { borderColor: color + '40' }]}>
      <Text style={[s.pillValue, { color }]}>{value}{unit || ''}</Text>
      <Text style={[s.pillLabel, { color: labelColor }]}>{label}</Text>
    </View>
  );
}
BottomSheet function · typescript · L23-L136 (114 LOC)
src/components/BottomSheet.tsx
export default function BottomSheet({ visible, onClose, children }: Props) {
  const { colors } = useTheme();
  const { height: screenHeight } = useWindowDimensions();
  const translateY = useRef(new Animated.Value(screenHeight)).current;
  const backdropOpacity = useRef(new Animated.Value(0)).current;
  const onCloseRef = useRef(onClose);
  onCloseRef.current = onClose;

  useEffect(() => {
    if (visible) {
      translateY.setValue(screenHeight);
      backdropOpacity.setValue(0);
      requestAnimationFrame(() => {
        Animated.parallel([
          Animated.spring(translateY, {
            toValue: 0,
            useNativeDriver: true,
            friction: 9,
            tension: 65,
          }),
          Animated.timing(backdropOpacity, {
            toValue: 1,
            duration: 300,
            useNativeDriver: true,
          }),
        ]).start();
      });
    }
  }, [visible, screenHeight, translateY, backdropOpacity]);

  const dismiss = useCallback(() => {
   
ChatInputBox function · typescript · L46-L150 (105 LOC)
src/components/ChatInputBox.tsx
export default function ChatInputBox({
  value,
  onChangeText,
  onSend,
  placeholder = 'Type a message...',
  disabled = false,
  sendIcon = 'arrow-up-circle',
  showImagePicker = true,
}: Props) {
  const { colors } = useTheme();
  const [pendingImage, setPendingImage] = useState<PendingImage | null>(null);

  const canSend = !disabled && (!!value.trim() || !!pendingImage);

  const handleImageSelected = useCallback((result: ImagePickerResponse) => {
    if (result.didCancel || result.errorCode || !result.assets?.length) return;
    const asset = result.assets[0];
    if (!asset.base64 || !asset.uri) return;
    setPendingImage({
      uri: asset.uri,
      base64: asset.base64,
      mimeType: asset.type || 'image/jpeg',
    });
  }, []);

  const handleImagePick = useCallback(() => {
    if (Platform.OS === 'ios') {
      ActionSheetIOS.showActionSheetWithOptions(
        { options: ['Cancel', 'Take Photo', 'Choose from Library'], cancelButtonIndex: 0 },
        idx => {
        
computeNiceScale function · typescript · L26-L37 (12 LOC)
src/components/CustomBarChart.tsx
function computeNiceScale(rawMax: number, tickCount: number) {
  if (rawMax <= 0) return { niceMax: tickCount, step: 1 };
  const rawStep = rawMax / tickCount;
  const magnitude = Math.pow(10, Math.floor(Math.log10(rawStep)));
  const residual = rawStep / magnitude;
  let niceStep: number;
  if (residual <= 1) niceStep = magnitude;
  else if (residual <= 2) niceStep = 2 * magnitude;
  else if (residual <= 5) niceStep = 5 * magnitude;
  else niceStep = 10 * magnitude;
  return { niceMax: niceStep * tickCount, step: niceStep };
}
CustomBarChart function · typescript · L48-L233 (186 LOC)
src/components/CustomBarChart.tsx
export default function CustomBarChart({
  labels,
  data,
  width,
  height,
  barColor = '#7C3AED',
  labelColor = '#888',
  gridColor = '#333',
  goalValue,
  goalColor = '#FF9800',
  goalLabel,
  barPercentage = 0.6,
  yAxisWidth = 48,
  formatYLabel = defaultFormatLabel,
  formatTooltip = (v) => Math.round(v).toLocaleString(),
}: CustomBarChartProps) {
  const [selectedIndex, setSelectedIndex] = useState<number | null>(null);

  const plotHeight = height - X_LABEL_HEIGHT - TOP_PAD;
  const plotWidth = width - yAxisWidth;
  const visibleSlots = Math.max(data.length, MIN_SLOTS);
  const slotWidth = visibleSlots > 0 ? plotWidth / visibleSlots : 0;
  const barW = Math.min(MAX_BAR_WIDTH, Math.max(6, slotWidth * barPercentage));

  const maxVisibleLabels = Math.max(2, Math.floor(plotWidth / LABEL_MIN_WIDTH));
  const labelSkip = Math.max(1, Math.ceil(data.length / maxVisibleLabels));
  const shouldShowLabel = (i: number) => {
    if (data.length <= maxVisibleLabels) return true;
    if 
Source: Repobility analyzer · https://repobility.com
CustomBottomTabBar function · typescript · L34-L75 (42 LOC)
src/components/CustomBottomTabBar.tsx
export default function CustomBottomTabBar({
  activeGroup,
  onTabPress,
  showAdmin,
}: CustomBottomTabBarProps): React.JSX.Element {
  const { colors } = useTheme();
  const insets = useSafeAreaInsets();
  const tabs = showAdmin ? [...STANDARD_TABS, ADMIN_TAB] : STANDARD_TABS;

  return (
    <View
      style={[
        s.bar,
        {
          backgroundColor: colors.tabBarBg,
          borderTopColor: colors.tabBarBorder,
          paddingBottom: insets.bottom,
        },
      ]}
    >
      {tabs.map(tab => {
        const focused = tab.key === activeGroup;
        const color = focused ? colors.tabBarActive : colors.tabBarInactive;
        return (
          <TouchableOpacity
            key={tab.key}
            style={s.tab}
            onPress={() => onTabPress(tab.key)}
            activeOpacity={0.7}
          >
            <Ionicons
              name={focused ? tab.iconFocused : tab.icon}
              size={24}
              color={color}
            />
            <
ProgressBar function · typescript · L13-L41 (29 LOC)
src/components/DailyTotals.tsx
function ProgressBar({
  current,
  goal,
  color,
  barTrackBg,
}: {
  current: number;
  goal: number;
  color: string;
  barTrackBg?: string;
}) {
  const pct = goal > 0 ? Math.min(current / goal, 1.5) : 0;
  const displayPct = Math.min(pct, 1);
  const over = pct > 1;

  return (
    <View style={[s.barTrack, barTrackBg ? { backgroundColor: barTrackBg } : undefined]}>
      <View
        style={[
          s.barFill,
          {
            width: `${displayPct * 100}%`,
            backgroundColor: over ? '#ff6b6b' : color,
          },
        ]}
      />
    </View>
  );
}
MacroRow function · typescript · L43-L77 (35 LOC)
src/components/DailyTotals.tsx
function MacroRow({
  label,
  current,
  goal,
  unit,
  color,
  compact,
  labelColor,
  valueColor,
  barTrackBg,
}: {
  label: string;
  current: number;
  goal: number;
  unit: string;
  color: string;
  compact?: boolean;
  labelColor?: string;
  valueColor?: string;
  barTrackBg?: string;
}) {
  return (
    <View style={compact ? s.macroRowCompact : s.macroRow}>
      <View style={s.macroLabel}>
        <View style={[s.dot, { backgroundColor: color }]} />
        <Text style={[compact ? s.labelTextCompact : s.labelText, labelColor ? { color: labelColor } : undefined]}>{label}</Text>
      </View>
      <ProgressBar current={current} goal={goal} color={color} barTrackBg={barTrackBg} />
      <Text style={[compact ? s.valueTextCompact : s.valueText, valueColor ? { color: valueColor } : undefined]}>
        {current}/{goal}
        {unit}
      </Text>
    </View>
  );
}
DailyTotals function · typescript · L79-L160 (82 LOC)
src/components/DailyTotals.tsx
export default function DailyTotals({ totals, goals, compact }: Props) {
  const { colors } = useTheme();
  const calPct =
    goals.calories > 0
      ? Math.round((totals.calories / goals.calories) * 100)
      : 0;

  if (compact) {
    return (
      <View style={[s.compactContainer, { backgroundColor: colors.surface, borderBottomColor: colors.border }]}>
        <View style={s.compactCalRow}>
          <Text style={[s.compactCalLabel, { color: colors.text }]}>
            {totals.calories}/{goals.calories} cal
          </Text>
          <Text style={s.compactCalPct}>{calPct}%</Text>
        </View>
        <ProgressBar
          current={totals.calories}
          goal={goals.calories}
          color="#7C3AED"
          barTrackBg={colors.border}
        />
        <View style={s.compactMacros}>
          <Text style={[s.compactMacro, { color: colors.textSecondary }]}>
            P {totals.protein}/{goals.protein}g
          </Text>
          <Text style={[s.compactMacro, { col
EditMealModal function · typescript · L55-L106 (52 LOC)
src/components/EditMealModal.tsx
export default function EditMealModal({ visible, initialData, onSave, onCancel }: Props) {
  const { colors } = useTheme();
  const [name, setName] = useState('');
  const [calories, setCalories] = useState('');
  const [protein, setProtein] = useState('');
  const [carbs, setCarbs] = useState('');
  const [fat, setFat] = useState('');

  useEffect(() => {
    if (visible) {
      setName(initialData.name);
      setCalories(String(initialData.calories));
      setProtein(String(initialData.protein));
      setCarbs(String(initialData.carbs));
      setFat(String(initialData.fat));
    }
  }, [visible, initialData]);

  const handleSave = () => {
    onSave({
      name: name.trim() || initialData.name,
      calories: parseInt(calories, 10) || 0,
      protein: parseInt(protein, 10) || 0,
      carbs: parseInt(carbs, 10) || 0,
      fat: parseInt(fat, 10) || 0,
    });
  };

  return (
    <BottomSheet visible={visible} onClose={onCancel}>
      <Text style={[s.title, { color: colors.
EmptyState function · typescript · L16-L46 (31 LOC)
src/components/EmptyState.tsx
export function EmptyState({
  icon,
  title,
  subtitle,
  actionLabel,
  onAction,
  compact,
}: EmptyStateProps) {
  const { colors } = useTheme();

  return (
    <View style={[styles.container, !compact && styles.fullScreen]}>
      <Icon name={icon} size={56} color={colors.textTertiary} />
      <Text style={[styles.title, { color: colors.text }]}>{title}</Text>
      {subtitle ? (
        <Text style={[styles.subtitle, { color: colors.textSecondary }]}>
          {subtitle}
        </Text>
      ) : null}
      {actionLabel && onAction ? (
        <TouchableOpacity
          style={[styles.actionButton, { backgroundColor: colors.accent }]}
          onPress={onAction}
          activeOpacity={0.7}
        >
          <Text style={styles.actionText}>{actionLabel}</Text>
        </TouchableOpacity>
      ) : null}
    </View>
  );
}
ErrorBoundary class · typescript · L13-L45 (33 LOC)
src/components/ErrorBoundary.tsx
export class ErrorBoundary extends Component<Props, State> {
  state: State = { hasError: false };

  static getDerivedStateFromError(): State {
    return { hasError: true };
  }

  componentDidCatch(error: Error, info: ErrorInfo) {
    console.error('ErrorBoundary caught:', error, info.componentStack);
  }

  handleReset = () => {
    this.setState({ hasError: false });
  };

  render() {
    if (this.state.hasError) {
      return (
        <View style={styles.container}>
          <Icon name="bug-outline" size={56} color="#999" />
          <Text style={styles.title}>Something went wrong</Text>
          <Text style={styles.subtitle}>
            The app ran into an unexpected error. Try restarting.
          </Text>
          <TouchableOpacity style={styles.button} onPress={this.handleReset} activeOpacity={0.7}>
            <Text style={styles.buttonText}>Try Again</Text>
          </TouchableOpacity>
        </View>
      );
    }
    return this.props.children;
  }
}
getDerivedStateFromError method · typescript · L16-L18 (3 LOC)
src/components/ErrorBoundary.tsx
  static getDerivedStateFromError(): State {
    return { hasError: true };
  }
Citation: Repobility (2026). State of AI-Generated Code. https://repobility.com/research/
componentDidCatch method · typescript · L20-L22 (3 LOC)
src/components/ErrorBoundary.tsx
  componentDidCatch(error: Error, info: ErrorInfo) {
    console.error('ErrorBoundary caught:', error, info.componentStack);
  }
render method · typescript · L28-L44 (17 LOC)
src/components/ErrorBoundary.tsx
  render() {
    if (this.state.hasError) {
      return (
        <View style={styles.container}>
          <Icon name="bug-outline" size={56} color="#999" />
          <Text style={styles.title}>Something went wrong</Text>
          <Text style={styles.subtitle}>
            The app ran into an unexpected error. Try restarting.
          </Text>
          <TouchableOpacity style={styles.button} onPress={this.handleReset} activeOpacity={0.7}>
            <Text style={styles.buttonText}>Try Again</Text>
          </TouchableOpacity>
        </View>
      );
    }
    return this.props.children;
  }
ErrorState function · typescript · L13-L36 (24 LOC)
src/components/ErrorState.tsx
export function ErrorState({
  message = 'Something went wrong.',
  onRetry,
  compact,
}: ErrorStateProps) {
  const { colors } = useTheme();

  return (
    <View style={[styles.container, !compact && styles.fullScreen, { backgroundColor: compact ? undefined : colors.background }]}>
      <Icon name="cloud-offline-outline" size={48} color={colors.textTertiary} />
      <Text style={[styles.message, { color: colors.textSecondary }]}>{message}</Text>
      {onRetry ? (
        <TouchableOpacity
          style={[styles.retryButton, { backgroundColor: colors.accent }]}
          onPress={onRetry}
          activeOpacity={0.7}
        >
          <Icon name="refresh" size={18} color="#fff" style={styles.retryIcon} />
          <Text style={styles.retryText}>Retry</Text>
        </TouchableOpacity>
      ) : null}
    </View>
  );
}
FriendCard function · typescript · L17-L46 (30 LOC)
src/components/FriendCard.tsx
export default function FriendCard({ friend, onPress }: FriendCardProps) {
  const { colors } = useTheme();

  return (
    <TouchableOpacity
      style={[s.container, { backgroundColor: colors.surface, borderColor: colors.border }]}
      onPress={onPress}
      activeOpacity={0.7}
    >
      {friend.profilePictureUrl ? (
        <Image source={{ uri: friend.profilePictureUrl }} style={s.avatar} />
      ) : (
        <View style={[s.avatarPlaceholder, { backgroundColor: colors.inputBg }]}>
          <Ionicons name="person" size={28} color={colors.textTertiary} />
        </View>
      )}
      <Text style={[s.username, { color: colors.text }]} numberOfLines={1}>
        @{friend.username || 'user'}
      </Text>
      {friend.displayName ? (
        <Text style={[s.displayName, { color: colors.textSecondary }]} numberOfLines={1}>
          {friend.displayName}
        </Text>
      ) : null}
      {(friend.currentStreak ?? 0) > 0 && (
        <Text style={s.streak}>{'\uD83D\uDD25'}
FriendPickerModal function · typescript · L24-L128 (105 LOC)
src/components/FriendPickerModal.tsx
export default function FriendPickerModal({
  visible,
  onClose,
  onSend,
}: FriendPickerModalProps) {
  const { colors } = useTheme();
  const [friends, setFriends] = useState<FriendWithProfile[]>([]);
  const [loading, setLoading] = useState(true);
  const [selected, setSelected] = useState<Set<string>>(new Set());
  const [message, setMessage] = useState('');

  useEffect(() => {
    if (!visible) return;
    setSelected(new Set());
    setMessage('');
    setLoading(true);
    getAcceptedFriends()
      .then(setFriends)
      .finally(() => setLoading(false));
  }, [visible]);

  const toggleFriend = (id: string) => {
    setSelected(prev => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id);
      else next.add(id);
      return next;
    });
  };

  const handleSend = () => {
    onSend(Array.from(selected), message.trim());
  };

  return (
    <BottomSheet visible={visible} onClose={onClose}>
      <View style={s.content}>
        <Text style={[s.tit
ImpersonateModal function · typescript · L34-L269 (236 LOC)
src/components/ImpersonateModal.tsx
export default function ImpersonateModal({ visible, onClose }: Props) {
  const { colors, isDark } = useTheme();
  const { switchToUser } = useImpersonation();

  const [query, setQuery] = useState('');
  const [results, setResults] = useState<AdminProfile[]>([]);
  const [searching, setSearching] = useState(false);
  const [searchError, setSearchError] = useState<string | null>(null);
  const [selected, setSelected] = useState<AdminProfile | null>(null);
  const [password, setPassword] = useState('');
  const [switching, setSwitching] = useState(false);

  const debounce = useRef<ReturnType<typeof setTimeout> | null>(null);

  useEffect(() => {
    if (!visible) {
      setQuery('');
      setResults([]);
      setSearchError(null);
      setSelected(null);
      setPassword('');
      setSwitching(false);
    }
  }, [visible]);

  const handleSearch = useCallback((text: string) => {
    setQuery(text);
    setSelected(null);
    setPassword('');
    setSearchError(null);
    if (debo
ImpersonationBanner function · typescript · L7-L35 (29 LOC)
src/components/ImpersonationBanner.tsx
export default function ImpersonationBanner(): React.JSX.Element | null {
  const { isImpersonating, isSwitching, impersonatedLabel, switchBack } = useImpersonation();
  const insets = useSafeAreaInsets();

  if (!isImpersonating) return null;

  return (
    <View style={[s.container, { paddingTop: insets.top + 4 }]}>
      <View style={s.content}>
        <Ionicons name="eye-outline" size={16} color="#ffffff" />
        <Text style={s.label} numberOfLines={1}>
          Viewing as {impersonatedLabel}
        </Text>
        <TouchableOpacity
          style={s.switchBackBtn}
          onPress={switchBack}
          disabled={isSwitching}
          activeOpacity={0.7}
        >
          {isSwitching ? (
            <ActivityIndicator size="small" color="#EF4444" />
          ) : (
            <Text style={s.switchBackText}>Switch Back</Text>
          )}
        </TouchableOpacity>
      </View>
    </View>
  );
}
LeaderboardRow function · typescript · L24-L66 (43 LOC)
src/components/LeaderboardRow.tsx
export default function LeaderboardRow({
  rank,
  user,
  adherence,
  isCurrentUser,
  onPress,
}: LeaderboardRowProps) {
  const { colors, isDark } = useTheme();

  const highlightBg = isCurrentUser
    ? isDark ? '#1a1033' : '#EDE9FE'
    : undefined;

  return (
    <TouchableOpacity
      style={[
        s.container,
        { borderColor: colors.border },
        highlightBg ? { backgroundColor: highlightBg } : undefined,
      ]}
      onPress={onPress}
      activeOpacity={0.7}
    >
      <Text style={[s.rank, { color: colors.text }]}>
        {MEDALS[rank] || `${rank}`}
      </Text>
      {user.profilePictureUrl ? (
        <Image source={{ uri: user.profilePictureUrl }} style={s.avatar} />
      ) : (
        <View style={[s.avatarPlaceholder, { backgroundColor: colors.inputBg }]}>
          <Ionicons name="person" size={14} color={colors.textTertiary} />
        </View>
      )}
      <Text style={[s.username, { color: colors.text }]} numberOfLines={1}>
        @{user.us
Powered by Repobility — scan your code at https://repobility.com
LoadingButton function · typescript · L26-L74 (49 LOC)
src/components/LoadingButton.tsx
export function LoadingButton({
  title,
  onPress,
  style,
  textStyle,
  loading: externalLoading,
  disabled,
  suppressErrorAlert,
}: LoadingButtonProps) {
  const { colors } = useTheme();
  const [internalLoading, setInternalLoading] = useState(false);
  const isLoading = externalLoading ?? internalLoading;

  const handlePress = useCallback(async () => {
    if (isLoading || disabled) return;
    setInternalLoading(true);
    try {
      await onPress();
    } catch (err) {
      if (!suppressErrorAlert) {
        Alert.alert('Error', getUserFriendlyError(err));
      } else {
        throw err;
      }
    } finally {
      setInternalLoading(false);
    }
  }, [onPress, isLoading, disabled, suppressErrorAlert]);

  return (
    <TouchableOpacity
      style={[
        styles.button,
        { backgroundColor: colors.accent },
        style,
        (isLoading || disabled) && styles.disabled,
      ]}
      onPress={handlePress}
      activeOpacity={0.7}
      disabled={isLoadi
LoadingScreen function · typescript · L9-L22 (14 LOC)
src/components/LoadingScreen.tsx
export function LoadingScreen({ message }: LoadingScreenProps) {
  const { colors } = useTheme();

  return (
    <View style={[styles.container, { backgroundColor: colors.background }]}>
      <ActivityIndicator size="large" color={colors.accent} />
      {message ? (
        <Text style={[styles.message, { color: colors.textSecondary }]}>
          {message}
        </Text>
      ) : null}
    </View>
  );
}
MealPickerModal function · typescript · L23-L97 (75 LOC)
src/components/MealPickerModal.tsx
export default function MealPickerModal({ visible, onClose, onSelect }: MealPickerModalProps) {
  const { colors } = useTheme();
  const [meals, setMeals] = useState<SavedMeal[]>([]);
  const [loading, setLoading] = useState(true);
  const [query, setQuery] = useState('');

  useEffect(() => {
    if (!visible) return;
    setQuery('');
    setLoading(true);
    getSavedMeals()
      .then(setMeals)
      .finally(() => setLoading(false));
  }, [visible]);

  const filtered = query.trim()
    ? meals.filter(m => m.name.toLowerCase().includes(query.toLowerCase()))
    : meals;

  return (
    <BottomSheet visible={visible} onClose={onClose}>
      <View style={s.content}>
        <Text style={[s.title, { color: colors.text }]}>Choose a Meal to Share</Text>

        <View style={[s.searchRow, { backgroundColor: colors.inputBg, borderColor: colors.inputBorder }]}>
          <Ionicons name="search" size={16} color={colors.textTertiary} />
          <TextInput
            style={[s.searchIn
OfflineBanner function · typescript · L9-L53 (45 LOC)
src/components/OfflineBanner.tsx
export function OfflineBanner({ isOnline, pendingCount }: OfflineBannerProps) {
  const height = useRef(new Animated.Value(0)).current;
  const [showBackOnline, setShowBackOnline] = useState(false);
  const wasOfflineRef = useRef(false);

  const visible = !isOnline || pendingCount > 0 || showBackOnline;

  useEffect(() => {
    if (!isOnline) {
      wasOfflineRef.current = true;
    } else if (wasOfflineRef.current && pendingCount === 0) {
      wasOfflineRef.current = false;
      setShowBackOnline(true);
      const timer = setTimeout(() => setShowBackOnline(false), 2000);
      return () => clearTimeout(timer);
    }
  }, [isOnline, pendingCount]);

  useEffect(() => {
    Animated.timing(height, {
      toValue: visible ? 28 : 0,
      duration: 250,
      useNativeDriver: false,
    }).start();
  }, [visible, height]);

  let label: string;
  let bgColor: string;
  if (showBackOnline) {
    label = 'Back online';
    bgColor = '#7C3AED';
  } else if (!isOnline) {
    label = 'Yo
PendingRequestCard function · typescript · L17-L58 (42 LOC)
src/components/PendingRequestCard.tsx
export default function PendingRequestCard({
  user,
  createdAt,
  onAccept,
  onDecline,
}: PendingRequestCardProps) {
  const { colors } = useTheme();

  const timeAgo = getTimeAgo(createdAt);

  return (
    <View style={[s.container, { backgroundColor: colors.surface, borderColor: colors.border }]}>
      <View style={s.row}>
        {user.profilePictureUrl ? (
          <Image source={{ uri: user.profilePictureUrl }} style={s.avatar} />
        ) : (
          <View style={[s.avatarPlaceholder, { backgroundColor: colors.inputBg }]}>
            <Ionicons name="person" size={22} color={colors.textTertiary} />
          </View>
        )}
        <View style={s.info}>
          <Text style={[s.text, { color: colors.text }]}>
            <Text style={s.bold}>@{user.username || 'user'}</Text> wants to be friends
          </Text>
          <Text style={[s.time, { color: colors.textTertiary }]}>{timeAgo}</Text>
        </View>
      </View>
      <View style={s.actions}>
        <Touc
getTimeAgo function · typescript · L60-L70 (11 LOC)
src/components/PendingRequestCard.tsx
function getTimeAgo(dateStr: string): string {
  const diff = Date.now() - new Date(dateStr).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.floor(hours / 24);
  if (days < 7) return `${days}d ago`;
  return `${Math.floor(days / 7)}w ago`;
}
PrivacyToggle function · typescript · L10-L29 (20 LOC)
src/components/PrivacyToggle.tsx
export default function PrivacyToggle({ isPrivate, onToggle }: PrivacyToggleProps) {
  const { colors } = useTheme();

  return (
    <View style={[s.container, { borderColor: colors.border }]}>
      <View style={s.textContainer}>
        <Text style={[s.label, { color: colors.text }]}>Private Account</Text>
        <Text style={[s.description, { color: colors.textSecondary }]}>
          Friends can't see your activity or progress
        </Text>
      </View>
      <Switch
        value={isPrivate}
        onValueChange={onToggle}
        trackColor={{ false: colors.inputBorder, true: '#7C3AED' }}
        thumbColor="#fff"
      />
    </View>
  );
}
SavedMealModal function · typescript · L83-L220 (138 LOC)
src/components/SavedMealModal.tsx
export default function SavedMealModal({
  visible,
  existing,
  prefill,
  onSave,
  onCancel,
}: Props) {
  const { colors } = useTheme();
  const [name, setName] = useState('');
  const [calories, setCalories] = useState('');
  const [protein, setProtein] = useState('');
  const [carbs, setCarbs] = useState('');
  const [fat, setFat] = useState('');
  const [notes, setNotes] = useState('');
  const [imageBase64, setImageBase64] = useState<string | null>(null);
  const [imagePreviewUri, setImagePreviewUri] = useState<string | null>(null);

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

    if (existing) {
      setName(existing.name);
      setCalories(String(existing.calories));
      setProtein(String(existing.protein));
      setCarbs(String(existing.carbs));
      setFat(String(existing.fat));
      setNotes(existing.notes || '');
      setImageBase64(null);
      setImagePreviewUri(existing.imageUrl || null);
    } else if (prefill) {
      setName(prefill.name);
      setCalor
Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
SharedMealCard function · typescript · L13-L65 (53 LOC)
src/components/SharedMealCard.tsx
export default function SharedMealCard({
  sharedMeal,
  onAddToMeals,
  onDelete,
}: SharedMealCardProps) {
  const { colors } = useTheme();

  const timeAgo = getTimeAgo(sharedMeal.createdAt);

  return (
    <View style={[s.container, { backgroundColor: colors.surface, borderColor: colors.border }]}>
      <View style={s.header}>
        {!sharedMeal.isRead && <View style={s.unreadDot} />}
        <Text style={[s.mealName, { color: colors.text }]}>{sharedMeal.mealName}</Text>
        <TouchableOpacity onPress={onDelete} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}>
          <Ionicons name="close-outline" size={20} color={colors.textTertiary} />
        </TouchableOpacity>
      </View>

      <View style={s.macroRow}>
        <MacroPill label="Cal" value={sharedMeal.calories} color="#7C3AED" labelColor={colors.textSecondary} />
        <MacroPill label="P" value={sharedMeal.protein} unit="g" color="#2196F3" labelColor={colors.textSecondary} />
        <MacroPill label="C" val
MacroPill function · typescript · L67-L86 (20 LOC)
src/components/SharedMealCard.tsx
function MacroPill({
  label,
  value,
  unit,
  color,
  labelColor,
}: {
  label: string;
  value: number;
  unit?: string;
  color: string;
  labelColor?: string;
}) {
  return (
    <View style={[s.pill, { borderColor: color + '40' }]}>
      <Text style={[s.pillValue, { color }]}>{value}{unit || ''}</Text>
      <Text style={[s.pillLabel, labelColor ? { color: labelColor } : undefined]}>{label}</Text>
    </View>
  );
}
getTimeAgo function · typescript · L88-L98 (11 LOC)
src/components/SharedMealCard.tsx
function getTimeAgo(dateStr: string): string {
  const diff = Date.now() - new Date(dateStr).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.floor(hours / 24);
  if (days < 7) return `${days}d ago`;
  return `${Math.floor(days / 7)}w ago`;
}
usePulse function · typescript · L5-L20 (16 LOC)
src/components/SkeletonLoader.tsx
function usePulse() {
  const opacity = useRef(new Animated.Value(0.3)).current;

  useEffect(() => {
    const animation = Animated.loop(
      Animated.sequence([
        Animated.timing(opacity, { toValue: 1, duration: 800, useNativeDriver: true }),
        Animated.timing(opacity, { toValue: 0.3, duration: 800, useNativeDriver: true }),
      ]),
    );
    animation.start();
    return () => animation.stop();
  }, [opacity]);

  return opacity;
}
SkeletonText function · typescript · L26-L44 (19 LOC)
src/components/SkeletonLoader.tsx
export function SkeletonText({ style, lines = 3, lastLineWidth = '60%' }: SkeletonProps & { lines?: number; lastLineWidth?: string }) {
  const { colors } = useTheme();
  const opacity = usePulse();

  return (
    <View style={style}>
      {Array.from({ length: lines }).map((_, i) => (
        <Animated.View
          key={i}
          style={[
            styles.textLine,
            { backgroundColor: colors.border, opacity },
            i === lines - 1 ? { width: lastLineWidth as any } : null,
          ]}
        />
      ))}
    </View>
  );
}
SkeletonCircle function · typescript · L46-L58 (13 LOC)
src/components/SkeletonLoader.tsx
export function SkeletonCircle({ style, size = 48 }: SkeletonProps & { size?: number }) {
  const { colors } = useTheme();
  const opacity = usePulse();

  return (
    <Animated.View
      style={[
        { width: size, height: size, borderRadius: size / 2, backgroundColor: colors.border, opacity },
        style,
      ]}
    />
  );
}
SkeletonCard function · typescript · L60-L73 (14 LOC)
src/components/SkeletonLoader.tsx
export function SkeletonCard({ style, height = 100 }: SkeletonProps & { height?: number }) {
  const { colors } = useTheme();
  const opacity = usePulse();

  return (
    <Animated.View
      style={[
        styles.card,
        { height, backgroundColor: colors.border, opacity },
        style,
      ]}
    />
  );
}
SkeletonRow function · typescript · L75-L88 (14 LOC)
src/components/SkeletonLoader.tsx
export function SkeletonRow({ style }: SkeletonProps) {
  const { colors } = useTheme();
  const opacity = usePulse();

  return (
    <View style={[styles.row, style]}>
      <Animated.View style={[styles.rowCircle, { backgroundColor: colors.border, opacity }]} />
      <View style={styles.rowLines}>
        <Animated.View style={[styles.textLine, { backgroundColor: colors.border, opacity, width: '70%' }]} />
        <Animated.View style={[styles.textLine, { backgroundColor: colors.border, opacity, width: '45%' }]} />
      </View>
    </View>
  );
}
Source: Repobility analyzer · https://repobility.com
SmartRecommendations function · typescript · L42-L216 (175 LOC)
src/components/SmartRecommendations.tsx
export default function SmartRecommendations({ onMealLogged }: Props) {
  const { colors, isDark } = useTheme();
  const [loading, setLoading] = useState(false);
  const [loadError, setLoadError] = useState<string | null>(null);
  const [recs, setRecs] = useState<Recommendation[]>([]);
  const [remaining, setRemaining] = useState<{ calories: number; protein: number; carbs: number; fat: number } | null>(null);
  const [visible, setVisible] = useState(false);
  const hasAutoFetched = useRef(false);

  const fetchRecommendations = useCallback(async () => {
    setLoading(true);
    setLoadError(null);
    try {
      const [apiKey, profile, meals, totals] = await Promise.all([
        getApiKey(),
        loadUserProfile(),
        getTodaysMeals(),
        getDailyTotals(),
      ]);

      if (!apiKey || !profile) {
        setLoading(false);
        return;
      }

      const goals = estimateDailyGoals(profile);
      const rem = {
        calories: Math.max(0, goals.calories - total
SubTabBar function · typescript · L17-L46 (30 LOC)
src/components/SubTabBar.tsx
export default function SubTabBar({ icons, activeIndex, onPress }: SubTabBarProps): React.JSX.Element {
  const { colors } = useTheme();

  return (
    <View style={[s.container, { borderBottomColor: colors.borderLight }]}>
      <View style={[s.segmented, { backgroundColor: colors.surface, borderColor: colors.border }]}>
        {icons.map((icon, i) => {
          const isActive = i === activeIndex;
          return (
            <TouchableOpacity
              key={icon.name}
              style={[s.segment, isActive && [s.segmentActive, { backgroundColor: colors.accent }]]}
              onPress={() => onPress(i)}
              activeOpacity={0.7}
            >
              <Ionicons
                name={icon.name}
                size={18}
                color={isActive ? '#fff' : colors.textSecondary}
              />
              {icon.badge && (
                <View style={s.badgeDot} />
              )}
            </TouchableOpacity>
          );
        })}
      </View
ThemedMarkdown function · typescript · L11-L88 (78 LOC)
src/components/ThemedMarkdown.tsx
export default function ThemedMarkdown({
  children,
  fontSize = 15,
  lineHeight = 21,
}: ThemedMarkdownProps): React.JSX.Element {
  const { colors } = useTheme();

  const mdStyles = useMemo(() => ({
    body: { color: colors.text, fontSize, lineHeight },
    paragraph: { marginTop: 0, marginBottom: 6 },
    strong: { fontWeight: '700' as const },
    em: { fontStyle: 'italic' as const },
    bullet_list: { marginVertical: 4 },
    ordered_list: { marginVertical: 4 },
    list_item: { marginVertical: 1 },
    heading1: { fontSize: 20, fontWeight: '700' as const, color: colors.text, marginVertical: 6 },
    heading2: { fontSize: 18, fontWeight: '700' as const, color: colors.text, marginVertical: 5 },
    heading3: { fontSize: 16, fontWeight: '600' as const, color: colors.text, marginVertical: 4 },
    code_inline: {
      backgroundColor: colors.card,
      color: colors.text,
      fontSize: fontSize - 2,
      paddingHorizontal: 4,
      borderRadius: 3,
    },
    fence: {
      
page 1 / 6next ›