Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 6 additions & 10 deletions src/modules/diet/recipe/infrastructure/recipeRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function createRecipeRepository(): RecipeRepository {
}
}

export async function fetchUserRecipes(
async function fetchUserRecipes(
userId: User['uuid'],
): Promise<readonly Recipe[]> {
try {
Expand All @@ -36,9 +36,7 @@ export async function fetchUserRecipes(
}
}

export async function fetchRecipeById(
recipeId: Recipe['id'],
): Promise<Recipe | null> {
async function fetchRecipeById(recipeId: Recipe['id']): Promise<Recipe | null> {
try {
// Check cache first
const cached = recipeCacheStore.findInCache({ by: 'id', value: recipeId })
Expand All @@ -60,7 +58,7 @@ export async function fetchRecipeById(
}
}

export async function fetchUserRecipeByName(
async function fetchUserRecipeByName(
userId: User['uuid'],
name: Recipe['name'],
): Promise<readonly Recipe[]> {
Expand All @@ -76,9 +74,7 @@ export async function fetchUserRecipeByName(
}
}

export async function insertRecipe(
newRecipe: NewRecipe,
): Promise<Recipe | null> {
async function insertRecipe(newRecipe: NewRecipe): Promise<Recipe | null> {
try {
const insertedRecipe = await supabaseGateway.insertRecipe(newRecipe)
if (insertedRecipe !== null) {
Expand All @@ -91,7 +87,7 @@ export async function insertRecipe(
}
}

export async function updateRecipe(
async function updateRecipe(
recipeId: Recipe['id'],
newRecipe: Recipe,
): Promise<Recipe | null> {
Expand All @@ -110,7 +106,7 @@ export async function updateRecipe(
}
}

export async function deleteRecipe(recipeId: Recipe['id']): Promise<void> {
async function deleteRecipe(recipeId: Recipe['id']): Promise<void> {
try {
await supabaseGateway.deleteRecipe(recipeId)
recipeCacheStore.removeFromCache({ by: 'id', value: recipeId })
Expand Down
104 changes: 57 additions & 47 deletions src/modules/diet/recipe/infrastructure/supabase/realtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,60 +4,70 @@ import { SUPABASE_TABLE_RECIPES } from '~/modules/diet/recipe/infrastructure/sup
import { registerSubapabaseRealtimeCallback } from '~/shared/supabase/supabase'
import { logging } from '~/shared/utils/logging'

let initialized = false
export function createRecipeRealtimeService() {
let initialized = false

/**
* Sets up granular realtime subscription for recipe changes
* @param onRecipeChange - Callback for granular updates with event details
*/
export function setupRecipeRealtimeSubscription(
onRecipeChange: (event: {
eventType: 'INSERT' | 'UPDATE' | 'DELETE'
old?: Recipe
new?: Recipe
}) => void,
): void {
registerSubapabaseRealtimeCallback(
SUPABASE_TABLE_RECIPES,
recipeSchema,
onRecipeChange,
)
}

export function initializeRecipeRealtime(): void {
if (initialized) {
return
/**
* Sets up granular realtime subscription for recipe changes
* @param onRecipeChange - Callback for granular updates with event details
*/
function setupRecipeRealtimeSubscription(
onRecipeChange: (event: {
eventType: 'INSERT' | 'UPDATE' | 'DELETE'
old?: Recipe
new?: Recipe
}) => void,
): void {
registerSubapabaseRealtimeCallback(
SUPABASE_TABLE_RECIPES,
recipeSchema,
onRecipeChange,
)
}
logging.debug(`Recipe realtime initialized!`)
initialized = true
registerSubapabaseRealtimeCallback(
SUPABASE_TABLE_RECIPES,
recipeSchema,
(event) => {
logging.debug(`Event:`, event)

switch (event.eventType) {
case 'INSERT': {
if (event.new !== undefined) {
recipeCacheStore.upsertToCache(event.new)
function initializeRecipeRealtime(): void {
if (initialized) {
return
}
logging.debug(`Recipe realtime initialized!`)
initialized = true
registerSubapabaseRealtimeCallback(
SUPABASE_TABLE_RECIPES,
recipeSchema,
(event) => {
logging.debug(`Event:`, event)

switch (event.eventType) {
case 'INSERT': {
if (event.new !== undefined) {
recipeCacheStore.upsertToCache(event.new)
}
break
}
break
}

case 'UPDATE': {
if (event.new) {
recipeCacheStore.upsertToCache(event.new)
case 'UPDATE': {
if (event.new) {
recipeCacheStore.upsertToCache(event.new)
}
break
}
break
}

case 'DELETE': {
if (event.old) {
recipeCacheStore.removeFromCache({ by: 'id', value: event.old.id })
case 'DELETE': {
if (event.old) {
recipeCacheStore.removeFromCache({
by: 'id',
value: event.old.id,
})
}
break
}
break
}
}
},
)
},
)
}

return {
setupRecipeRealtimeSubscription,
initializeRecipeRealtime,
}
}
Comment thread
marcuscastelo marked this conversation as resolved.
Comment on lines +7 to 73
Copy link

Copilot AI Dec 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The realtime service has been wrapped in a factory pattern (createRecipeRealtimeService()), which is inconsistent with other realtime services in the codebase (e.g., day-diet/infrastructure/supabase/realtime.ts and macro-profile/infrastructure/supabase/realtime.ts).

These other modules export the initialization function directly with module-level singleton initialized state, not wrapped in a factory. The factory pattern here could break singleton behavior if multiple instances are created, potentially causing duplicate subscriptions.

Recommendation: Export initializeRecipeRealtime and setupRecipeRealtimeSubscription directly with module-level initialized state to match the established pattern in the codebase.

Copilot uses AI. Check for mistakes.
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import { type Database } from '~/shared/supabase/database.types'
import { parseWithStack } from '~/shared/utils/parseWithStack'

// Types
export type RecipeDTO = Database['public']['Tables']['recipes']['Row']
export type InsertRecipeDTO = Database['public']['Tables']['recipes']['Insert']
export type UpdateRecipeDTO = Database['public']['Tables']['recipes']['Update']
type RecipeDTO = Database['public']['Tables']['recipes']['Row']
type InsertRecipeDTO = Database['public']['Tables']['recipes']['Insert']
type UpdateRecipeDTO = Database['public']['Tables']['recipes']['Update']

function toInsertDTO(recipe: NewRecipe): InsertRecipeDTO {
return {
Expand Down