Function bodies 299 total
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 {
NotiReactNativeDelegate 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}
</TexgetTimeAgo 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, { colEditMealModal 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.titImpersonateModal 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 (deboImpersonationBanner 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.usPowered 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={isLoadiLoadingScreen 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.searchInOfflineBanner 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 = 'YoPendingRequestCard 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}>
<ToucgetTimeAgo 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);
setCalorRepobility'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" valMacroPill 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 - totalSubTabBar 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>
);
})}
</ViewThemedMarkdown 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 ›