← back to ajthal__noomi-bodi

Function bodies 299 total

All specs Real LLM only Function bodies
deleteMeal function · typescript · L171-L178 (8 LOC)
src/services/mealLog.ts
export async function deleteMeal(id: string): Promise<void> {
  if (id.startsWith('pending-')) {
    await removePendingItem(id);
    return;
  }
  const { error } = await supabase.from('daily_logs').delete().eq('id', id);
  if (error) console.error('Error deleting meal:', error);
}
checkStreakMilestone function · typescript · L180-L203 (24 LOC)
src/services/mealLog.ts
async function checkStreakMilestone(): Promise<void> {
  try {
    const { data: plan } = await supabase
      .from('user_plans')
      .select('daily_calories')
      .eq('is_active', true)
      .order('created_at', { ascending: false })
      .limit(1)
      .single();

    if (!plan?.daily_calories) return;

    const stats = await getOverviewStats(plan.daily_calories);
    if (stats.streak > 0) {
      await checkAndRecordMilestone(stats.streak);
      const userId = await getUserId();
      if (userId) {
        sendMilestoneNotifications(userId, stats.streak).catch(() => {});
      }
    }
  } catch {
    // Non-critical: silently fail
  }
}
getDailyTotals function · typescript · L205-L217 (13 LOC)
src/services/mealLog.ts
export async function getDailyTotals(date?: Date): Promise<DailyMacroTotals> {
  const meals = date ? await getMealsForDate(date) : await getTodaysMeals();
  return meals.reduce<DailyMacroTotals>(
    (acc, m) => ({
      calories: acc.calories + m.calories,
      protein: acc.protein + m.protein,
      carbs: acc.carbs + m.carbs,
      fat: acc.fat + m.fat,
      mealCount: acc.mealCount + 1,
    }),
    { calories: 0, protein: 0, carbs: 0, fat: 0, mealCount: 0 },
  );
}
getUserId function · typescript · L17-L20 (4 LOC)
src/services/notifications.ts
async function getUserId(): Promise<string | null> {
  const { data: { user } } = await supabase.auth.getUser();
  return user?.id ?? null;
}
registerForPushNotifications function · typescript · L22-L48 (27 LOC)
src/services/notifications.ts
export async function registerForPushNotifications(): Promise<string | null> {
  const messaging = getMessaging();
  const authStatus = await requestPermission(messaging);
  const enabled =
    authStatus === AuthorizationStatus.AUTHORIZED ||
    authStatus === AuthorizationStatus.PROVISIONAL;

  if (!enabled) return null;

  const fcmToken = await getToken(messaging);
  if (!fcmToken) return null;

  const userId = await getUserId();
  if (!userId) return null;

  // Remove stale tokens: if this device's token was registered under a
  // different user (e.g. after sign-out / sign-in with another account),
  // delete the old entry so the previous user stops receiving pushes here.
  await supabase.rpc('claim_device_token', { p_fcm_token: fcmToken });

  await supabase.from('device_tokens').upsert(
    { user_id: userId, fcm_token: fcmToken, platform: 'ios', updated_at: new Date().toISOString() },
    { onConflict: 'user_id,fcm_token' },
  );

  return fcmToken;
}
unregisterPushToken function · typescript · L50-L66 (17 LOC)
src/services/notifications.ts
export async function unregisterPushToken(): Promise<void> {
  try {
    const fcmToken = await getToken(getMessaging());
    if (!fcmToken) return;

    const userId = await getUserId();
    if (!userId) return;

    await supabase
      .from('device_tokens')
      .delete()
      .eq('user_id', userId)
      .eq('fcm_token', fcmToken);
  } catch {
    // Best-effort cleanup
  }
}
onTokenRefresh function · typescript · L68-L70 (3 LOC)
src/services/notifications.ts
export function onTokenRefresh(callback: (token: string) => void): () => void {
  return firebaseOnTokenRefresh(getMessaging(), callback);
}
Powered by Repobility — scan your code at https://repobility.com
upsertToken function · typescript · L72-L80 (9 LOC)
src/services/notifications.ts
export async function upsertToken(fcmToken: string): Promise<void> {
  const userId = await getUserId();
  if (!userId) return;

  await supabase.from('device_tokens').upsert(
    { user_id: userId, fcm_token: fcmToken, platform: 'ios', updated_at: new Date().toISOString() },
    { onConflict: 'user_id,fcm_token' },
  );
}
sendNotification function · typescript · L82-L93 (12 LOC)
src/services/notifications.ts
export async function sendNotification(
  type: NotificationType,
  recipientId: string,
  data: Record<string, string>,
): Promise<void> {
  await withRetry(
    () => supabase.functions.invoke('send-notification', {
      body: { type, recipientId, data },
    }),
    { maxRetries: 1 },
  );
}
sendMilestoneNotifications function · typescript · L95-L102 (8 LOC)
src/services/notifications.ts
export async function sendMilestoneNotifications(
  userId: string,
  streakDays: number,
): Promise<void> {
  await supabase.functions.invoke('send-notification', {
    body: { type: 'streak_milestone', userId, data: { streakDays: String(streakDays) } },
  });
}
todayKey function · typescript · L36-L39 (4 LOC)
src/services/offlineStore.ts
function todayKey(): string {
  const d = new Date();
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
}
nextPendingId function · typescript · L42-L44 (3 LOC)
src/services/offlineStore.ts
function nextPendingId(): string {
  return `pending-${Date.now()}-${++pendingIdCounter}`;
}
getUserId function · typescript · L46-L49 (4 LOC)
src/services/offlineStore.ts
async function getUserId(): Promise<string | null> {
  const { data: { user } } = await supabase.auth.getUser();
  return user?.id ?? null;
}
getQueue function · typescript · L53-L60 (8 LOC)
src/services/offlineStore.ts
export async function getQueue(): Promise<QueueItem[]> {
  try {
    const json = await AsyncStorage.getItem(QUEUE_KEY);
    return json ? JSON.parse(json) : [];
  } catch {
    return [];
  }
}
saveQueue function · typescript · L62-L64 (3 LOC)
src/services/offlineStore.ts
async function saveQueue(queue: QueueItem[]): Promise<void> {
  await AsyncStorage.setItem(QUEUE_KEY, JSON.stringify(queue));
}
All rows scored by the Repobility analyzer (https://repobility.com)
enqueueMealLog function · typescript · L70-L95 (26 LOC)
src/services/offlineStore.ts
export async function enqueueMealLog(
  data: MealData,
  imageBase64?: string,
): Promise<MealEntry> {
  const id = nextPendingId();
  const now = Date.now();

  const item: MealQueueItem = {
    id,
    type: 'meal',
    payload: { data, imageBase64 },
    createdAt: now,
  };

  const queue = await getQueue();
  queue.push(item);
  await saveQueue(queue);

  return {
    id,
    ...data,
    timestamp: now,
    imageBase64,
    imageUri: imageBase64 ? `data:image/jpeg;base64,${imageBase64}` : undefined,
  };
}
enqueueWeightLog function · typescript · L98-L108 (11 LOC)
src/services/offlineStore.ts
export async function enqueueWeightLog(weightKg: number): Promise<void> {
  const item: WeightQueueItem = {
    id: nextPendingId(),
    type: 'weight',
    payload: { weightKg },
    createdAt: Date.now(),
  };
  const queue = await getQueue();
  queue.push(item);
  await saveQueue(queue);
}
removePendingItem function · typescript · L111-L114 (4 LOC)
src/services/offlineStore.ts
export async function removePendingItem(id: string): Promise<void> {
  const queue = await getQueue();
  await saveQueue(queue.filter(q => q.id !== id));
}
getPendingMealsForToday function · typescript · L117-L134 (18 LOC)
src/services/offlineStore.ts
export async function getPendingMealsForToday(): Promise<MealEntry[]> {
  const queue = await getQueue();
  const startOfDay = new Date();
  startOfDay.setHours(0, 0, 0, 0);
  const dayStart = startOfDay.getTime();

  return queue
    .filter((q): q is MealQueueItem => q.type === 'meal' && q.createdAt >= dayStart)
    .map(q => ({
      id: q.id,
      ...q.payload.data,
      timestamp: q.createdAt,
      imageBase64: q.payload.imageBase64,
      imageUri: q.payload.imageBase64
        ? `data:image/jpeg;base64,${q.payload.imageBase64}`
        : undefined,
    }));
}
cacheTodaysMeals function · typescript · L219-L222 (4 LOC)
src/services/offlineStore.ts
export async function cacheTodaysMeals(meals: MealEntry[]): Promise<void> {
  const payload: CachedMeals = { dateKey: todayKey(), meals };
  await AsyncStorage.setItem(CACHE_MEALS_KEY, JSON.stringify(payload));
}
getCachedTodaysMeals function · typescript · L224-L234 (11 LOC)
src/services/offlineStore.ts
export async function getCachedTodaysMeals(): Promise<MealEntry[] | null> {
  try {
    const json = await AsyncStorage.getItem(CACHE_MEALS_KEY);
    if (!json) return null;
    const cached: CachedMeals = JSON.parse(json);
    if (cached.dateKey !== todayKey()) return null;
    return cached.meals;
  } catch {
    return null;
  }
}
cacheProfile function · typescript · L236-L238 (3 LOC)
src/services/offlineStore.ts
export async function cacheProfile(profile: UserProfile): Promise<void> {
  await AsyncStorage.setItem(CACHE_PROFILE_KEY, JSON.stringify(profile));
}
getCachedProfile function · typescript · L240-L247 (8 LOC)
src/services/offlineStore.ts
export async function getCachedProfile(): Promise<UserProfile | null> {
  try {
    const json = await AsyncStorage.getItem(CACHE_PROFILE_KEY);
    return json ? JSON.parse(json) : null;
  } catch {
    return null;
  }
}
Citation: Repobility (2026). State of AI-Generated Code. https://repobility.com/research/
cacheSavedMeals function · typescript · L249-L251 (3 LOC)
src/services/offlineStore.ts
export async function cacheSavedMeals(meals: SavedMeal[]): Promise<void> {
  await AsyncStorage.setItem(CACHE_SAVED_MEALS_KEY, JSON.stringify(meals));
}
getCachedSavedMeals function · typescript · L253-L260 (8 LOC)
src/services/offlineStore.ts
export async function getCachedSavedMeals(): Promise<SavedMeal[] | null> {
  try {
    const json = await AsyncStorage.getItem(CACHE_SAVED_MEALS_KEY);
    return json ? JSON.parse(json) : null;
  } catch {
    return null;
  }
}
clearOfflineData function · typescript · L262-L266 (5 LOC)
src/services/offlineStore.ts
export async function clearOfflineData(): Promise<void> {
  await AsyncStorage.multiRemove([
    QUEUE_KEY, CACHE_MEALS_KEY, CACHE_PROFILE_KEY, CACHE_SAVED_MEALS_KEY,
  ]);
}
getUserId function · typescript · L17-L20 (4 LOC)
src/services/profileService.ts
async function getUserId(): Promise<string | null> {
  const { data: { user } } = await supabase.auth.getUser();
  return user?.id ?? null;
}
validateUsername function · typescript · L26-L36 (11 LOC)
src/services/profileService.ts
export function validateUsername(username: string, skipProfanity = false): string | null {
  if (!username) return 'Username is required';
  if (username.length < 3) return 'Username must be at least 3 characters';
  if (username.length > 20) return 'Username must be 20 characters or fewer';
  if (!USERNAME_REGEX.test(username)) {
    return 'Only letters, numbers, and underscores allowed';
  }
  if (/^[_0-9]/.test(username)) return 'Username must start with a letter';
  if (!skipProfanity && containsProhibitedWord(username)) return 'This username is not available';
  return null;
}
checkUsernameAvailable function · typescript · L38-L53 (16 LOC)
src/services/profileService.ts
export async function checkUsernameAvailable(username: string): Promise<boolean> {
  const userId = await getUserId();
  const { data, error } = await supabase
    .from('public_profiles')
    .select('id')
    .ilike('username', username)
    .limit(1)
    .single();

  if (error && error.code === 'PGRST116') return true; // no rows
  if (error) {
    console.error('Error checking username:', error);
    return false;
  }
  return data?.id === userId;
}
searchUsers function · typescript · L57-L75 (19 LOC)
src/services/profileService.ts
export async function searchUsers(query: string): Promise<PublicProfile[]> {
  const userId = await getUserId();
  if (!userId || query.length < 2) return [];

  try {
    const { data, error } = await supabase
      .from('public_profiles')
      .select('id, username, display_name, profile_picture_url, bio, is_private')
      .or(`username.ilike.%${query}%,display_name.ilike.%${query}%`)
      .neq('id', userId)
      .limit(10);

    if (error) throw error;
    return (data ?? []).map(rowToPublicProfile);
  } catch (error) {
    console.error('Error searching users:', error);
    return [];
  }
}
adminSearchUsers function · typescript · L84-L86 (3 LOC)
src/services/profileService.ts
export async function adminSearchUsers(
  query: string,
): Promise<{ data: AdminProfile[]; error: string | null }> {
Repobility · open methodology · https://repobility.com/research/
getPublicProfile function · typescript · L115-L129 (15 LOC)
src/services/profileService.ts
export async function getPublicProfile(userId: string): Promise<PublicProfile | null> {
  try {
    const { data, error } = await supabase
      .from('public_profiles')
      .select('id, username, display_name, profile_picture_url, bio, is_private')
      .eq('id', userId)
      .single();

    if (error || !data) return null;
    return rowToPublicProfile(data);
  } catch (error) {
    console.error('Error fetching public profile:', error);
    return null;
  }
}
rowToPublicProfile function · typescript · L131-L140 (10 LOC)
src/services/profileService.ts
function rowToPublicProfile(row: any): PublicProfile {
  return {
    id: row.id,
    username: row.username || null,
    displayName: row.display_name || null,
    profilePictureUrl: row.profile_picture_url || null,
    bio: row.bio || null,
    isPrivate: row.is_private ?? false,
  };
}
base64ToArrayBuffer function · typescript · L144-L152 (9 LOC)
src/services/profileService.ts
function base64ToArrayBuffer(base64: string): ArrayBuffer {
  const binaryString = atob(base64);
  const len = binaryString.length;
  const bytes = new Uint8Array(len);
  for (let i = 0; i < len; i++) {
    bytes[i] = binaryString.charCodeAt(i);
  }
  return bytes.buffer;
}
uploadProfilePicture function · typescript · L154-L198 (45 LOC)
src/services/profileService.ts
export async function uploadProfilePicture(base64Data: string): Promise<string> {
  const userId = await getUserId();
  if (!userId) throw new Error('Not authenticated');

  const timestamp = Date.now();
  const filePath = `${userId}/${timestamp}.jpg`;

  const arrayBuffer = base64ToArrayBuffer(base64Data);

  const { error: uploadError } = await supabase.storage
    .from('profile-pictures')
    .upload(filePath, arrayBuffer, { contentType: 'image/jpeg', upsert: true });

  if (uploadError) throw uploadError;

  const { data: urlData } = supabase.storage
    .from('profile-pictures')
    .getPublicUrl(filePath);

  const publicUrl = urlData.publicUrl;

  await supabase.from('profiles').update({
    profile_picture_url: publicUrl,
    updated_at: new Date().toISOString(),
  }).eq('id', userId);

  // Clean up old pictures
  try {
    const { data: files } = await supabase.storage
      .from('profile-pictures')
      .list(userId);
    if (files && files.length > 1) {
      const oldFi
updateProfileFields function · typescript · L202-L220 (19 LOC)
src/services/profileService.ts
export async function updateProfileFields(
  fields: Partial<{
    username: string;
    display_name: string | null;
    profile_picture_url: string | null;
    bio: string | null;
    is_private: boolean;
  }>,
): Promise<void> {
  const userId = await getUserId();
  if (!userId) throw new Error('Not authenticated');

  const { error } = await supabase
    .from('profiles')
    .update({ ...fields, updated_at: new Date().toISOString() })
    .eq('id', userId);

  if (error) throw error;
}
suggestUsernameFromEmail function · typescript · L222-L225 (4 LOC)
src/services/profileService.ts
export function suggestUsernameFromEmail(email: string): string {
  const local = email.split('@')[0] || '';
  return local.replace(/[^a-zA-Z0-9_]/g, '_').slice(0, 20).toLowerCase();
}
getUserId function · typescript · L50-L53 (4 LOC)
src/services/reportData.ts
async function getUserId(): Promise<string | null> {
  const { data: { user } } = await supabase.auth.getUser();
  return user?.id ?? null;
}
toLocalDateString function · typescript · L56-L61 (6 LOC)
src/services/reportData.ts
function toLocalDateString(date: Date): string {
  const y = date.getFullYear();
  const m = String(date.getMonth() + 1).padStart(2, '0');
  const d = String(date.getDate()).padStart(2, '0');
  return `${y}-${m}-${d}`;
}
Powered by Repobility — scan your code at https://repobility.com
getDailySummaries function · typescript · L70-L111 (42 LOC)
src/services/reportData.ts
export async function getDailySummaries(days: number): Promise<DailySummary[]> {
  const userId = await getUserId();
  if (!userId) return [];

  let query = supabase
    .from('daily_logs')
    .select('logged_at, calories, protein_g, carbs_g, fat_g')
    .eq('user_id', userId)
    .order('logged_at', { ascending: true });

  if (days > 0) {
    const start = new Date();
    start.setHours(0, 0, 0, 0);
    start.setDate(start.getDate() - (days - 1));
    query = query.gte('logged_at', start.toISOString());
  }

  const { data: rows, error } = await query;
  if (error || !rows) return [];

  const map = new Map<string, DailySummary>();

  for (const r of rows) {
    const dateKey = toLocalDateString(new Date(r.logged_at));
    const existing = map.get(dateKey) ?? {
      date: dateKey,
      calories: 0,
      protein: 0,
      carbs: 0,
      fat: 0,
      mealCount: 0,
    };
    existing.calories += r.calories || 0;
    existing.protein += Number(r.protein_g) || 0;
    existing.carb
getOverviewStats function · typescript · L118-L175 (58 LOC)
src/services/reportData.ts
export async function getOverviewStats(goalCalories: number): Promise<OverviewStats> {
  const summaries = await getDailySummaries(0);

  const dateSet = new Set(summaries.map(s => s.date));
  const daysTracked = dateSet.size;

  // Streak: walk backwards from today.
  // Today counts if it has any logged meals; then check consecutive prior days.
  let streak = 0;
  const cursor = new Date();
  cursor.setHours(0, 0, 0, 0);
  const todayKey = toLocalDateString(cursor);

  if (dateSet.has(todayKey)) {
    // Today has meals — include it and walk backwards
    streak++;
    cursor.setDate(cursor.getDate() - 1);
    while (dateSet.has(toLocalDateString(cursor))) {
      streak++;
      cursor.setDate(cursor.getDate() - 1);
    }
  } else {
    // Today has no meals yet — start from yesterday
    cursor.setDate(cursor.getDate() - 1);
    while (dateSet.has(toLocalDateString(cursor))) {
      streak++;
      cursor.setDate(cursor.getDate() - 1);
    }
  }

  // Adherence: current calendar we
getWeightLogs function · typescript · L178-L203 (26 LOC)
src/services/reportData.ts
export async function getWeightLogs(days?: number): Promise<WeightLog[]> {
  const userId = await getUserId();
  if (!userId) return [];

  let query = supabase
    .from('weight_logs')
    .select('id, weight_kg, logged_at')
    .eq('user_id', userId)
    .order('logged_at', { ascending: true });

  if (days && days > 0) {
    const start = new Date();
    start.setHours(0, 0, 0, 0);
    start.setDate(start.getDate() - (days - 1));
    query = query.gte('logged_at', start.toISOString());
  }

  const { data: rows, error } = await query;
  if (error || !rows) return [];

  return rows.map(r => ({
    id: r.id,
    weightKg: Number(r.weight_kg),
    loggedAt: r.logged_at,
  }));
}
logWeight function · typescript · L206-L241 (36 LOC)
src/services/reportData.ts
export async function logWeight(weightKg: number): Promise<void> {
  const userId = await getUserId();
  if (!userId) throw new Error('Not authenticated');

  try {
    const todayStart = new Date();
    todayStart.setHours(0, 0, 0, 0);
    const tomorrowStart = new Date(todayStart);
    tomorrowStart.setDate(tomorrowStart.getDate() + 1);

    await supabase
      .from('weight_logs')
      .delete()
      .eq('user_id', userId)
      .gte('logged_at', todayStart.toISOString())
      .lt('logged_at', tomorrowStart.toISOString());

    const { error } = await supabase
      .from('weight_logs')
      .insert({ user_id: userId, weight_kg: weightKg });

    if (error) throw error;
  } catch (err) {
    const msg = String((err as any)?.message ?? err).toLowerCase();
    const isNetwork =
      msg.includes('network') ||
      msg.includes('fetch') ||
      msg.includes('timeout') ||
      msg.includes('failed to fetch');
    if (isNetwork) {
      await enqueueWeightLog(weightKg);
      re
getFriendStats function · typescript · L244-L265 (22 LOC)
src/services/reportData.ts
export async function getFriendStats(friendId: string): Promise<FriendStats | null> {
  const { data, error } = await supabase.rpc('get_friend_stats', { p_friend_id: friendId });
  if (error || !data) return null;
  return {
    streak: data.streak ?? 0,
    daysTracked: data.days_tracked ?? 0,
    adherencePct: data.adherence_pct ?? 0,
    goalType: data.goal_type ?? null,
    goalCalories: data.goal_calories ?? null,
    goalProtein: data.goal_protein ?? null,
    goalCarbs: data.goal_carbs ?? null,
    goalFat: data.goal_fat ?? null,
    startWeightKg: data.start_weight_kg ?? null,
    currentWeightKg: data.current_weight_kg ?? null,
    weightChangeKg: data.weight_change_kg ?? null,
    avgCalories: data.avg_calories ?? 0,
    avgProtein: data.avg_protein ?? 0,
    avgCarbs: data.avg_carbs ?? 0,
    avgFat: data.avg_fat ?? 0,
    avgDays: data.avg_days ?? 0,
  };
}
getUserId function · typescript · L26-L29 (4 LOC)
src/services/savedMeals.ts
async function getUserId(): Promise<string | null> {
  const { data: { user } } = await supabase.auth.getUser();
  return user?.id ?? null;
}
rowToSavedMeal function · typescript · L31-L43 (13 LOC)
src/services/savedMeals.ts
function rowToSavedMeal(row: any): SavedMeal {
  return {
    id: row.id,
    name: row.meal_name,
    calories: row.calories || 0,
    protein: Number(row.protein_g) || 0,
    carbs: Number(row.carbs_g) || 0,
    fat: Number(row.fat_g) || 0,
    notes: row.notes,
    imageUrl: row.image_url,
    createdAt: new Date(row.created_at).getTime(),
  };
}
getSavedMeals function · typescript · L47-L68 (22 LOC)
src/services/savedMeals.ts
export async function getSavedMeals(): Promise<SavedMeal[]> {
  const userId = await getUserId();
  if (!userId) return [];

  try {
    const { data: rows, error } = await supabase
      .from('saved_meals')
      .select('*')
      .eq('user_id', userId)
      .order('created_at', { ascending: false });

    if (error) throw error;

    const meals = (rows ?? []).map(rowToSavedMeal);
    await cacheSavedMeals(meals);
    return meals;
  } catch (error) {
    console.error('Error loading saved meals:', error);
    const cached = await getCachedSavedMeals();
    return cached ?? [];
  }
}
All rows scored by the Repobility analyzer (https://repobility.com)
saveMeal function · typescript · L70-L95 (26 LOC)
src/services/savedMeals.ts
export async function saveMeal(input: SavedMealInput): Promise<SavedMeal> {
  const userId = await getUserId();
  if (!userId) throw new Error('Not authenticated');

  const imageUrl = input.imageBase64
    ? `data:image/jpeg;base64,${input.imageBase64}`
    : null;

  const { data: row, error } = await supabase
    .from('saved_meals')
    .insert({
      user_id: userId,
      meal_name: input.name,
      calories: input.calories,
      protein_g: input.protein,
      carbs_g: input.carbs,
      fat_g: input.fat,
      notes: input.notes || null,
      image_url: imageUrl,
    })
    .select()
    .single();

  if (error || !row) throw error || new Error('Failed to save meal');
  return rowToSavedMeal(row);
}
updateSavedMeal function · typescript · L97-L126 (30 LOC)
src/services/savedMeals.ts
export async function updateSavedMeal(
  id: string,
  input: SavedMealInput,
): Promise<SavedMeal> {
  const imageUrl = input.imageBase64
    ? `data:image/jpeg;base64,${input.imageBase64}`
    : undefined;

  const updateObj: Record<string, unknown> = {
    meal_name: input.name,
    calories: input.calories,
    protein_g: input.protein,
    carbs_g: input.carbs,
    fat_g: input.fat,
    notes: input.notes || null,
  };
  if (imageUrl !== undefined) {
    updateObj.image_url = imageUrl;
  }

  const { data: row, error } = await supabase
    .from('saved_meals')
    .update(updateObj)
    .eq('id', id)
    .select()
    .single();

  if (error || !row) throw error || new Error('Failed to update saved meal');
  return rowToSavedMeal(row);
}
deleteSavedMeal function · typescript · L128-L131 (4 LOC)
src/services/savedMeals.ts
export async function deleteSavedMeal(id: string): Promise<void> {
  const { error } = await supabase.from('saved_meals').delete().eq('id', id);
  if (error) console.error('Error deleting saved meal:', error);
}
‹ prevpage 5 / 6next ›