mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-20 04:28:09 -05:00
Compare commits
1 Commits
fix/undefi
...
fix/accoun
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc85a37305 |
@@ -3,15 +3,16 @@ import {
|
|||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/__legacy__/ui/popover";
|
} from "@/components/__legacy__/ui/popover";
|
||||||
import Link from "next/link";
|
import { Skeleton } from "@/components/__legacy__/ui/skeleton";
|
||||||
import * as React from "react";
|
|
||||||
import { getAccountMenuOptionIcon, MenuItemGroup } from "../../helpers";
|
|
||||||
import { AccountLogoutOption } from "./components/AccountLogoutOption";
|
|
||||||
import { PublishAgentModal } from "@/components/contextual/PublishAgentModal/PublishAgentModal";
|
|
||||||
import Avatar, {
|
import Avatar, {
|
||||||
AvatarFallback,
|
AvatarFallback,
|
||||||
AvatarImage,
|
AvatarImage,
|
||||||
} from "@/components/atoms/Avatar/Avatar";
|
} from "@/components/atoms/Avatar/Avatar";
|
||||||
|
import { PublishAgentModal } from "@/components/contextual/PublishAgentModal/PublishAgentModal";
|
||||||
|
import Link from "next/link";
|
||||||
|
import * as React from "react";
|
||||||
|
import { getAccountMenuOptionIcon, MenuItemGroup } from "../../helpers";
|
||||||
|
import { AccountLogoutOption } from "./components/AccountLogoutOption";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
userName?: string;
|
userName?: string;
|
||||||
@@ -19,6 +20,7 @@ interface Props {
|
|||||||
avatarSrc?: string;
|
avatarSrc?: string;
|
||||||
hideNavBarUsername?: boolean;
|
hideNavBarUsername?: boolean;
|
||||||
menuItemGroups: MenuItemGroup[];
|
menuItemGroups: MenuItemGroup[];
|
||||||
|
isLoading?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AccountMenu({
|
export function AccountMenu({
|
||||||
@@ -26,6 +28,7 @@ export function AccountMenu({
|
|||||||
userEmail,
|
userEmail,
|
||||||
avatarSrc,
|
avatarSrc,
|
||||||
menuItemGroups,
|
menuItemGroups,
|
||||||
|
isLoading = false,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const popupId = React.useId();
|
const popupId = React.useId();
|
||||||
|
|
||||||
@@ -63,15 +66,24 @@ export function AccountMenu({
|
|||||||
</AvatarFallback>
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<div className="relative flex h-[47px] w-[173px] flex-col items-start justify-center gap-1">
|
<div className="relative flex h-[47px] w-[173px] flex-col items-start justify-center gap-1">
|
||||||
<div className="max-w-[10.5rem] truncate font-sans text-base font-semibold leading-none text-white dark:text-neutral-200">
|
{isLoading || !userName || !userEmail ? (
|
||||||
{userName}
|
<>
|
||||||
</div>
|
<Skeleton className="h-4 w-24 bg-white/40" />
|
||||||
<div
|
<Skeleton className="h-4 w-32 bg-white/40" />
|
||||||
data-testid="account-menu-user-email"
|
</>
|
||||||
className="max-w-[10.5rem] truncate font-sans text-base font-normal leading-none text-white dark:text-neutral-400"
|
) : (
|
||||||
>
|
<>
|
||||||
{userEmail}
|
<div className="max-w-[10.5rem] truncate font-sans text-base font-semibold leading-none text-white dark:text-neutral-200">
|
||||||
</div>
|
{userName}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
data-testid="account-menu-user-email"
|
||||||
|
className="max-w-[10.5rem] truncate font-sans text-base font-normal leading-none text-white dark:text-neutral-400"
|
||||||
|
>
|
||||||
|
{userEmail}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ export function AccountLogoutOption() {
|
|||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoggingOut(false);
|
setTimeout(() => {
|
||||||
|
setIsLoggingOut(false);
|
||||||
|
}, 3000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,12 +26,19 @@ export function NavbarView({ isLoggedIn, previewBranchName }: NavbarViewProps) {
|
|||||||
const dynamicMenuItems = getAccountMenuItems(user?.role);
|
const dynamicMenuItems = getAccountMenuItems(user?.role);
|
||||||
const isChatEnabled = useGetFlag(Flag.CHAT);
|
const isChatEnabled = useGetFlag(Flag.CHAT);
|
||||||
|
|
||||||
const { data: profile } = useGetV2GetUserProfile({
|
const { data: profile, isLoading: isProfileLoading } = useGetV2GetUserProfile(
|
||||||
query: {
|
{
|
||||||
select: (res) => (res.status === 200 ? res.data : null),
|
query: {
|
||||||
enabled: isLoggedIn,
|
select: (res) => (res.status === 200 ? res.data : null),
|
||||||
|
enabled: isLoggedIn && !!user,
|
||||||
|
// Include user ID in query key to ensure cache invalidation when user changes
|
||||||
|
queryKey: ["/api/store/profile", user?.id],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
);
|
||||||
|
|
||||||
|
const { isUserLoading } = useSupabase();
|
||||||
|
const isLoadingProfile = isProfileLoading || isUserLoading;
|
||||||
|
|
||||||
const linksWithChat = useMemo(() => {
|
const linksWithChat = useMemo(() => {
|
||||||
const chatLink = { name: "Chat", href: "/chat" };
|
const chatLink = { name: "Chat", href: "/chat" };
|
||||||
@@ -84,6 +91,7 @@ export function NavbarView({ isLoggedIn, previewBranchName }: NavbarViewProps) {
|
|||||||
userEmail={profile?.name}
|
userEmail={profile?.name}
|
||||||
avatarSrc={profile?.avatar_url ?? ""}
|
avatarSrc={profile?.avatar_url ?? ""}
|
||||||
menuItemGroups={dynamicMenuItems}
|
menuItemGroups={dynamicMenuItems}
|
||||||
|
isLoading={isLoadingProfile}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ import {
|
|||||||
RefundRequest,
|
RefundRequest,
|
||||||
TransactionHistory,
|
TransactionHistory,
|
||||||
} from "@/lib/autogpt-server-api/types";
|
} from "@/lib/autogpt-server-api/types";
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
|
||||||
export default function useCredits({
|
export default function useCredits({
|
||||||
fetchInitialCredits = false,
|
fetchInitialCredits = false,
|
||||||
@@ -30,6 +31,7 @@ export default function useCredits({
|
|||||||
fetchRefundRequests: () => void;
|
fetchRefundRequests: () => void;
|
||||||
formatCredits: (credit: number | null) => string;
|
formatCredits: (credit: number | null) => string;
|
||||||
} {
|
} {
|
||||||
|
const { isLoggedIn } = useSupabase();
|
||||||
const [credits, setCredits] = useState<number | null>(null);
|
const [credits, setCredits] = useState<number | null>(null);
|
||||||
const [autoTopUpConfig, setAutoTopUpConfig] = useState<{
|
const [autoTopUpConfig, setAutoTopUpConfig] = useState<{
|
||||||
amount: number;
|
amount: number;
|
||||||
@@ -40,49 +42,90 @@ export default function useCredits({
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const fetchCredits = useCallback(async () => {
|
const fetchCredits = useCallback(async () => {
|
||||||
const response = await api.getUserCredit();
|
if (!isLoggedIn) return;
|
||||||
setCredits(response.credits);
|
try {
|
||||||
}, [api]);
|
const response = await api.getUserCredit();
|
||||||
|
if (response) {
|
||||||
|
setCredits(response.credits);
|
||||||
|
} else {
|
||||||
|
setCredits(null);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching credits:", error);
|
||||||
|
setCredits(null);
|
||||||
|
}
|
||||||
|
}, [api, isLoggedIn]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!fetchInitialCredits) return;
|
if (!fetchInitialCredits || !isLoggedIn) return;
|
||||||
fetchCredits();
|
fetchCredits();
|
||||||
}, [fetchCredits, fetchInitialCredits]);
|
}, [fetchCredits, fetchInitialCredits, isLoggedIn]);
|
||||||
|
|
||||||
|
// Clear credits when user logs out
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
setCredits(null);
|
||||||
|
}
|
||||||
|
}, [isLoggedIn]);
|
||||||
|
|
||||||
const fetchAutoTopUpConfig = useCallback(async () => {
|
const fetchAutoTopUpConfig = useCallback(async () => {
|
||||||
const response = await api.getAutoTopUpConfig();
|
if (!isLoggedIn) return;
|
||||||
setAutoTopUpConfig(response);
|
try {
|
||||||
}, [api]);
|
const response = await api.getAutoTopUpConfig();
|
||||||
|
setAutoTopUpConfig(response || null);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching auto top-up config:", error);
|
||||||
|
setAutoTopUpConfig(null);
|
||||||
|
}
|
||||||
|
}, [api, isLoggedIn]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!fetchInitialAutoTopUpConfig) return;
|
if (!fetchInitialAutoTopUpConfig || !isLoggedIn) return;
|
||||||
fetchAutoTopUpConfig();
|
fetchAutoTopUpConfig();
|
||||||
}, [fetchAutoTopUpConfig, fetchInitialAutoTopUpConfig]);
|
}, [fetchAutoTopUpConfig, fetchInitialAutoTopUpConfig, isLoggedIn]);
|
||||||
|
|
||||||
|
// Clear auto top-up config when user logs out
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
setAutoTopUpConfig(null);
|
||||||
|
}
|
||||||
|
}, [isLoggedIn]);
|
||||||
|
|
||||||
const updateAutoTopUpConfig = useCallback(
|
const updateAutoTopUpConfig = useCallback(
|
||||||
async (amount: number, threshold: number) => {
|
async (amount: number, threshold: number) => {
|
||||||
|
if (!isLoggedIn) return;
|
||||||
await api.setAutoTopUpConfig({ amount, threshold });
|
await api.setAutoTopUpConfig({ amount, threshold });
|
||||||
fetchAutoTopUpConfig();
|
fetchAutoTopUpConfig();
|
||||||
},
|
},
|
||||||
[api, fetchAutoTopUpConfig],
|
[api, fetchAutoTopUpConfig, isLoggedIn],
|
||||||
);
|
);
|
||||||
|
|
||||||
const requestTopUp = useCallback(
|
const requestTopUp = useCallback(
|
||||||
async (credit_amount: number) => {
|
async (credit_amount: number) => {
|
||||||
|
if (!isLoggedIn) return;
|
||||||
const response = await api.requestTopUp(credit_amount);
|
const response = await api.requestTopUp(credit_amount);
|
||||||
router.push(response.checkout_url);
|
router.push(response.checkout_url);
|
||||||
},
|
},
|
||||||
[api, router],
|
[api, router, isLoggedIn],
|
||||||
);
|
);
|
||||||
|
|
||||||
const refundTopUp = useCallback(
|
const refundTopUp = useCallback(
|
||||||
async (transaction_key: string, reason: string) => {
|
async (transaction_key: string, reason: string) => {
|
||||||
const refunded_amount = await api.refundTopUp(transaction_key, reason);
|
if (!isLoggedIn) return 0;
|
||||||
await fetchCredits();
|
try {
|
||||||
setTransactionHistory(await api.getTransactionHistory());
|
const refunded_amount = await api.refundTopUp(transaction_key, reason);
|
||||||
return refunded_amount;
|
await fetchCredits();
|
||||||
|
const history = await api.getTransactionHistory();
|
||||||
|
if (history) {
|
||||||
|
setTransactionHistory(history);
|
||||||
|
}
|
||||||
|
return refunded_amount;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error refunding top-up:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[api, fetchCredits],
|
[api, fetchCredits, isLoggedIn],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [transactionHistory, setTransactionHistory] =
|
const [transactionHistory, setTransactionHistory] =
|
||||||
@@ -92,38 +135,68 @@ export default function useCredits({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const fetchTransactionHistory = useCallback(async () => {
|
const fetchTransactionHistory = useCallback(async () => {
|
||||||
const response = await api.getTransactionHistory(
|
if (!isLoggedIn) return;
|
||||||
transactionHistory.next_transaction_time,
|
try {
|
||||||
20,
|
const response = await api.getTransactionHistory(
|
||||||
);
|
transactionHistory.next_transaction_time,
|
||||||
setTransactionHistory({
|
20,
|
||||||
transactions: [
|
);
|
||||||
...transactionHistory.transactions,
|
if (response) {
|
||||||
...response.transactions,
|
setTransactionHistory({
|
||||||
],
|
transactions: [
|
||||||
next_transaction_time: response.next_transaction_time,
|
...transactionHistory.transactions,
|
||||||
});
|
...response.transactions,
|
||||||
}, [api, transactionHistory]);
|
],
|
||||||
|
next_transaction_time: response.next_transaction_time,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching transaction history:", error);
|
||||||
|
}
|
||||||
|
}, [api, transactionHistory, isLoggedIn]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!fetchInitialTransactionHistory) return;
|
if (!fetchInitialTransactionHistory || !isLoggedIn) return;
|
||||||
fetchTransactionHistory();
|
fetchTransactionHistory();
|
||||||
// Note: We only need to fetch transaction history once.
|
// Note: We only need to fetch transaction history once.
|
||||||
// Hence, we should avoid `fetchTransactionHistory` to the dependency array.
|
// Hence, we should avoid `fetchTransactionHistory` to the dependency array.
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [fetchInitialTransactionHistory]);
|
}, [fetchInitialTransactionHistory, isLoggedIn]);
|
||||||
|
|
||||||
|
// Clear transaction history when user logs out
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
setTransactionHistory({
|
||||||
|
transactions: [],
|
||||||
|
next_transaction_time: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [isLoggedIn]);
|
||||||
|
|
||||||
const [refundRequests, setRefundRequests] = useState<RefundRequest[]>([]);
|
const [refundRequests, setRefundRequests] = useState<RefundRequest[]>([]);
|
||||||
|
|
||||||
const fetchRefundRequests = useCallback(async () => {
|
const fetchRefundRequests = useCallback(async () => {
|
||||||
const response = await api.getRefundRequests();
|
if (!isLoggedIn) return;
|
||||||
setRefundRequests(response);
|
try {
|
||||||
}, [api]);
|
const response = await api.getRefundRequests();
|
||||||
|
setRefundRequests(response || []);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching refund requests:", error);
|
||||||
|
setRefundRequests([]);
|
||||||
|
}
|
||||||
|
}, [api, isLoggedIn]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!fetchInitialRefundRequests) return;
|
if (!fetchInitialRefundRequests || !isLoggedIn) return;
|
||||||
fetchRefundRequests();
|
fetchRefundRequests();
|
||||||
}, [fetchRefundRequests, fetchInitialRefundRequests]);
|
}, [fetchRefundRequests, fetchInitialRefundRequests, isLoggedIn]);
|
||||||
|
|
||||||
|
// Clear refund requests when user logs out
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
setRefundRequests([]);
|
||||||
|
}
|
||||||
|
}, [isLoggedIn]);
|
||||||
|
|
||||||
const formatCredits = useCallback((credit: number | null) => {
|
const formatCredits = useCallback((credit: number | null) => {
|
||||||
if (credit === null) {
|
if (credit === null) {
|
||||||
|
|||||||
@@ -51,9 +51,11 @@ export async function fetchUser(): Promise<FetchUserResult> {
|
|||||||
const { user, error } = await getCurrentUser();
|
const { user, error } = await getCurrentUser();
|
||||||
|
|
||||||
if (error || !user) {
|
if (error || !user) {
|
||||||
|
// Only mark as loaded if we got an explicit error (not just no user)
|
||||||
|
// This allows retrying when cookies aren't ready yet after login
|
||||||
return {
|
return {
|
||||||
user: null,
|
user: null,
|
||||||
hasLoadedUser: true,
|
hasLoadedUser: !!error, // Only true if there was an error, not just no user
|
||||||
isUserLoading: false,
|
isUserLoading: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -68,7 +70,7 @@ export async function fetchUser(): Promise<FetchUserResult> {
|
|||||||
console.error("Get user error:", error);
|
console.error("Get user error:", error);
|
||||||
return {
|
return {
|
||||||
user: null,
|
user: null,
|
||||||
hasLoadedUser: true,
|
hasLoadedUser: true, // Error means we tried and failed, so mark as loaded
|
||||||
isUserLoading: false,
|
isUserLoading: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { create } from "zustand";
|
|||||||
import { serverLogout, type ServerLogoutOptions } from "../actions";
|
import { serverLogout, type ServerLogoutOptions } from "../actions";
|
||||||
import {
|
import {
|
||||||
broadcastLogout,
|
broadcastLogout,
|
||||||
|
isProtectedPage,
|
||||||
setWebSocketDisconnectIntent,
|
setWebSocketDisconnectIntent,
|
||||||
setupSessionEventListeners,
|
setupSessionEventListeners,
|
||||||
} from "../helpers";
|
} from "../helpers";
|
||||||
@@ -77,11 +78,14 @@ export const useSupabaseStore = create<SupabaseStoreState>((set, get) => {
|
|||||||
if (!get().hasLoadedUser || !get().user) {
|
if (!get().hasLoadedUser || !get().user) {
|
||||||
set({ isUserLoading: true });
|
set({ isUserLoading: true });
|
||||||
const result = await fetchUser();
|
const result = await fetchUser();
|
||||||
|
|
||||||
|
// Always update state with fetch result
|
||||||
set(result);
|
set(result);
|
||||||
|
|
||||||
// If fetchUser didn't return a user, validate the session to ensure we have the latest state
|
// If fetchUser didn't return a user, validate the session to ensure we have the latest state
|
||||||
// This handles race conditions after login where cookies might not be immediately available
|
// This handles race conditions after login where cookies might not be immediately available
|
||||||
if (!result.user) {
|
if (!result.user && !result.hasLoadedUser) {
|
||||||
|
// Cookies might not be ready yet, retry validation
|
||||||
const validationResult = await validateSessionHelper({
|
const validationResult = await validateSessionHelper({
|
||||||
pathname: params.pathname,
|
pathname: params.pathname,
|
||||||
currentUser: null,
|
currentUser: null,
|
||||||
@@ -93,10 +97,69 @@ export const useSupabaseStore = create<SupabaseStoreState>((set, get) => {
|
|||||||
hasLoadedUser: true,
|
hasLoadedUser: true,
|
||||||
isUserLoading: false,
|
isUserLoading: false,
|
||||||
});
|
});
|
||||||
|
} else if (!validationResult.isValid) {
|
||||||
|
// Session is invalid, mark as loaded so we don't keep retrying
|
||||||
|
set({
|
||||||
|
hasLoadedUser: true,
|
||||||
|
isUserLoading: false,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Validation succeeded but no user - might be cookies not ready
|
||||||
|
// If we're on a protected page, schedule a retry since we should have a user
|
||||||
|
const isProtected = isProtectedPage(params.pathname);
|
||||||
|
if (isProtected && params.router) {
|
||||||
|
// Retry after a short delay to allow cookies to propagate
|
||||||
|
// Use router.refresh() to trigger a re-initialization
|
||||||
|
setTimeout(() => {
|
||||||
|
const currentState = get();
|
||||||
|
if (
|
||||||
|
!currentState.user &&
|
||||||
|
isProtectedPage(currentState.currentPathname)
|
||||||
|
) {
|
||||||
|
// Trigger router refresh to cause re-initialization
|
||||||
|
params.router.refresh();
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
// Don't mark as loaded yet, allow retry on next initialization
|
||||||
|
set({
|
||||||
|
isUserLoading: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
} else if (!result.user && result.hasLoadedUser) {
|
||||||
|
// Explicit error or already marked as loaded - don't retry
|
||||||
|
set({
|
||||||
|
isUserLoading: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
set({ isUserLoading: false });
|
// Even if we have a user, validate session to catch account switches
|
||||||
|
// This ensures that if user logged out and logged in with different account,
|
||||||
|
// we detect the change immediately
|
||||||
|
const currentUser = get().user;
|
||||||
|
if (currentUser) {
|
||||||
|
const validationResult = await validateSessionHelper({
|
||||||
|
pathname: params.pathname,
|
||||||
|
currentUser,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update user if IDs differ (account switch detected)
|
||||||
|
if (
|
||||||
|
validationResult.user &&
|
||||||
|
validationResult.isValid &&
|
||||||
|
validationResult.user.id !== currentUser.id
|
||||||
|
) {
|
||||||
|
set({
|
||||||
|
user: validationResult.user,
|
||||||
|
hasLoadedUser: true,
|
||||||
|
isUserLoading: false,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
set({ isUserLoading: false });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
set({ isUserLoading: false });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingCleanup = get().listenersCleanup;
|
const existingCleanup = get().listenersCleanup;
|
||||||
@@ -140,10 +203,20 @@ export const useSupabaseStore = create<SupabaseStoreState>((set, get) => {
|
|||||||
|
|
||||||
broadcastLogout();
|
broadcastLogout();
|
||||||
|
|
||||||
|
// Clear React Query cache to prevent stale data from old user
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
const { getQueryClient } = await import("@/lib/react-query/queryClient");
|
||||||
|
const queryClient = getQueryClient();
|
||||||
|
queryClient.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset all state to ensure fresh initialization on next login
|
||||||
set({
|
set({
|
||||||
user: null,
|
user: null,
|
||||||
hasLoadedUser: false,
|
hasLoadedUser: false,
|
||||||
isUserLoading: false,
|
isUserLoading: false,
|
||||||
|
initializationPromise: null, // Force fresh initialization
|
||||||
|
lastValidation: 0, // Reset validation timestamp
|
||||||
});
|
});
|
||||||
|
|
||||||
await serverLogout(options);
|
await serverLogout(options);
|
||||||
@@ -186,15 +259,47 @@ export const useSupabaseStore = create<SupabaseStoreState>((set, get) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.user && result.shouldUpdateUser) {
|
// Always update user if:
|
||||||
set({ user: result.user });
|
// 1. We got a user and current user is null (login scenario)
|
||||||
}
|
// 2. We got a user and IDs differ (account switch scenario)
|
||||||
|
// 3. shouldUpdateUser is true (session validation detected change)
|
||||||
if (result.user) {
|
if (result.user) {
|
||||||
set({
|
const currentUser = get().user;
|
||||||
hasLoadedUser: true,
|
const shouldUpdate =
|
||||||
isUserLoading: false,
|
!currentUser ||
|
||||||
});
|
currentUser.id !== result.user.id ||
|
||||||
|
result.shouldUpdateUser;
|
||||||
|
|
||||||
|
if (shouldUpdate) {
|
||||||
|
// Invalidate profile query when user changes to ensure fresh data
|
||||||
|
if (
|
||||||
|
typeof window !== "undefined" &&
|
||||||
|
currentUser?.id !== result.user.id
|
||||||
|
) {
|
||||||
|
const { getQueryClient } = await import(
|
||||||
|
"@/lib/react-query/queryClient"
|
||||||
|
);
|
||||||
|
const { getGetV2GetUserProfileQueryKey } = await import(
|
||||||
|
"@/app/api/__generated__/endpoints/store/store"
|
||||||
|
);
|
||||||
|
const queryClient = getQueryClient();
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: getGetV2GetUserProfileQueryKey(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
set({
|
||||||
|
user: result.user,
|
||||||
|
hasLoadedUser: true,
|
||||||
|
isUserLoading: false,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Even if user didn't change, ensure loading state is cleared
|
||||||
|
set({
|
||||||
|
hasLoadedUser: true,
|
||||||
|
isUserLoading: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
Reference in New Issue
Block a user