diff --git a/packages/sdk/src/app.shared.ts b/packages/sdk/src/app.shared.ts index 712017e3..ab6ed1a0 100644 --- a/packages/sdk/src/app.shared.ts +++ b/packages/sdk/src/app.shared.ts @@ -2,12 +2,11 @@ import { ClientApplication } from "@b3dotfun/b3-api"; import { AuthenticationClient } from "@feathersjs/authentication-client"; import Cookies from "js-cookie"; import { B3_AUTH_COOKIE_NAME } from "./shared/constants"; +import { getSessionDurationDays } from "./shared/utils/session-duration"; export const B3_API_URL = process.env.EXPO_PUBLIC_B3_API || process.env.NEXT_PUBLIC_B3_API || process.env.PUBLIC_B3_API || "https://api.b3.fun"; -const DEV_USER_GROUP = 4; - export const authenticate = async ( app: ClientApplication, accessToken: string, @@ -33,12 +32,14 @@ export const authenticate = async ( }, ); - // Extend cookie expiration to 30 days for dev users - if (response?.user?.userGroups?.includes(DEV_USER_GROUP)) { - const token = Cookies.get(B3_AUTH_COOKIE_NAME); - if (token) { - Cookies.set(B3_AUTH_COOKIE_NAME, token, { expires: 30 }); - } + const token = Cookies.get(B3_AUTH_COOKIE_NAME); + if (token) { + const days = getSessionDurationDays(response?.user?.preferences, params?.partnerId); + Cookies.set(B3_AUTH_COOKIE_NAME, token, { + ...(days > 0 ? { expires: days } : {}), + secure: true, + sameSite: "Lax", + }); } return response; diff --git a/packages/sdk/src/global-account/react/components/B3DynamicModal.tsx b/packages/sdk/src/global-account/react/components/B3DynamicModal.tsx index 5f671f88..4753b3d9 100644 --- a/packages/sdk/src/global-account/react/components/B3DynamicModal.tsx +++ b/packages/sdk/src/global-account/react/components/B3DynamicModal.tsx @@ -27,6 +27,7 @@ import { LinkAccount } from "./LinkAccount/LinkAccount"; import { LinkNewAccount } from "./LinkAccount/LinkNewAccount"; import { ManageAccount } from "./ManageAccount/ManageAccount"; import NotificationsContent from "./ManageAccount/NotificationsContent"; +import SessionDurationContent from "./ManageAccount/SessionDurationContent"; import { RequestPermissions } from "./RequestPermissions/RequestPermissions"; import { Send } from "./Send/Send"; import { SignInWithB3Flow } from "./SignInWithB3/SignInWithB3Flow"; @@ -172,6 +173,8 @@ export function B3DynamicModal() { return ; case "notifications": return ; + case "sessionDuration": + return ; // Add other modal types here default: return null; diff --git a/packages/sdk/src/global-account/react/components/ManageAccount/SessionDurationContent.tsx b/packages/sdk/src/global-account/react/components/ManageAccount/SessionDurationContent.tsx new file mode 100644 index 00000000..8a3a6fe3 --- /dev/null +++ b/packages/sdk/src/global-account/react/components/ManageAccount/SessionDurationContent.tsx @@ -0,0 +1,107 @@ +import app from "@b3dotfun/sdk/global-account/app"; +import { useAuthentication, useModalStore } from "@b3dotfun/sdk/global-account/react"; +import { + getSessionDurationDays, + SESSION_DURATION_LABELS, + SESSION_DURATION_OPTIONS, + SessionDurationDays, + setSessionDurationDays, +} from "@b3dotfun/sdk/shared/utils/session-duration"; +import { useState } from "react"; +import ModalHeader from "../ModalHeader/ModalHeader"; + +interface SessionDurationContentProps { + partnerId: string; +} + +const DESCRIPTIONS: Record = { + 0: "Sign out when browser closes", + 1: "Stay signed in for 1 day", + 7: "Stay signed in for 7 days", + 14: "Stay signed in for 2 weeks", + 30: "Stay signed in for 30 days", +}; + +const SessionDurationContent = ({ partnerId }: SessionDurationContentProps) => { + const { user, setUser } = useAuthentication(partnerId); + const navigateBack = useModalStore(state => state.navigateBack); + const [sessionDays, setSessionDays] = useState(() => + getSessionDurationDays(user?.preferences, partnerId), + ); + const [saving, setSaving] = useState(false); + + const handleSelect = async (days: SessionDurationDays) => { + const previous = sessionDays; + setSessionDurationDays(days, partnerId); + setSessionDays(days); + if (user?.userId) { + setSaving(true); + try { + const updated = await app.service("users").patch(user.userId, { + preferences: { + ...user.preferences, + [partnerId]: { + ...((((user.preferences as Record) ?? {})[partnerId] as Record) ?? {}), + sessionDuration: days, + }, + }, + }); + setUser(updated); + } catch (error) { + console.error("Failed to save session duration preference:", error); + // Revert optimistic update so UI stays consistent with server state + setSessionDays(previous); + setSessionDurationDays(previous, partnerId); + } finally { + setSaving(false); + } + } + }; + + return ( +
+ + +
+ {SESSION_DURATION_OPTIONS.map(days => ( + + ))} +
+
+ ); +}; + +export default SessionDurationContent; diff --git a/packages/sdk/src/global-account/react/components/ManageAccount/SettingsContent.tsx b/packages/sdk/src/global-account/react/components/ManageAccount/SettingsContent.tsx index 028b1dda..c2cff14f 100644 --- a/packages/sdk/src/global-account/react/components/ManageAccount/SettingsContent.tsx +++ b/packages/sdk/src/global-account/react/components/ManageAccount/SettingsContent.tsx @@ -1,5 +1,6 @@ import { useAuthentication, useModalStore } from "@b3dotfun/sdk/global-account/react"; import { client } from "@b3dotfun/sdk/shared/utils/thirdweb"; +import { getSessionDurationDays, SESSION_DURATION_LABELS } from "@b3dotfun/sdk/shared/utils/session-duration"; import { Loader2 } from "lucide-react"; import { useState } from "react"; import { Chain } from "thirdweb"; @@ -20,46 +21,29 @@ const SettingsContent = ({ }) => { const setB3ModalContentType = useModalStore(state => state.setB3ModalContentType); const setB3ModalOpen = useModalStore(state => state.setB3ModalOpen); - const { logout } = useAuthentication(partnerId); + const { logout, user } = useAuthentication(partnerId); const [logoutLoading, setLogoutLoading] = useState(false); - const { data: profilesRaw = [] } = useProfiles({ client }); + const sessionDays = getSessionDurationDays(user?.preferences, partnerId); + const { data: profilesRaw = [] } = useProfiles({ client }); const profiles = profilesRaw.filter((profile: any) => !["custom_auth_endpoint"].includes(profile.type)); - const handleNavigate = (type: "home" | "swap" | "linkAccount" | "avatarEditor" | "notifications") => { + const handleNavigate = ( + type: "home" | "swap" | "linkAccount" | "avatarEditor" | "notifications" | "sessionDuration", + ) => { if (type === "home") { - setB3ModalContentType({ - type: "manageAccount", - chain, - partnerId, - onLogout, - activeTab: "home", - }); + setB3ModalContentType({ type: "manageAccount", chain, partnerId, onLogout, activeTab: "home" }); } else if (type === "swap") { - setB3ModalContentType({ - type: "manageAccount", - chain, - partnerId, - onLogout, - activeTab: "tokens", - }); + setB3ModalContentType({ type: "manageAccount", chain, partnerId, onLogout, activeTab: "tokens" }); } else if (type === "linkAccount") { - setB3ModalContentType({ - type: "linkAccount", - chain, - partnerId, - }); + setB3ModalContentType({ type: "linkAccount", chain, partnerId }); } else if (type === "notifications") { - setB3ModalContentType({ - type: "notifications", - chain, - partnerId, - }); + setB3ModalContentType({ type: "notifications", chain, partnerId }); + } else if (type === "sessionDuration") { + setB3ModalContentType({ type: "sessionDuration", chain, partnerId }); } else { - setB3ModalContentType({ - type: "avatarEditor", - }); + setB3ModalContentType({ type: "avatarEditor" }); } setB3ModalOpen(true); }; @@ -111,11 +95,25 @@ const SettingsContent = ({ subtitle="Manage your notifications" onClick={() => handleNavigate("notifications")} /> + + + + } + title="Stay signed in" + subtitle={SESSION_DURATION_LABELS[sessionDays] ?? `${sessionDays} days`} + onClick={() => handleNavigate("sessionDuration")} + /> {/* Logout Section */}