fix: corregir race condition en favoritos con Supabase INITIAL_SESSION

This commit is contained in:
2026-03-04 16:03:11 -05:00
parent c178523c7e
commit ef5955cea2
3 changed files with 41 additions and 17 deletions

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, onMounted, onUnmounted } from 'vue' import { computed, onMounted, onUnmounted, watch } from 'vue'
import { RouterView, useRoute } from "vue-router"; import { RouterView, useRoute } from "vue-router";
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import MainLayout from "./components/layouts/MainLayout.vue"; import MainLayout from "./components/layouts/MainLayout.vue";
@ -155,15 +155,24 @@ onMounted(() => {
event_name: 'app_open', event_name: 'app_open',
properties: { language: locale.value } properties: { language: locale.value }
}) })
// Load favorites if the user is already logged in
if (authStore.isAuthenticated) {
favoritesStore.loadFavorites()
}
document.addEventListener('visibilitychange', handleVisibilityChange) document.addEventListener('visibilitychange', handleVisibilityChange)
window.addEventListener('pageshow', handlePageShow) window.addEventListener('pageshow', handlePageShow)
window.addEventListener('focus', handleWindowFocus) window.addEventListener('focus', handleWindowFocus)
}) })
// Cargar favoritos cuando Supabase confirme la sesión (puede ser después de onMounted)
// immediate:true cubre el caso donde la sesión ya está lista al arrancar
const stopFavWatcher = watch(
() => authStore.isAuthenticated,
(authenticated) => {
if (authenticated && favoritesStore.favorites.length === 0 && !favoritesStore.isLoading) {
favoritesStore.loadFavorites()
stopFavWatcher() // Solo necesitamos cargarlo una vez al inicio
}
},
{ immediate: true }
)
onUnmounted(() => { onUnmounted(() => {
document.removeEventListener('visibilitychange', handleVisibilityChange) document.removeEventListener('visibilitychange', handleVisibilityChange)
window.removeEventListener('pageshow', handlePageShow) window.removeEventListener('pageshow', handlePageShow)

View File

@ -12,7 +12,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, onMounted } from 'vue' import { ref, computed, watch } from 'vue'
import { useFavoritesStore } from '@/stores/favorites' import { useFavoritesStore } from '@/stores/favorites'
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/auth'
@ -31,16 +31,22 @@ const isFavorited = computed(() => {
return favoritesStore.isFavorite(props.itemType, props.itemId) return favoritesStore.isFavorite(props.itemType, props.itemId)
}) })
onMounted(() => { // Observamos isAuthenticated con immediate:true para cubrir 2 casos:
// Load favorites if authenticated and not loaded yet // 1. El usuario ya estaba autenticado cuando el componente montó.
if (authStore.isAuthenticated && favoritesStore.favorites.length === 0) { // 2. Supabase completó INITIAL_SESSION DESPUÉS de que el componente montó
favoritesStore.loadFavorites() // (race condition común al navegar directamente a una URL).
} watch(
}) () => authStore.isAuthenticated,
(authenticated) => {
if (authenticated && favoritesStore.favorites.length === 0 && !favoritesStore.isLoading) {
favoritesStore.loadFavorites()
}
},
{ immediate: true }
)
async function handleToggle() { async function handleToggle() {
if (!authStore.isAuthenticated) { if (!authStore.isAuthenticated) {
// Optionally redirect to login or show message
alert('Debes iniciar sesión para agregar favoritos') alert('Debes iniciar sesión para agregar favoritos')
return return
} }

View File

@ -1,7 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, onMounted } from 'vue' import { ref, computed, watch } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useFavoritesStore } from '@/stores/favorites' import { useFavoritesStore } from '@/stores/favorites'
import { useAuthStore } from '@/stores/auth'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { getImageUrl as utilGetImageUrl } from '@/utils/imageUrl' import { getImageUrl as utilGetImageUrl } from '@/utils/imageUrl'
import LoadingBranded from '@/components/common/LoadingBranded.vue' import LoadingBranded from '@/components/common/LoadingBranded.vue'
@ -9,6 +10,7 @@ import LoadingBranded from '@/components/common/LoadingBranded.vue'
const router = useRouter() const router = useRouter()
const { t } = useI18n() const { t } = useI18n()
const favoritesStore = useFavoritesStore() const favoritesStore = useFavoritesStore()
const authStore = useAuthStore()
const selectedFilter = ref('all') const selectedFilter = ref('all')
const filters = computed(() => [ const filters = computed(() => [
@ -20,9 +22,16 @@ const filters = computed(() => [
{ key: 'stops', label: t('navigation.routes'), icon: 'location_on' }, // Reusing navigation.routes or adding a specific one { key: 'stops', label: t('navigation.routes'), icon: 'location_on' }, // Reusing navigation.routes or adding a specific one
]) ])
onMounted(async () => { // Mismo patrón que FavoriteButton: esperar a que Supabase complete INITIAL_SESSION
await favoritesStore.loadFavorites() watch(
}) () => authStore.isAuthenticated,
(authenticated) => {
if (authenticated) {
favoritesStore.loadFavorites()
}
},
{ immediate: true }
)
function getImageUrl(path?: string) { function getImageUrl(path?: string) {
return utilGetImageUrl(path, 'business') return utilGetImageUrl(path, 'business')