Function bodies 299 total
TopBar function · typescript · L13-L70 (58 LOC)src/components/TopBar.tsx
export default function TopBar(): React.JSX.Element {
const { colors } = useTheme();
const navigation = useNavigation<any>();
const [pictureUrl, setPictureUrl] = useState<string | null>(null);
const activeTabName = useNavigationState(state => {
const tabRoute = state?.routes?.[0];
const tabState = tabRoute?.state;
if (tabState) {
const activeRoute = tabState.routes[tabState.index ?? 0];
return activeRoute?.name ?? 'Home';
}
return 'Home';
});
useFocusEffect(
useCallback(() => {
loadUserProfile().then(profile => {
setPictureUrl(profile?.profilePictureUrl ?? null);
});
}, []),
);
return (
<View style={[s.bar, { backgroundColor: colors.background, borderBottomColor: colors.borderLight }]}>
<TouchableOpacity
onPress={() => navigation.navigate('ProfileScreen')}
activeOpacity={0.7}
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
>
{pictureUrl ? (
<Image UpdatePlanModal function · typescript · L37-L210 (174 LOC)src/components/UpdatePlanModal.tsx
export default function UpdatePlanModal({ visible, onClose, onPlanUpdated }: Props) {
const { colors, isDark } = useTheme();
const [request, setRequest] = useState('');
const [loading, setLoading] = useState(false);
const [preview, setPreview] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
const handleGenerate = useCallback(async () => {
if (!request.trim()) return;
setLoading(true);
setError(null);
setPreview(null);
try {
const [apiKey, profile] = await Promise.all([getApiKey(), loadUserProfile()]);
if (!apiKey) throw new Error('No API key found');
if (!profile) throw new Error('No profile found');
const systemPrompt = await buildChatSystemPrompt(profile);
const messages = [
{
role: 'user' as const,
content: `I'd like to update my nutrition/fitness plan. Here's what I want to change:\n\n${request.trim()}\n\nPlease generate a complete updated plan basegenerateNonce function · typescript · L15-L22 (8 LOC)src/contexts/AuthContext.tsx
function generateNonce(length = 32): string {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}useAuth function · typescript · L59-L61 (3 LOC)src/contexts/AuthContext.tsx
export function useAuth() {
return useContext(AuthContext);
}AuthProvider function · typescript · L63-L202 (140 LOC)src/contexts/AuthContext.tsx
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [session, setSession] = useState<Session | null>(null);
const [user, setUser] = useState<User | null>(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
supabase.auth.getSession().then(({ data: { session: s } }) => {
setSession(s);
setUser(s?.user ?? null);
setIsLoading(false);
});
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(_event, s) => {
setSession(s);
setUser(s?.user ?? null);
},
);
return () => subscription.unsubscribe();
}, []);
const signUp = useCallback(async (email: string, password: string) => {
const { error } = await supabase.auth.signUp({ email, password });
return { error: error?.message ?? null };
}, []);
const signIn = useCallback(async (email: string, password: string) => {
const { error } = await supabase.auth.signInWithPassword({ email, passChatProvider function · typescript · L36-L105 (70 LOC)src/contexts/ChatContext.tsx
export function ChatProvider({ children }: { children: React.ReactNode }) {
const [isReady, setIsReady] = useState(false);
const [messages, setMessages] = useState<Message[]>([]);
const [apiKey, setApiKey] = useState<string | null>(null);
const [profile, setProfile] = useState<UserProfile | null>(null);
const [conversationSummary, setConversationSummary] = useState<string | null>(null);
// Load initial state once on mount
useEffect(() => {
let cancelled = false;
Promise.all([
loadMessages(),
getApiKey(),
loadUserProfile(),
loadConversationSummary(),
]).then(([savedMessages, storedKey, storedProfile, storedSummary]) => {
if (cancelled) return;
setMessages(savedMessages);
setApiKey(storedKey);
setProfile(storedProfile);
setConversationSummary(storedSummary);
setIsReady(true);
});
return () => { cancelled = true; };
}, []);
// Persist messages whenever they change (debounced by React batchiuseChatContext function · typescript · L109-L115 (7 LOC)src/contexts/ChatContext.tsx
export function useChatContext(): ChatContextValue {
const ctx = useContext(ChatContext);
if (!ctx) {
throw new Error('useChatContext must be used within a ChatProvider');
}
return ctx;
}Generated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
useImpersonation function · typescript · L26-L28 (3 LOC)src/contexts/ImpersonationContext.tsx
export function useImpersonation() {
return useContext(ImpersonationContext);
}ImpersonationProvider function · typescript · L30-L112 (83 LOC)src/contexts/ImpersonationContext.tsx
export function ImpersonationProvider({ children }: { children: React.ReactNode }) {
const [isImpersonating, setIsImpersonating] = useState(false);
const [isSwitching, setIsSwitching] = useState(false);
const [impersonatedLabel, setImpersonatedLabel] = useState<string | null>(null);
const stashedSession = useRef<StashedSession | null>(null);
const switchToUser = useCallback(async (email: string, password: string, label: string) => {
try {
setIsSwitching(true);
const { data: { session: currentSession } } = await supabase.auth.getSession();
if (currentSession) {
stashedSession.current = {
access_token: currentSession.access_token,
refresh_token: currentSession.refresh_token,
};
}
await supabase.auth.signOut();
const { error } = await supabase.auth.signInWithPassword({ email, password });
if (error) {
if (stashedSession.current) {
await supabase.auth.setSession(stashedSession.ThemeProvider function · typescript · L101-L135 (35 LOC)src/contexts/ThemeContext.tsx
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const systemScheme = useColorScheme();
const [mode, setModeState] = useState<ThemeMode>('system');
const [loaded, setLoaded] = useState(false);
useEffect(() => {
AsyncStorage.getItem(STORAGE_KEY).then(stored => {
if (stored === 'light' || stored === 'dark' || stored === 'system') {
setModeState(stored);
}
setLoaded(true);
});
}, []);
const setMode = useCallback((newMode: ThemeMode) => {
setModeState(newMode);
AsyncStorage.setItem(STORAGE_KEY, newMode);
}, []);
const isDark = mode === 'dark' || (mode === 'system' && systemScheme === 'dark');
const colors = isDark ? DARK : LIGHT;
const value = useMemo(
() => ({ mode, isDark, colors, setMode }),
[mode, isDark, colors, setMode],
);
if (!loaded) return null;
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
}useTheme function · typescript · L137-L139 (3 LOC)src/contexts/ThemeContext.tsx
export function useTheme(): ThemeContextValue {
return useContext(ThemeContext);
}useAsyncData function · typescript · L22-L74 (53 LOC)src/hooks/useAsyncData.ts
export function useAsyncData<T>(
fetcher: () => Promise<T>,
options?: UseAsyncDataOptions<T>,
): UseAsyncDataResult<T> {
const deps = options?.deps ?? [];
const [data, setData] = useState<T | undefined>(options?.initialData);
const [loading, setLoading] = useState(!options?.manual);
const [refreshing, setRefreshing] = useState(false);
const [error, setError] = useState<string | null>(null);
const mountedRef = useRef(true);
useEffect(() => {
mountedRef.current = true;
return () => { mountedRef.current = false; };
}, []);
const fetchData = useCallback(async (isRefresh: boolean) => {
if (isRefresh) {
setRefreshing(true);
} else {
setLoading(true);
}
setError(null);
try {
const result = await fetcher();
if (mountedRef.current) {
setData(result);
}
} catch (err) {
if (mountedRef.current) {
setError(getUserFriendlyError(err));
}
} finally {
if (mountedRef.current) {
todayKey function · typescript · L8-L11 (4 LOC)src/hooks/useDayChange.ts
function todayKey(): string {
const d = new Date();
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
}useDayChange function · typescript · L22-L46 (25 LOC)src/hooks/useDayChange.ts
export function useDayChange(onDayChange: () => void): void {
const lastDate = useRef(todayKey());
useEffect(() => {
const check = () => {
const now = todayKey();
if (now !== lastDate.current) {
lastDate.current = now;
onDayChange();
}
};
const interval = setInterval(check, 30_000);
const handleAppState = (state: AppStateStatus) => {
if (state === 'active') check();
};
const subscription = AppState.addEventListener('change', handleAppState);
return () => {
clearInterval(interval);
subscription.remove();
};
}, [onDayChange]);
}useDeepLink function · typescript · L6-L25 (20 LOC)src/hooks/useDeepLink.ts
export function useDeepLink(onAction: (action: DeepLinkAction) => void) {
const onActionRef = useRef(onAction);
onActionRef.current = onAction;
const handleURL = useCallback((url: string | null) => {
if (!url) return;
if (url === 'noomibodi://add-photo') {
onActionRef.current('add-photo');
} else if (url === 'noomibodi://quick-log') {
onActionRef.current('quick-log');
}
}, []);
useEffect(() => {
Linking.getInitialURL().then(handleURL);
const sub = Linking.addEventListener('url', ({ url }) => handleURL(url));
return () => sub.remove();
}, [handleURL]);
}If a scraper extracted this row, it came from Repobility (https://repobility.com)
handleNotificationNavigation function · typescript · L16-L32 (17 LOC)src/hooks/useNotifications.ts
function handleNotificationNavigation(
remoteMessage: FirebaseMessagingTypes.RemoteMessage | null,
navRef: React.RefObject<NavigationContainerRef<any> | null>,
) {
if (!remoteMessage?.data?.type || !navRef.current) return;
switch (remoteMessage.data.type) {
case 'friend_request':
case 'friend_accepted':
case 'streak_milestone':
navRef.current.navigate('MainTabs' as never, { screen: 'Social' } as never);
break;
case 'shared_meal':
navRef.current.navigate('MainTabs' as never, { screen: 'Meals' } as never);
break;
}
}useNotifications function · typescript · L34-L71 (38 LOC)src/hooks/useNotifications.ts
export function useNotifications(
enabled: boolean,
navigationRef: React.RefObject<NavigationContainerRef<any> | null>,
) {
const initialized = useRef(false);
useEffect(() => {
if (!enabled || initialized.current) return;
initialized.current = true;
const messaging = getMessaging();
registerForPushNotifications().catch(() => {});
const unsubRefresh = onTokenRefresh(token => {
upsertToken(token).catch(() => {});
});
const unsubForeground = onMessage(messaging, async _remoteMessage => {
// No in-app banner; user already sees data on-screen
});
const unsubBackground = onNotificationOpenedApp(messaging, remoteMessage => {
handleNotificationNavigation(remoteMessage, navigationRef);
});
getInitialNotification(messaging).then(remoteMessage => {
handleNotificationNavigation(remoteMessage, navigationRef);
});
return () => {
initialized.current = false;
unsubRefresh();
unsubForeground();useOfflineSync function · typescript · L18-L52 (35 LOC)src/hooks/useOfflineSync.ts
export function useOfflineSync(onSynced?: () => void): OfflineSyncResult {
const { isOnline } = useNetworkStatus();
const wasOffline = useRef(false);
const [pendingCount, setPendingCount] = useState(0);
const refreshPendingCount = useCallback(async () => {
const q = await getQueue();
setPendingCount(q.length);
}, []);
const syncNow = useCallback(async () => {
const { synced } = await flushQueue();
await refreshPendingCount();
if (synced > 0) onSynced?.();
}, [onSynced, refreshPendingCount]);
// Flush when transitioning from offline → online
useEffect(() => {
if (!isOnline) {
wasOffline.current = true;
return;
}
if (wasOffline.current) {
wasOffline.current = false;
syncNow();
}
}, [isOnline, syncNow]);
// Check queue size on mount and whenever online state changes
useEffect(() => {
refreshPendingCount();
}, [isOnline, refreshPendingCount]);
return { isOnline, pendingCount, syncNow };
}useStaleFetch function · typescript · L12-L39 (28 LOC)src/hooks/useStaleFetch.ts
export function useStaleFetch(
fetchFn: (isRefresh: boolean) => Promise<void>,
staleTimeMs: number = 30_000,
) {
const lastFetchedAt = useRef<number>(0);
const fetchIfStale = useCallback(async () => {
const now = Date.now();
const elapsed = now - lastFetchedAt.current;
const isFirstLoad = lastFetchedAt.current === 0;
if (!isFirstLoad && elapsed < staleTimeMs) return;
lastFetchedAt.current = now;
await fetchFn(isFirstLoad ? false : true);
}, [fetchFn, staleTimeMs]);
const forceFetch = useCallback(async () => {
lastFetchedAt.current = Date.now();
await fetchFn(true);
}, [fetchFn]);
const markStale = useCallback(() => {
lastFetchedAt.current = 0;
}, []);
return { fetchIfStale, forceFetch, markStale };
}AddFriendModal function · typescript · L37-L226 (190 LOC)src/screens/AddFriendModal.tsx
export default function AddFriendModal({
visible,
onClose,
onFriendAdded,
}: AddFriendModalProps) {
const { colors } = useTheme();
const [query, setQuery] = useState('');
const [results, setResults] = useState<SearchResult[]>([]);
const [loading, setLoading] = useState(false);
const [actionLoading, setActionLoading] = useState<string | null>(null);
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
useEffect(() => {
if (!visible) {
setQuery('');
setResults([]);
}
}, [visible]);
const handleSearch = useCallback(async (text: string) => {
if (text.length < 2) {
setResults([]);
return;
}
setLoading(true);
try {
const users = await searchUsers(text);
const enriched = await Promise.all(
users.map(async u => {
const { status, friendshipId } = await getFriendshipStatus(u.id);
return { ...u, relationship: status, friendshipId };
}),
);
setResfmtNum function · typescript · L55-L57 (3 LOC)src/screens/AdminDashboard.tsx
function fmtNum(n: number): string {
return n.toLocaleString('en-US');
}fmtCost function · typescript · L59-L61 (3 LOC)src/screens/AdminDashboard.tsx
function fmtCost(n: number): string {
return `$${n.toFixed(n >= 1 ? 2 : 4)}`;
}shortDate function · typescript · L63-L66 (4 LOC)src/screens/AdminDashboard.tsx
function shortDate(iso: string): string {
const d = new Date(iso);
return `${d.getMonth() + 1}/${d.getDate()}`;
}Open data scored by Repobility · https://repobility.com
timeAgo function · typescript · L68-L76 (9 LOC)src/screens/AdminDashboard.tsx
function timeAgo(iso: string): string {
const diff = Date.now() - new Date(iso).getTime();
const mins = Math.floor(diff / 60000);
if (mins < 1) return 'just now';
if (mins < 60) return `${mins}m ago`;
const hrs = Math.floor(mins / 60);
if (hrs < 24) return `${hrs}h ago`;
return `${Math.floor(hrs / 24)}d ago`;
}OverviewCard function · typescript · L984-L1004 (21 LOC)src/screens/AdminDashboard.tsx
function OverviewCard({
label,
value,
sub,
color,
bgColor,
}: {
label: string;
value: string;
sub: string;
color: string;
bgColor: string;
}) {
return (
<View style={[s.overviewCard, { backgroundColor: bgColor }]}>
<Text style={[s.overviewLabel, { color }]}>{label}</Text>
<Text style={[s.overviewValue, { color }]}>{value}</Text>
<Text style={[s.overviewSub, { color: color + '99' }]}>{sub}</Text>
</View>
);
}TypingIndicator function · typescript · L62-L99 (38 LOC)src/screens/ChatScreen.tsx
function TypingIndicator({ color }: { color: string }) {
const dots = useRef([
new Animated.Value(0.3),
new Animated.Value(0.3),
new Animated.Value(0.3),
]).current;
useEffect(() => {
const animations = dots.map((dot, i) =>
Animated.loop(
Animated.sequence([
Animated.delay(i * 200),
Animated.timing(dot, { toValue: 1, duration: 400, useNativeDriver: true }),
Animated.timing(dot, { toValue: 0.3, duration: 400, useNativeDriver: true }),
]),
),
);
animations.forEach(a => a.start());
return () => animations.forEach(a => a.stop());
}, [dots]);
return (
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 4, paddingVertical: 4 }}>
{dots.map((dot, i) => (
<Animated.View
key={i}
style={{
width: 8,
height: 8,
borderRadius: 4,
backgroundColor: color,
opacity: dot,
}}
/>
ChatImage function · typescript · L103-L124 (22 LOC)src/screens/ChatScreen.tsx
function ChatImage({ uri }: { uri: string }) {
const { colors } = useTheme();
const [failed, setFailed] = useState(false);
if (failed) {
return (
<View style={[styles.messageImage, { backgroundColor: colors.inputBg, alignItems: 'center', justifyContent: 'center' }]}>
<Ionicons name="image-outline" size={32} color={colors.textTertiary} />
<Text style={{ fontSize: 11, color: colors.textTertiary, marginTop: 4 }}>Photo unavailable</Text>
</View>
);
}
return (
<Image
source={{ uri }}
style={styles.messageImage}
resizeMode="cover"
onError={() => setFailed(true)}
/>
);
}EditProfileScreen function · typescript · L35-L302 (268 LOC)src/screens/EditProfileScreen.tsx
export default function EditProfileScreen({ navigation }: EditProfileScreenProps) {
const { colors, isDark } = useTheme();
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const [uploading, setUploading] = useState(false);
const [isAdminUser, setIsAdminUser] = useState(false);
const [username, setUsername] = useState('');
const [displayName, setDisplayName] = useState('');
const [bio, setBio] = useState('');
const [pictureUrl, setPictureUrl] = useState<string | null>(null);
const [usernameError, setUsernameError] = useState<string | null>(null);
const [displayNameError, setDisplayNameError] = useState<string | null>(null);
const [bioError, setBioError] = useState<string | null>(null);
const [original, setOriginal] = useState<{
username: string;
displayName: string;
bio: string;
pictureUrl: string | null;
} | null>(null);
useEffect(() => {
Promise.all([loadUserProfile(), isAdmin()]).then(([procreateStyles function · typescript · L4-L132 (129 LOC)src/screens/FeedbackScreen.styles.ts
export default function createStyles(colors: ThemeColors, _isDark: boolean) {
return StyleSheet.create({
container: {
flex: 1,
},
headerBar: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 12,
paddingVertical: 8,
borderBottomWidth: StyleSheet.hairlineWidth,
},
headerTitle: {
fontSize: 17,
fontWeight: '700',
},
scrollContent: {
padding: 20,
paddingBottom: 40,
},
// ── Category Picker ──────────────────────────────────────────────
section: {
marginBottom: 20,
},
label: {
fontSize: 14,
fontWeight: '600',
marginBottom: 8,
},
categoryRow: {
flexDirection: 'row',
gap: 10,
},
categoryPill: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
gap: 6,
paddingVertical: 12,
borderRadius: 12,
borderWidth: 1.FeedbackScreen function · typescript · L38-L254 (217 LOC)src/screens/FeedbackScreen.tsx
export default function FeedbackScreen(): React.JSX.Element {
const { colors, isDark } = useTheme();
const navigation = useNavigation<any>();
const route = useRoute<any>();
const s = useMemo(() => createStyles(colors, isDark), [colors, isDark]);
const [category, setCategory] = useState<FeedbackCategory>('bug');
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const [screenshots, setScreenshots] = useState<ScreenshotItem[]>([]);
const sourceScreen = route.params?.sourceScreen ?? 'Unknown';
const deviceInfo = useMemo(() => getDeviceContext(), []);
const handleAddScreenshot = useCallback(async () => {
try {
const result = await launchImageLibrary({
mediaType: 'photo',
maxWidth: 1200,
maxHeight: 1200,
quality: 0.8,
includeBase64: true,
});
const asset = result.assets?.[0];
if (result.didCancel || !asset?.uri) return;
setScreenshots(prev => [
FriendProfileScreen function · typescript · L31-L312 (282 LOC)src/screens/FriendProfileScreen.tsx
export default function FriendProfileScreen({ route, navigation }: FriendProfileScreenProps) {
const { colors, isDark } = useTheme();
const { userId } = route.params;
const [profile, setProfile] = useState<PublicProfile | null>(null);
const [loading, setLoading] = useState(true);
const [relationship, setRelationship] = useState<RelationshipStatus>('none');
const [friendshipId, setFriendshipId] = useState<string | null>(null);
const [stats, setStats] = useState<FriendStats | null>(null);
useEffect(() => {
Promise.all([
getPublicProfile(userId),
getFriendshipStatus(userId),
getFriendStats(userId),
]).then(([p, fs, friendStats]) => {
setProfile(p);
setRelationship(fs.status);
setFriendshipId(fs.friendshipId);
setStats(friendStats);
setLoading(false);
});
}, [userId]);
const handleRemoveFriend = () => {
if (!friendshipId || !profile) return;
Alert.alert(
'Remove Friend',
`Remove @${profilRepobility — the code-quality scanner for AI-generated software · https://repobility.com
InsightsPage function · typescript · L38-L256 (219 LOC)src/screens/InsightsPage.tsx
export default function InsightsPage(): React.JSX.Element {
const { colors, isDark } = useTheme();
const isFocused = useIsFocused();
const [insights, setInsights] = useState<Insight[]>([]);
const [loading, setLoading] = useState(true);
const [loadError, setLoadError] = useState<string | null>(null);
const [refreshing, setRefreshing] = useState(false);
const [cacheExpired, setCacheExpired] = useState(false);
const [expandedId, setExpandedId] = useState<string | null>(null);
const load = useCallback(async (isRefresh = false) => {
if (isRefresh) {
setRefreshing(true);
} else {
setLoading(true);
}
setLoadError(null);
try {
if (isRefresh) {
// Only call Claude on explicit refresh
const result = await generateInsights(true);
setInsights(result.filter(i => !i.isDismissed));
setCacheExpired(false);
} else {
// On page load, only show cached insights (never auto-generate)
const { insiMacroPill function · typescript · L575-L596 (22 LOC)src/screens/MealsScreen.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>
);
}ProfilePage function · typescript · L30-L269 (240 LOC)src/screens/ProfilePage.tsx
export default function ProfilePage(): React.JSX.Element {
const { colors, isDark } = useTheme();
const navigation = useNavigation<any>();
const isFocused = useIsFocused();
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [loadError, setLoadError] = useState<string | null>(null);
const [profile, setProfile] = useState<UserProfile | null>(null);
const [stats, setStats] = useState<OverviewStats | null>(null);
const [planExpanded, setPlanExpanded] = useState(false);
const [updatePlanVisible, setUpdatePlanVisible] = useState(false);
const loadData = useCallback(
async (isRefresh: boolean) => {
if (isRefresh) {
setRefreshing(true);
} else {
setLoading(true);
}
setLoadError(null);
try {
const p = await loadUserProfile();
setProfile(p);
if (p) {
const goals = estimateDailyGoals(p);
const overview = await getOverviewStats(gMacroPill function · typescript · L271-L291 (21 LOC)src/screens/ProfilePage.tsx
function MacroPill({
label,
value,
color,
labelColor,
}: {
label: string;
value: number;
color: string;
labelColor?: string;
}) {
return (
<View style={[s.pill, { borderColor: color + '30' }]}>
<View style={[s.pillDot, { backgroundColor: color }]} />
<View>
<Text style={[s.pillValue, { color }]}>{value}g</Text>
<Text style={[s.pillLabel, labelColor ? { color: labelColor } : undefined]}>{label}</Text>
</View>
</View>
);
}ProfileScreen function · typescript · L30-L284 (255 LOC)src/screens/ProfileScreen.tsx
export default function ProfileScreen(): React.JSX.Element {
const { user, signOut } = useAuth();
const { mode, colors, setMode } = useTheme();
const { onResetProfile } = useContext(OnboardingContext);
const [initialLoading, setInitialLoading] = useState(true);
const [apiKeyInput, setApiKeyInput] = useState('');
const [savedApiKey, setSavedApiKey] = useState<string | null>(null);
const [status, setStatus] = useState<string | null>(null);
const [profile, setProfile] = useState<UserProfile | null>(null);
const [planExpanded, setPlanExpanded] = useState(false);
useEffect(() => {
const loadData = async () => {
try {
const [storedKey, storedProfile] = await Promise.all([
getApiKey(),
loadUserProfile(),
]);
if (storedKey) setSavedApiKey(storedKey);
if (storedProfile) setProfile(storedProfile);
} catch (error) {
console.warn('Failed to load profile data', error);
} finally {
setInitiGoalsSummary function · typescript · L288-L306 (19 LOC)src/screens/ProfileScreen.tsx
function GoalsSummary({ goals }: { goals: MacroGoals }) {
const { isDark, colors } = useTheme();
return (
<View style={s.goalsContainer}>
{/* Calorie ring-style highlight */}
<View style={[s.calorieCard, { backgroundColor: isDark ? '#1a1033' : '#EDE9FE' }]}>
<Text style={[s.calorieValue, { color: isDark ? '#A78BFA' : '#5B21B6' }]}>{goals.calories}</Text>
<Text style={[s.calorieUnit, { color: colors.accent }]}>cal / day</Text>
</View>
{/* Macro pills */}
<View style={s.macroPills}>
<MacroPill label="Protein" value={goals.protein} color="#2196F3" />
<MacroPill label="Carbs" value={goals.carbs} color="#FF9800" />
<MacroPill label="Fat" value={goals.fat} color="#9C27B0" />
</View>
</View>
);
}MacroPill function · typescript · L308-L327 (20 LOC)src/screens/ProfileScreen.tsx
function MacroPill({
label,
value,
color,
}: {
label: string;
value: number;
color: string;
}) {
const { colors } = useTheme();
return (
<View style={[s.pill, { borderColor: color + '30' }]}>
<View style={[s.pillDot, { backgroundColor: color }]} />
<View>
<Text style={[s.pillValue, { color }]}>{value}g</Text>
<Text style={[s.pillLabel, { color: colors.textSecondary }]}>{label}</Text>
</View>
</View>
);
}QuickLogPage function · typescript · L97-L99 (3 LOC)src/screens/QuickLogPage.tsx
export default function QuickLogPage({
refreshTrigger = 0,
onMealLogged = () => {},Generated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
MacroPill function · typescript · L643-L662 (20 LOC)src/screens/QuickLogPage.tsx
function MacroPill({
label,
value,
unit,
color,
textSecondary,
}: {
label: string;
value: number;
unit?: string;
color: string;
textSecondary?: string;
}) {
return (
<View style={s.pill}>
<Text style={[s.pillValue, { color }]}>{value}{unit || ''}</Text>
<Text style={[s.pillLabel, textSecondary ? { color: textSecondary } : undefined]}>{label}</Text>
</View>
);
}StatCard function · typescript · L618-L638 (21 LOC)src/screens/ReportsScreen.tsx
function StatCard({
colors,
icon,
iconColor,
label,
value,
}: {
colors: { card: string; text: string; textSecondary: string };
icon: string;
iconColor: string;
label: string;
value: string;
}) {
return (
<View style={[s.statCard, { backgroundColor: colors.card }]}>
<Ionicons name={icon} size={24} color={iconColor} />
<Text style={[s.statValue, { color: colors.text }]}>{value}</Text>
<Text style={[s.statLabel, { color: colors.textSecondary }]}>{label}</Text>
</View>
);
}WeightStatPill function · typescript · L640-L657 (18 LOC)src/screens/ReportsScreen.tsx
function WeightStatPill({
colors,
isDark,
label,
value,
}: {
colors: { textSecondary: string };
isDark: boolean;
label: string;
value: string;
}) {
return (
<View style={[s.wStatPill, { backgroundColor: isDark ? '#2d1f33' : '#f3e5f5' }]}>
<Text style={[s.wStatLabel, { color: colors.textSecondary }]}>{label}</Text>
<Text style={s.wStatValue}>{value}</Text>
</View>
);
}EmptyCard function · typescript · L659-L674 (16 LOC)src/screens/ReportsScreen.tsx
function EmptyCard({
colors,
message,
icon,
}: {
colors: { card: string; textTertiary: string };
message: string;
icon?: string;
}) {
return (
<View style={[s.emptyCard, { backgroundColor: colors.card }]}>
<Ionicons name={icon ?? 'analytics-outline'} size={28} color={colors.textTertiary} />
<Text style={[s.emptyText, { color: colors.textTertiary }]}>{message}</Text>
</View>
);
}SettingsPage function · typescript · L29-L279 (251 LOC)src/screens/SettingsPage.tsx
export default function SettingsPage(): React.JSX.Element {
const { user, signOut } = useAuth();
const { mode, colors, setMode } = useTheme();
const { onResetProfile } = useContext(OnboardingContext);
const [apiKeyInput, setApiKeyInput] = useState('');
const [savedApiKey, setSavedApiKey] = useState<string | null>(null);
const [status, setStatus] = useState<string | null>(null);
const [isPrivate, setIsPrivate] = useState(false);
useEffect(() => {
getApiKey().then(key => {
if (key) setSavedApiKey(key);
});
loadUserProfile().then(profile => {
if (profile) setIsPrivate(profile.isPrivate ?? false);
});
}, []);
const maskKey = (key: string | null) => {
if (!key) return 'No key saved';
if (key.length <= 8) return '********';
return `${key.slice(0, 4)}****${key.slice(-4)}`;
};
const [validating, setValidating] = useState(false);
const handleSave = async () => {
const key = apiKeyInput.trim();
if (!key) {
setStaSharedMealsPage function · typescript · L38-L255 (218 LOC)src/screens/SharedMealsPage.tsx
export default function SharedMealsPage({ onUnreadCountChange }: SharedMealsPageProps) {
const { colors } = useTheme();
const isFocused = useIsFocused();
const [loading, setLoading] = useState(true);
const [received, setReceived] = useState<SharedMeal[]>([]);
const [sent, setSent] = useState<SentSharedMeal[]>([]);
const [sentExpanded, setSentExpanded] = useState(false);
const [mealPickerVisible, setMealPickerVisible] = useState(false);
const [friendPickerVisible, setFriendPickerVisible] = useState(false);
const [selectedMealId, setSelectedMealId] = useState<string | null>(null);
const [refreshing, setRefreshing] = useState(false);
const [loadError, setLoadError] = useState<string | null>(null);
const refresh = useCallback(async (isRefresh = false) => {
if (isRefresh) setRefreshing(true);
setLoadError(null);
try {
const [receivedData, sentData] = await Promise.all([
getSharedWithMe(),
getSharedByMe(),
]);
setReceiveSentMealCard function · typescript · L257-L294 (38 LOC)src/screens/SharedMealsPage.tsx
function SentMealCard({ item }: { item: SentSharedMeal }) {
const { colors } = useTheme();
const recipient = item.sharedWithDisplayName || item.sharedWithUsername || 'someone';
const timeAgo = getTimeAgo(item.createdAt);
return (
<View style={[s.sentCard, { backgroundColor: colors.surface, borderColor: colors.border }]}>
<View style={s.sentHeader}>
<Text style={[s.sentMealName, { color: colors.text }]} numberOfLines={1}>
{item.mealName}
</Text>
<Text style={[s.sentTime, { color: colors.textTertiary }]}>{timeAgo}</Text>
</View>
<View style={s.sentMacros}>
<Text style={[s.sentMacroText, { color: colors.textSecondary }]}>
{item.calories} cal · {item.protein}g P · {item.carbs}g C · {item.fat}g F
</Text>
</View>
<View style={s.sentRecipientRow}>
{item.sharedWithProfilePicture ? (
<Image source={{ uri: item.sharedWithProfilePicture }} style={s.sentAvatar} />
) : (
getTimeAgo function · typescript · L296-L306 (11 LOC)src/screens/SharedMealsPage.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`;
}If a scraper extracted this row, it came from Repobility (https://repobility.com)
SocialScreen function · typescript · L29-L339 (311 LOC)src/screens/SocialScreen.tsx
export default function SocialScreen(): React.JSX.Element {
const { colors, isDark } = useTheme();
const navigation = useNavigation<any>();
const isFocused = useIsFocused();
const [loading, setLoading] = useState(true);
const [friends, setFriends] = useState<FriendWithProfile[]>([]);
const [pending, setPending] = useState<{ friendship: any; profile: any }[]>([]);
const [sentRequests, setSentRequests] = useState<{ friendship: any; profile: any }[]>([]);
const [activities, setActivities] = useState<ActivityFeedItem[]>([]);
const [leaderboard, setLeaderboard] = useState<LeaderboardEntry[]>([]);
const [weekRange, setWeekRange] = useState<WeekRange | null>(null);
const [addFriendVisible, setAddFriendVisible] = useState(false);
const [pendingExpanded, setPendingExpanded] = useState(true);
const [sentExpanded, setSentExpanded] = useState(false);
const [refreshing, setRefreshing] = useState(false);
const [loadError, setLoadError] = useState<string | null>(null);
getUserId function · typescript · L18-L21 (4 LOC)src/services/activityFeed.ts
async function getUserId(): Promise<string | null> {
const { data: { user } } = await supabase.auth.getUser();
return user?.id ?? null;
}isMilestone function · typescript · L25-L29 (5 LOC)src/services/activityFeed.ts
function isMilestone(streakDays: number): boolean {
if (MILESTONES.includes(streakDays)) return true;
if (streakDays > 180 && streakDays % 30 === 0) return true;
return false;
}