This commit is contained in:
KS Jannette
2026-03-29 08:17:11 -04:00
parent 0412ea3e99
commit 965830fcde
2 changed files with 51 additions and 24 deletions

View File

@@ -1,4 +1,4 @@
import { createContext, useReducer, useContext, useEffect } from "react";
import { createContext, useReducer, useContext, useEffect, useCallback, useRef } from "react";
import authReducer, { initialState, ACTION_TYPES } from "../reducers/authReducer";
import { loginUser, registerAfterCheckout } from "../api/auth";
import { getAccount } from "../api/account";
@@ -7,58 +7,79 @@ const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const [state, dispatch] = useReducer(authReducer, initialState);
const fetchedRef = useRef(false);
// On mount, if we have a token in localStorage, hydrate the user object
// by fetching the account from the backend.
useEffect(() => {
if (!state.token || state.user) return;
getAccount()
.then((account) => {
dispatch({
type: ACTION_TYPES.SET_USER,
payload: {
id: account.user_id,
email: account.email,
subscriptionStatus: account.subscription_status,
subscriptionTier: account.subscription_tier,
},
});
})
.catch(() => {
dispatch({ type: ACTION_TYPES.LOGOUT });
const fetchAccount = useCallback(async () => {
try {
const account = await getAccount();
dispatch({
type: ACTION_TYPES.SET_USER,
payload: {
id: account.user_id,
email: account.email,
subscriptionStatus: account.subscription_status,
subscriptionTier: account.subscription_tier,
tierLimits: account.tier_limits,
addressCount: account.address_count,
},
});
}, [state.token, state.user]);
} catch {
dispatch({ type: ACTION_TYPES.LOGOUT });
}
}, []);
// On mount, if we have a token in localStorage, hydrate user from backend.
useEffect(() => {
if (!state.token || fetchedRef.current) return;
fetchedRef.current = true;
fetchAccount();
}, [state.token, fetchAccount]);
async function login(email, password) {
const data = await loginUser(email, password);
dispatch({ type: ACTION_TYPES.LOGIN_SUCCESS, payload: data });
fetchedRef.current = false;
return data;
}
async function register(email, password, sessionId) {
const data = await registerAfterCheckout(email, password, sessionId);
dispatch({ type: ACTION_TYPES.LOGIN_SUCCESS, payload: data });
fetchedRef.current = false;
return data;
}
function logout() {
fetchedRef.current = false;
dispatch({ type: ACTION_TYPES.LOGOUT });
}
function refreshAccount() {
fetchAccount();
}
const isAuthenticated = state.isAuthenticated && !!state.token;
const isSubscribed =
state.user?.subscriptionStatus === "active" ||
state.user?.subscriptionStatus === "trialing";
const tierLimits = state.user?.tierLimits || {
max_addresses: 1,
max_alert_types: 1,
allowed_channels: ["email"],
};
const value = {
user: state.user,
token: state.token,
isAuthenticated,
isSubscribed,
tierLimits,
userTier: state.user?.subscriptionTier || "free",
login,
register,
logout,
refreshAccount,
dispatch,
ACTION_TYPES,
};

View File

@@ -1,4 +1,4 @@
import { useState, useEffect } from "react";
import { useState, useEffect, useRef } from "react";
import { useNavigate, useSearchParams } from "react-router-dom";
import { useAuth } from "../../contexts/AuthContext";
import { createOnboardingCheckout, activateFreeTier } from "../../api/stripe";
@@ -26,12 +26,17 @@ export default function Subscribe() {
confirmPassword: "",
});
const registerCalledRef = useRef(false);
function set(field, value) {
setData((prev) => ({ ...prev, [field]: value }));
}
// Handle return from Stripe checkout redirect
// Handle return from Stripe checkout redirect.
// The ref guard prevents React StrictMode from double-firing this.
useEffect(() => {
if (registerCalledRef.current) return;
const payment = searchParams.get("payment");
const sessionId = searchParams.get("session_id");
@@ -44,7 +49,6 @@ export default function Subscribe() {
if (payment !== "success" || !sessionId) return;
// Recover credentials stashed before the Stripe redirect
const savedEmail = sessionStorage.getItem("kp_onboard_email");
const savedPassword = sessionStorage.getItem("kp_onboard_password");
@@ -56,6 +60,7 @@ export default function Subscribe() {
return;
}
registerCalledRef.current = true;
setSearchParams({}, { replace: true });
setLoading(true);
@@ -66,6 +71,7 @@ export default function Subscribe() {
navigate("/addresses", { replace: true });
})
.catch((err) => {
registerCalledRef.current = false;
setError("Registration failed: " + err.message);
setStep(1);
})