Correcao Sidebar Classico e Rail, Correcao Layout, Ajuste de Breakpoint para Tailwind, Ajuste AppTopbar, Ajuste Menu PopOver, Recriado Paleta de Cores, Inserido algumas animações leves, Reajuste Cor items NOVOS da tabela, Drawer Ajuda Corrigido no Logout, Whatsapp, sms, email, recursos extras
This commit is contained in:
Vendored
+114
-108
@@ -14,135 +14,141 @@
|
||||
| © 2026 — Todos os direitos reservados
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
import { supabase } from '@/lib/supabase/client'
|
||||
import { useLayout } from '@/layout/composables/layout'
|
||||
import { $t, updatePreset, updateSurfacePalette } from '@primeuix/themes'
|
||||
import Aura from '@primeuix/themes/aura'
|
||||
import Lara from '@primeuix/themes/lara'
|
||||
import Nora from '@primeuix/themes/nora'
|
||||
import { supabase } from '@/lib/supabase/client';
|
||||
import { useLayout } from '@/layout/composables/layout';
|
||||
import { $t, updatePreset, updateSurfacePalette } from '@primeuix/themes';
|
||||
import Aura from '@primeuix/themes/aura';
|
||||
import Lara from '@primeuix/themes/lara';
|
||||
import Nora from '@primeuix/themes/nora';
|
||||
|
||||
const presets = { Aura, Lara, Nora }
|
||||
const presets = { Aura, Lara, Nora };
|
||||
|
||||
function safeEq (a, b) {
|
||||
return String(a || '').trim() === String(b || '').trim()
|
||||
function safeEq(a, b) {
|
||||
return String(a || '').trim() === String(b || '').trim();
|
||||
}
|
||||
|
||||
// copia do seu getPresetExt (ou exporta ele do Perfil pra reutilizar)
|
||||
function getPresetExt(primaryColors, layoutConfig) {
|
||||
const color = primaryColors.find((c) => c.name === layoutConfig.primary) || { name: 'noir', palette: {} }
|
||||
const color = primaryColors.find((c) => c.name === layoutConfig.primary) || { name: 'noir', palette: {} };
|
||||
|
||||
if (color.name === 'noir') {
|
||||
return {
|
||||
semantic: {
|
||||
primary: {
|
||||
50: '{surface.50}',
|
||||
100: '{surface.100}',
|
||||
200: '{surface.200}',
|
||||
300: '{surface.300}',
|
||||
400: '{surface.400}',
|
||||
500: '{surface.500}',
|
||||
600: '{surface.600}',
|
||||
700: '{surface.700}',
|
||||
800: '{surface.800}',
|
||||
900: '{surface.900}',
|
||||
950: '{surface.950}'
|
||||
},
|
||||
colorScheme: {
|
||||
light: {
|
||||
primary: { color: '{primary.950}', contrastColor: '#ffffff', hoverColor: '{primary.800}', activeColor: '{primary.700}' },
|
||||
highlight: { background: '{primary.950}', focusBackground: '{primary.700}', color: '#ffffff', focusColor: '#ffffff' }
|
||||
},
|
||||
dark: {
|
||||
primary: { color: '{primary.50}', contrastColor: '{primary.950}', hoverColor: '{primary.200}', activeColor: '{primary.300}' },
|
||||
highlight: { background: '{primary.50}', focusBackground: '{primary.300}', color: '{primary.950}', focusColor: '{primary.950}' }
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (color.name === 'noir') {
|
||||
return {
|
||||
semantic: {
|
||||
primary: {
|
||||
50: '{surface.50}', 100: '{surface.100}', 200: '{surface.200}', 300: '{surface.300}',
|
||||
400: '{surface.400}', 500: '{surface.500}', 600: '{surface.600}', 700: '{surface.700}',
|
||||
800: '{surface.800}', 900: '{surface.900}', 950: '{surface.950}'
|
||||
},
|
||||
colorScheme: {
|
||||
light: {
|
||||
primary: { color: '{primary.950}', contrastColor: '#ffffff', hoverColor: '{primary.800}', activeColor: '{primary.700}' },
|
||||
highlight: { background: '{primary.950}', focusBackground: '{primary.700}', color: '#ffffff', focusColor: '#ffffff' }
|
||||
},
|
||||
dark: {
|
||||
primary: { color: '{primary.50}', contrastColor: '{primary.950}', hoverColor: '{primary.200}', activeColor: '{primary.300}' },
|
||||
highlight: { background: '{primary.50}', focusBackground: '{primary.300}', color: '{primary.950}', focusColor: '{primary.950}' }
|
||||
}
|
||||
semantic: {
|
||||
primary: color.palette,
|
||||
colorScheme: {
|
||||
light: {
|
||||
primary: { color: '{primary.500}', contrastColor: '#ffffff', hoverColor: '{primary.600}', activeColor: '{primary.700}' },
|
||||
highlight: { background: '{primary.50}', focusBackground: '{primary.100}', color: '{primary.700}', focusColor: '{primary.800}' }
|
||||
},
|
||||
dark: {
|
||||
primary: { color: '{primary.400}', contrastColor: '{surface.900}', hoverColor: '{primary.300}', activeColor: '{primary.200}' },
|
||||
highlight: {
|
||||
background: 'color-mix(in srgb, {primary.400}, transparent 84%)',
|
||||
focusBackground: 'color-mix(in srgb, {primary.400}, transparent 76%)',
|
||||
color: 'rgba(255,255,255,.87)',
|
||||
focusColor: 'rgba(255,255,255,.87)'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
semantic: {
|
||||
primary: color.palette,
|
||||
colorScheme: {
|
||||
light: {
|
||||
primary: { color: '{primary.500}', contrastColor: '#ffffff', hoverColor: '{primary.600}', activeColor: '{primary.700}' },
|
||||
highlight: { background: '{primary.50}', focusBackground: '{primary.100}', color: '{primary.700}', focusColor: '{primary.800}' }
|
||||
},
|
||||
dark: {
|
||||
primary: { color: '{primary.400}', contrastColor: '{surface.900}', hoverColor: '{primary.300}', activeColor: '{primary.200}' },
|
||||
highlight: {
|
||||
background: 'color-mix(in srgb, {primary.400}, transparent 84%)',
|
||||
focusBackground: 'color-mix(in srgb, {primary.400}, transparent 76%)',
|
||||
color: 'rgba(255,255,255,.87)',
|
||||
focusColor: 'rgba(255,255,255,.87)'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export async function bootstrapUserSettings({
|
||||
primaryColors = [], // passe a lista do seu Perfil (ou uma versão reduzida)
|
||||
surfaces = [] // idem
|
||||
primaryColors = [], // passe a lista do seu Perfil (ou uma versão reduzida)
|
||||
surfaces = [] // idem
|
||||
} = {}) {
|
||||
const { layoutConfig, isDarkTheme, toggleDarkMode, changeMenuMode, setVariant } = useLayout()
|
||||
const { layoutConfig, isDarkTheme, toggleDarkMode, changeMenuMode, setVariant } = useLayout();
|
||||
|
||||
const { data: uRes, error: uErr } = await supabase.auth.getUser()
|
||||
if (uErr) return
|
||||
const user = uRes?.user
|
||||
if (!user) return
|
||||
const { data: uRes, error: uErr } = await supabase.auth.getUser();
|
||||
if (uErr) return;
|
||||
const user = uRes?.user;
|
||||
if (!user) return;
|
||||
|
||||
const { data: settings, error } = await supabase
|
||||
.from('user_settings')
|
||||
.select('theme_mode, preset, primary_color, surface_color, menu_mode, layout_variant')
|
||||
.eq('user_id', user.id)
|
||||
.maybeSingle()
|
||||
const { data: settings, error } = await supabase.from('user_settings').select('theme_mode, preset, primary_color, surface_color, menu_mode, layout_variant').eq('user_id', user.id).maybeSingle();
|
||||
|
||||
if (error || !settings) return
|
||||
if (error || !settings) return;
|
||||
|
||||
// layout variant: respeita a preferência já gravada no localStorage.
|
||||
// Se localStorage está vazio, só aplica 'rail' do banco (confirma o padrão).
|
||||
// Nunca aplica 'classic' automaticamente quando não há preferência local —
|
||||
// dado antigo no banco não deve sobrescrever o padrão do app.
|
||||
const _lsV = (() => {
|
||||
try {
|
||||
const v = localStorage.getItem('layout_variant')
|
||||
return (v === 'rail' || v === 'classic') ? v : null
|
||||
} catch { return null }
|
||||
})()
|
||||
// layout variant: respeita a preferência já gravada no localStorage.
|
||||
// Se localStorage está vazio, só aplica 'rail' do banco (confirma o padrão).
|
||||
// Nunca aplica 'classic' automaticamente quando não há preferência local —
|
||||
// dado antigo no banco não deve sobrescrever o padrão do app.
|
||||
const _lsV = (() => {
|
||||
try {
|
||||
const v = localStorage.getItem('layout_variant');
|
||||
return v === 'rail' || v === 'classic' ? v : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
|
||||
if (_lsV !== null) {
|
||||
// localStorage já tem valor → aplica ele (garante coerência com layoutConfig)
|
||||
if (_lsV !== (settings.layout_variant ?? _lsV)) setVariant(_lsV)
|
||||
} else if (settings.layout_variant === 'rail') {
|
||||
// localStorage vazio + banco tem 'rail' → aplica e grava no localStorage
|
||||
setVariant('rail')
|
||||
}
|
||||
// localStorage vazio + banco tem 'classic' → mantém padrão 'rail' (não aplica)
|
||||
if (_lsV !== null) {
|
||||
// localStorage já tem valor → aplica ele (garante coerência com layoutConfig)
|
||||
if (_lsV !== (settings.layout_variant ?? _lsV)) setVariant(_lsV);
|
||||
} else if (settings.layout_variant === 'rail') {
|
||||
// localStorage vazio + banco tem 'rail' → aplica e grava no localStorage
|
||||
setVariant('rail');
|
||||
}
|
||||
// localStorage vazio + banco tem 'classic' → mantém padrão 'rail' (não aplica)
|
||||
|
||||
// menu mode
|
||||
if (settings.menu_mode && settings.menu_mode !== layoutConfig.menuMode) {
|
||||
layoutConfig.menuMode = settings.menu_mode
|
||||
changeMenuMode()
|
||||
}
|
||||
// menu mode
|
||||
if (settings.menu_mode && settings.menu_mode !== layoutConfig.menuMode) {
|
||||
layoutConfig.menuMode = settings.menu_mode;
|
||||
changeMenuMode();
|
||||
}
|
||||
|
||||
// preset
|
||||
if (settings.preset && settings.preset !== layoutConfig.preset) {
|
||||
layoutConfig.preset = settings.preset
|
||||
const presetValue = presets[settings.preset] || presets.Aura
|
||||
const surfacePalette = surfaces.find(s => s.name === layoutConfig.surface)?.palette
|
||||
$t().preset(presetValue).preset(getPresetExt(primaryColors, layoutConfig)).surfacePalette(surfacePalette).use({ useDefaultOptions: true })
|
||||
}
|
||||
// preset
|
||||
if (settings.preset && settings.preset !== layoutConfig.preset) {
|
||||
layoutConfig.preset = settings.preset;
|
||||
const presetValue = presets[settings.preset] || presets.Aura;
|
||||
const surfacePalette = surfaces.find((s) => s.name === layoutConfig.surface)?.palette;
|
||||
$t().preset(presetValue).preset(getPresetExt(primaryColors, layoutConfig)).surfacePalette(surfacePalette).use({ useDefaultOptions: true });
|
||||
}
|
||||
|
||||
// colors
|
||||
if (settings.primary_color && !safeEq(settings.primary_color, layoutConfig.primary)) {
|
||||
layoutConfig.primary = settings.primary_color
|
||||
updatePreset(getPresetExt(primaryColors, layoutConfig))
|
||||
}
|
||||
// colors
|
||||
if (settings.primary_color && !safeEq(settings.primary_color, layoutConfig.primary)) {
|
||||
layoutConfig.primary = settings.primary_color;
|
||||
updatePreset(getPresetExt(primaryColors, layoutConfig));
|
||||
}
|
||||
|
||||
if (settings.surface_color && !safeEq(settings.surface_color, layoutConfig.surface)) {
|
||||
layoutConfig.surface = settings.surface_color
|
||||
const surface = surfaces.find(s => s.name === settings.surface_color)
|
||||
if (surface) updateSurfacePalette(surface.palette)
|
||||
}
|
||||
if (settings.surface_color && !safeEq(settings.surface_color, layoutConfig.surface)) {
|
||||
layoutConfig.surface = settings.surface_color;
|
||||
const surface = surfaces.find((s) => s.name === settings.surface_color);
|
||||
if (surface) updateSurfacePalette(surface.palette);
|
||||
}
|
||||
|
||||
// dark/light
|
||||
if (settings.theme_mode) {
|
||||
const shouldBeDark = settings.theme_mode === 'dark'
|
||||
if (shouldBeDark !== isDarkTheme) toggleDarkMode()
|
||||
}
|
||||
// dark/light
|
||||
if (settings.theme_mode) {
|
||||
const shouldBeDark = settings.theme_mode === 'dark';
|
||||
if (shouldBeDark !== isDarkTheme) toggleDarkMode();
|
||||
}
|
||||
}
|
||||
|
||||
+133
-141
@@ -14,8 +14,8 @@
|
||||
| © 2026 — Todos os direitos reservados
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
import { ref } from 'vue'
|
||||
import { supabase } from '@/lib/supabase/client'
|
||||
import { ref } from 'vue';
|
||||
import { supabase } from '@/lib/supabase/client';
|
||||
|
||||
/**
|
||||
* ⚠️ IMPORTANTE — ESTABILIDADE DE NAVEGAÇÃO
|
||||
@@ -42,197 +42,189 @@ import { supabase } from '@/lib/supabase/client'
|
||||
* entre sessão, guard e carregamento de tenant/entitlements.
|
||||
*/
|
||||
|
||||
export const sessionUser = ref(null)
|
||||
export const sessionRole = ref(null)
|
||||
export const sessionIsSaasAdmin = ref(false)
|
||||
export const sessionUser = ref(null);
|
||||
export const sessionRole = ref(null);
|
||||
export const sessionIsSaasAdmin = ref(false);
|
||||
|
||||
// só no primeiro boot
|
||||
export const sessionReady = ref(false)
|
||||
export const sessionReady = ref(false);
|
||||
|
||||
// refresh leve (troca de aba / refresh token) sem desmontar UI
|
||||
export const sessionRefreshing = ref(false)
|
||||
export const sessionRefreshing = ref(false);
|
||||
|
||||
let onSignedOutCallback = null
|
||||
export function setOnSignedOut (cb) {
|
||||
onSignedOutCallback = typeof cb === 'function' ? cb : null
|
||||
let onSignedOutCallback = null;
|
||||
export function setOnSignedOut(cb) {
|
||||
onSignedOutCallback = typeof cb === 'function' ? cb : null;
|
||||
}
|
||||
|
||||
// evita init concorrente
|
||||
let initPromise = null
|
||||
let initPromise = null;
|
||||
|
||||
async function fetchRole (userId) {
|
||||
const { data, error } = await supabase
|
||||
.from('profiles')
|
||||
.select('role')
|
||||
.eq('id', userId)
|
||||
.single()
|
||||
async function fetchRole(userId) {
|
||||
const { data, error } = await supabase.from('profiles').select('role').eq('id', userId).single();
|
||||
|
||||
if (error) return null
|
||||
return data?.role || null
|
||||
if (error) return null;
|
||||
return data?.role || null;
|
||||
}
|
||||
|
||||
async function fetchIsSaasAdmin (userId) {
|
||||
const { data, error } = await supabase
|
||||
.from('saas_admins')
|
||||
.select('user_id')
|
||||
.eq('user_id', userId)
|
||||
.maybeSingle()
|
||||
async function fetchIsSaasAdmin(userId) {
|
||||
const { data, error } = await supabase.from('saas_admins').select('user_id').eq('user_id', userId).maybeSingle();
|
||||
|
||||
if (error) return false
|
||||
return !!data
|
||||
if (error) return false;
|
||||
return !!data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Atualiza estado a partir de uma session "confiável" (getSession() ou callback do auth).
|
||||
* ⚠️ NÃO zera user/role durante refresh enquanto existir sessão.
|
||||
*/
|
||||
async function hydrateFromSession (sess) {
|
||||
const user = sess?.user || null
|
||||
if (!user?.id) return false
|
||||
async function hydrateFromSession(sess) {
|
||||
const user = sess?.user || null;
|
||||
if (!user?.id) return false;
|
||||
|
||||
const prevUid = sessionUser.value?.id || null
|
||||
const uid = user.id
|
||||
const prevUid = sessionUser.value?.id || null;
|
||||
const uid = user.id;
|
||||
|
||||
// ✅ pega primeiro hydrate e troca de usuário
|
||||
const userChanged = prevUid !== uid
|
||||
// ✅ pega primeiro hydrate e troca de usuário
|
||||
const userChanged = prevUid !== uid;
|
||||
|
||||
// atualiza user imediatamente (sem flicker)
|
||||
sessionUser.value = user
|
||||
// atualiza user imediatamente (sem flicker)
|
||||
sessionUser.value = user;
|
||||
|
||||
// ✅ saas admin: calcula no primeiro hydrate e sempre que trocar de user
|
||||
// (no primeiro hydrate prevUid é null, então userChanged = true)
|
||||
if (userChanged) {
|
||||
sessionIsSaasAdmin.value = await fetchIsSaasAdmin(uid)
|
||||
}
|
||||
// ✅ saas admin: calcula no primeiro hydrate e sempre que trocar de user
|
||||
// (no primeiro hydrate prevUid é null, então userChanged = true)
|
||||
if (userChanged) {
|
||||
sessionIsSaasAdmin.value = await fetchIsSaasAdmin(uid);
|
||||
}
|
||||
|
||||
// role: busca se não tem, ou se mudou user
|
||||
if (!sessionRole.value || userChanged) {
|
||||
sessionRole.value = await fetchRole(uid)
|
||||
}
|
||||
// role: busca se não tem, ou se mudou user
|
||||
if (!sessionRole.value || userChanged) {
|
||||
sessionRole.value = await fetchRole(uid);
|
||||
}
|
||||
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Boot inicial (pode bloquear UI) ou refresh (não pode derrubar menu).
|
||||
*/
|
||||
export async function initSession ({ initial = false } = {}) {
|
||||
if (initPromise) return initPromise
|
||||
export async function initSession({ initial = false } = {}) {
|
||||
if (initPromise) return initPromise;
|
||||
|
||||
if (initial) sessionReady.value = false
|
||||
else sessionRefreshing.value = true
|
||||
if (initial) sessionReady.value = false;
|
||||
else sessionRefreshing.value = true;
|
||||
|
||||
initPromise = (async () => {
|
||||
try {
|
||||
const { data, error } = await supabase.auth.getSession();
|
||||
if (error) throw error;
|
||||
|
||||
const sess = data?.session || null;
|
||||
const ok = await hydrateFromSession(sess);
|
||||
|
||||
// se não tem sessão, zera estado (aqui pode, porque é init/refresh controlado)
|
||||
if (!ok) {
|
||||
sessionUser.value = null;
|
||||
sessionRole.value = null;
|
||||
sessionIsSaasAdmin.value = false;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[initSession] getSession falhou (tratando como sem sessão):', e);
|
||||
// não deixa estourar pro router guard
|
||||
sessionUser.value = null;
|
||||
sessionRole.value = null;
|
||||
sessionIsSaasAdmin.value = false;
|
||||
}
|
||||
})();
|
||||
|
||||
initPromise = (async () => {
|
||||
try {
|
||||
const { data, error } = await supabase.auth.getSession()
|
||||
if (error) throw error
|
||||
|
||||
const sess = data?.session || null
|
||||
const ok = await hydrateFromSession(sess)
|
||||
|
||||
// se não tem sessão, zera estado (aqui pode, porque é init/refresh controlado)
|
||||
if (!ok) {
|
||||
sessionUser.value = null
|
||||
sessionRole.value = null
|
||||
sessionIsSaasAdmin.value = false
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[initSession] getSession falhou (tratando como sem sessão):', e)
|
||||
// não deixa estourar pro router guard
|
||||
sessionUser.value = null
|
||||
sessionRole.value = null
|
||||
sessionIsSaasAdmin.value = false
|
||||
await initPromise;
|
||||
} finally {
|
||||
initPromise = null;
|
||||
if (initial) sessionReady.value = true;
|
||||
sessionRefreshing.value = false;
|
||||
}
|
||||
})()
|
||||
|
||||
try {
|
||||
await initPromise
|
||||
} finally {
|
||||
initPromise = null
|
||||
if (initial) sessionReady.value = true
|
||||
sessionRefreshing.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// refresh leve (troca de aba etc.)
|
||||
export async function refreshSession () {
|
||||
// ✅ evita corrida: se já está refreshing/init, não dispara outro
|
||||
if (sessionRefreshing.value || initPromise) return
|
||||
export async function refreshSession() {
|
||||
// ✅ evita corrida: se já está refreshing/init, não dispara outro
|
||||
if (sessionRefreshing.value || initPromise) return;
|
||||
|
||||
const { data, error } = await supabase.auth.getSession()
|
||||
if (error) return
|
||||
const { data, error } = await supabase.auth.getSession();
|
||||
if (error) return;
|
||||
|
||||
const sess = data?.session || null
|
||||
const uid = sess?.user?.id || null
|
||||
const sess = data?.session || null;
|
||||
const uid = sess?.user?.id || null;
|
||||
|
||||
// se não tem sessão, não zera aqui (deixa SIGNED_OUT cuidar)
|
||||
if (!uid) return
|
||||
// se não tem sessão, não zera aqui (deixa SIGNED_OUT cuidar)
|
||||
if (!uid) return;
|
||||
|
||||
// se já está consistente, não faz nada
|
||||
if (sessionUser.value?.id === uid && sessionRole.value) return
|
||||
// se já está consistente, não faz nada
|
||||
if (sessionUser.value?.id === uid && sessionRole.value) return;
|
||||
|
||||
await initSession({ initial: false })
|
||||
await initSession({ initial: false });
|
||||
}
|
||||
|
||||
// evita múltiplos listeners
|
||||
let authSubscription = null
|
||||
let authSubscription = null;
|
||||
|
||||
export function listenAuthChanges () {
|
||||
if (authSubscription) return
|
||||
export function listenAuthChanges() {
|
||||
if (authSubscription) return;
|
||||
|
||||
const { data } = supabase.auth.onAuthStateChange(async (event, sess) => {
|
||||
console.log('[AUTH EVENT]', event)
|
||||
const { data } = supabase.auth.onAuthStateChange(async (event, sess) => {
|
||||
console.log('[AUTH EVENT]', event);
|
||||
|
||||
// ✅ SIGNED_OUT: zera e chama callback
|
||||
if (event === 'SIGNED_OUT') {
|
||||
sessionUser.value = null
|
||||
sessionRole.value = null
|
||||
sessionIsSaasAdmin.value = false
|
||||
sessionRefreshing.value = false
|
||||
sessionReady.value = true
|
||||
if (onSignedOutCallback) onSignedOutCallback()
|
||||
return
|
||||
}
|
||||
// ✅ SIGNED_OUT: zera e chama callback
|
||||
if (event === 'SIGNED_OUT') {
|
||||
sessionUser.value = null;
|
||||
sessionRole.value = null;
|
||||
sessionIsSaasAdmin.value = false;
|
||||
sessionRefreshing.value = false;
|
||||
sessionReady.value = true;
|
||||
if (onSignedOutCallback) onSignedOutCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ se já está consistente, ignora SIGNED_IN redundante
|
||||
if (event === 'SIGNED_IN') {
|
||||
const uid = sess?.user?.id || null
|
||||
if (uid && sessionReady.value && sessionUser.value?.id === uid && sessionRole.value) {
|
||||
return
|
||||
}
|
||||
}
|
||||
// ✅ se já está consistente, ignora SIGNED_IN redundante
|
||||
if (event === 'SIGNED_IN') {
|
||||
const uid = sess?.user?.id || null;
|
||||
if (uid && sessionReady.value && sessionUser.value?.id === uid && sessionRole.value) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ use a session fornecida no callback
|
||||
if (sess?.user?.id) {
|
||||
// evita reentrância
|
||||
if (sessionRefreshing.value) return
|
||||
// ✅ use a session fornecida no callback
|
||||
if (sess?.user?.id) {
|
||||
// evita reentrância
|
||||
if (sessionRefreshing.value) return;
|
||||
|
||||
sessionRefreshing.value = true
|
||||
try {
|
||||
await hydrateFromSession(sess)
|
||||
sessionReady.value = true
|
||||
} catch (e) {
|
||||
console.warn('[auth hydrate error]', e)
|
||||
} finally {
|
||||
sessionRefreshing.value = false
|
||||
}
|
||||
return
|
||||
}
|
||||
sessionRefreshing.value = true;
|
||||
try {
|
||||
await hydrateFromSession(sess);
|
||||
sessionReady.value = true;
|
||||
} catch (e) {
|
||||
console.warn('[auth hydrate error]', e);
|
||||
} finally {
|
||||
sessionRefreshing.value = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// fallback: refresh leve
|
||||
try {
|
||||
await refreshSession()
|
||||
} catch (e) {
|
||||
console.error('[refreshSession error]', e)
|
||||
}
|
||||
})
|
||||
// fallback: refresh leve
|
||||
try {
|
||||
await refreshSession();
|
||||
} catch (e) {
|
||||
console.error('[refreshSession error]', e);
|
||||
}
|
||||
});
|
||||
|
||||
authSubscription = data?.subscription || null
|
||||
authSubscription = data?.subscription || null;
|
||||
}
|
||||
|
||||
export function stopAuthChanges () {
|
||||
if (authSubscription) {
|
||||
authSubscription.unsubscribe()
|
||||
authSubscription = null
|
||||
}
|
||||
}
|
||||
export function stopAuthChanges() {
|
||||
if (authSubscription) {
|
||||
authSubscription.unsubscribe();
|
||||
authSubscription = null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user