@b3dotfun/sdk 0.1.69-alpha.7 → 0.1.69-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/anyspend/react/components/AnySpendStakeB3.js +1 -1
- package/dist/cjs/anyspend/react/components/AnySpendStakeB3ExactIn.js +1 -1
- package/dist/cjs/anyspend/react/components/checkout/CheckoutSuccess.d.ts +2 -1
- package/dist/cjs/anyspend/react/components/checkout/CheckoutSuccess.js +5 -3
- package/dist/cjs/app.shared.js +9 -7
- package/dist/cjs/global-account/react/components/B3DynamicModal.js +3 -0
- package/dist/cjs/global-account/react/components/ManageAccount/SessionDurationContent.d.ts +5 -0
- package/dist/cjs/global-account/react/components/ManageAccount/SessionDurationContent.js +57 -0
- package/dist/cjs/global-account/react/components/ManageAccount/SettingsContent.js +12 -29
- package/dist/cjs/global-account/react/hooks/useFirstEOA.d.ts +8 -8
- package/dist/cjs/global-account/react/stores/useModalStore.d.ts +10 -1
- package/dist/cjs/shared/utils/session-duration.d.ts +15 -0
- package/dist/cjs/shared/utils/session-duration.js +69 -0
- package/dist/esm/anyspend/react/components/AnySpendStakeB3.js +2 -2
- package/dist/esm/anyspend/react/components/AnySpendStakeB3ExactIn.js +2 -2
- package/dist/esm/anyspend/react/components/checkout/CheckoutSuccess.d.ts +2 -1
- package/dist/esm/anyspend/react/components/checkout/CheckoutSuccess.js +5 -3
- package/dist/esm/app.shared.js +9 -7
- package/dist/esm/global-account/react/components/B3DynamicModal.js +3 -0
- package/dist/esm/global-account/react/components/ManageAccount/SessionDurationContent.d.ts +5 -0
- package/dist/esm/global-account/react/components/ManageAccount/SessionDurationContent.js +52 -0
- package/dist/esm/global-account/react/components/ManageAccount/SettingsContent.js +12 -29
- package/dist/esm/global-account/react/hooks/useFirstEOA.d.ts +8 -8
- package/dist/esm/global-account/react/stores/useModalStore.d.ts +10 -1
- package/dist/esm/shared/utils/session-duration.d.ts +15 -0
- package/dist/esm/shared/utils/session-duration.js +64 -0
- package/dist/styles/index.css +1 -1
- package/dist/types/anyspend/react/components/checkout/CheckoutSuccess.d.ts +2 -1
- package/dist/types/global-account/react/components/ManageAccount/SessionDurationContent.d.ts +5 -0
- package/dist/types/global-account/react/hooks/useFirstEOA.d.ts +8 -8
- package/dist/types/global-account/react/stores/useModalStore.d.ts +10 -1
- package/dist/types/shared/utils/session-duration.d.ts +15 -0
- package/package.json +1 -1
- package/src/anyspend/react/components/AnySpendStakeB3.tsx +2 -2
- package/src/anyspend/react/components/AnySpendStakeB3ExactIn.tsx +2 -2
- package/src/anyspend/react/components/checkout/CheckoutSuccess.tsx +13 -3
- package/src/app.shared.ts +9 -8
- package/src/global-account/react/components/B3DynamicModal.tsx +3 -0
- package/src/global-account/react/components/ManageAccount/SessionDurationContent.tsx +107 -0
- package/src/global-account/react/components/ManageAccount/SettingsContent.tsx +28 -30
- package/src/global-account/react/stores/useModalStore.ts +11 -0
- package/src/shared/utils/session-duration.ts +64 -0
|
@@ -425,6 +425,15 @@ export interface SendModalProps extends BaseModalProps {
|
|
|
425
425
|
/** Callback function called when send is successful */
|
|
426
426
|
onSuccess?: (txHash?: string) => void;
|
|
427
427
|
}
|
|
428
|
+
/**
|
|
429
|
+
* Props for the Session Duration modal
|
|
430
|
+
* Allows users to configure how long they stay signed in
|
|
431
|
+
*/
|
|
432
|
+
export interface SessionDurationModalProps extends BaseModalProps {
|
|
433
|
+
type: "sessionDuration";
|
|
434
|
+
partnerId: string;
|
|
435
|
+
chain: Chain;
|
|
436
|
+
}
|
|
428
437
|
/**
|
|
429
438
|
* Props for the Notifications modal
|
|
430
439
|
* Allows users to manage notification settings and channels
|
|
@@ -626,7 +635,7 @@ export interface AnySpendDepositModalProps extends BaseModalProps {
|
|
|
626
635
|
/**
|
|
627
636
|
* Union type of all possible modal content types
|
|
628
637
|
*/
|
|
629
|
-
export type ModalContentType = SignInWithB3ModalProps | RequestPermissionsModalProps | ManageAccountModalProps | AnySpendModalProps | AnyspendOrderDetailsProps | AnySpendNftProps | AnySpendJoinTournamentProps | AnySpendFundTournamentProps | AnySpendOrderHistoryProps | AnySpendStakeB3Props | AnySpendStakeB3ExactInProps | AnySpendStakeUpsideProps | AnySpendStakeUpsideExactInProps | AnySpendDepositUpsideProps | AnySpendBuySpinProps | AnySpendSignatureMintProps | AnySpendBondKitProps | LinkAccountModalProps | LinkNewAccountModalProps | AnySpendDepositHypeProps | AvatarEditorModalProps | DepositModalProps | SendModalProps | NotificationsModalProps | AnySpendCollectorClubPurchaseProps | AnySpendDepositModalProps | AnySpendWorkflowTriggerModalProps | AnySpendCheckoutTriggerModalProps;
|
|
638
|
+
export type ModalContentType = SignInWithB3ModalProps | RequestPermissionsModalProps | ManageAccountModalProps | AnySpendModalProps | AnyspendOrderDetailsProps | AnySpendNftProps | AnySpendJoinTournamentProps | AnySpendFundTournamentProps | AnySpendOrderHistoryProps | AnySpendStakeB3Props | AnySpendStakeB3ExactInProps | AnySpendStakeUpsideProps | AnySpendStakeUpsideExactInProps | AnySpendDepositUpsideProps | AnySpendBuySpinProps | AnySpendSignatureMintProps | AnySpendBondKitProps | LinkAccountModalProps | LinkNewAccountModalProps | AnySpendDepositHypeProps | AvatarEditorModalProps | DepositModalProps | SendModalProps | NotificationsModalProps | SessionDurationModalProps | AnySpendCollectorClubPurchaseProps | AnySpendDepositModalProps | AnySpendWorkflowTriggerModalProps | AnySpendCheckoutTriggerModalProps;
|
|
630
639
|
/**
|
|
631
640
|
* State interface for the modal store
|
|
632
641
|
*/
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare const SESSION_DURATION_OPTIONS: readonly [0, 1, 7, 14, 30];
|
|
2
|
+
export type SessionDurationDays = (typeof SESSION_DURATION_OPTIONS)[number];
|
|
3
|
+
/**
|
|
4
|
+
* Read session duration for a specific partner.
|
|
5
|
+
*
|
|
6
|
+
* preferences shape: { [partnerId]: { sessionDuration: number }, sessionDuration?: number }
|
|
7
|
+
*
|
|
8
|
+
* Priority: user.preferences[partnerId].sessionDuration
|
|
9
|
+
* → user.preferences.sessionDuration (global fallback)
|
|
10
|
+
* → localStorage (per-partner) → localStorage (global) → default 7d
|
|
11
|
+
*/
|
|
12
|
+
export declare function getSessionDurationDays(userPreferences?: Record<string, any>, partnerId?: string): SessionDurationDays;
|
|
13
|
+
export declare const SESSION_DURATION_LABELS: Record<SessionDurationDays, string>;
|
|
14
|
+
/** Cache the preference locally so it's available immediately on next login */
|
|
15
|
+
export declare function setSessionDurationDays(days: SessionDurationDays, partnerId?: string): void;
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ABI_ERC20_STAKING, B3_TOKEN, eqci } from "@b3dotfun/sdk/anyspend";
|
|
1
|
+
import { ABI_ERC20_STAKING, B3_TOKEN, eqci, getExplorerTxUrl } from "@b3dotfun/sdk/anyspend";
|
|
2
2
|
import {
|
|
3
3
|
Button,
|
|
4
4
|
GlareCardRounded,
|
|
@@ -474,7 +474,7 @@ export function AnySpendStakeB3({
|
|
|
474
474
|
>
|
|
475
475
|
<div className="mb-6">
|
|
476
476
|
<a
|
|
477
|
-
href={
|
|
477
|
+
href={getExplorerTxUrl(base.id, stakingTxHash)}
|
|
478
478
|
target="_blank"
|
|
479
479
|
rel="noopener noreferrer"
|
|
480
480
|
className="text-as-primary/70 hover:text-as-primary block break-all text-center font-mono text-sm underline transition-colors"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ABI_ERC20_STAKING, B3_TOKEN, eqci } from "@b3dotfun/sdk/anyspend";
|
|
1
|
+
import { ABI_ERC20_STAKING, B3_TOKEN, eqci, getExplorerTxUrl } from "@b3dotfun/sdk/anyspend";
|
|
2
2
|
import { normalizeAddress } from "@b3dotfun/sdk/anyspend/utils";
|
|
3
3
|
import {
|
|
4
4
|
Button,
|
|
@@ -492,7 +492,7 @@ export function AnySpendStakeB3ExactIn({
|
|
|
492
492
|
>
|
|
493
493
|
<div className="mb-6">
|
|
494
494
|
<a
|
|
495
|
-
href={
|
|
495
|
+
href={getExplorerTxUrl(base.id, stakingTxHash)}
|
|
496
496
|
target="_blank"
|
|
497
497
|
rel="noopener noreferrer"
|
|
498
498
|
className="text-as-primary/70 hover:text-as-primary block break-all text-center font-mono text-sm underline transition-colors"
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { getExplorerTxUrl } from "@b3dotfun/sdk/anyspend";
|
|
4
|
+
import { b3 } from "viem/chains";
|
|
3
5
|
import { cn } from "@b3dotfun/sdk/shared/utils/cn";
|
|
4
6
|
import { ExternalLink } from "lucide-react";
|
|
5
7
|
import { motion } from "motion/react";
|
|
@@ -9,13 +11,21 @@ import type { AnySpendCheckoutClasses } from "./AnySpendCheckout";
|
|
|
9
11
|
|
|
10
12
|
interface CheckoutSuccessProps {
|
|
11
13
|
txHash?: string;
|
|
14
|
+
dstChainId?: number;
|
|
12
15
|
orderId?: string;
|
|
13
16
|
returnUrl?: string;
|
|
14
17
|
returnLabel?: string;
|
|
15
18
|
classes?: AnySpendCheckoutClasses;
|
|
16
19
|
}
|
|
17
20
|
|
|
18
|
-
export function CheckoutSuccess({
|
|
21
|
+
export function CheckoutSuccess({
|
|
22
|
+
txHash,
|
|
23
|
+
dstChainId,
|
|
24
|
+
orderId,
|
|
25
|
+
returnUrl,
|
|
26
|
+
returnLabel,
|
|
27
|
+
classes,
|
|
28
|
+
}: CheckoutSuccessProps) {
|
|
19
29
|
const { content, slots } = useAnySpendCustomization();
|
|
20
30
|
|
|
21
31
|
if (slots.successScreen) {
|
|
@@ -29,7 +39,7 @@ export function CheckoutSuccess({ txHash, orderId, returnUrl, returnLabel, class
|
|
|
29
39
|
: "Your payment has been processed successfully.",
|
|
30
40
|
txHash,
|
|
31
41
|
orderId,
|
|
32
|
-
explorerUrl: txHash ?
|
|
42
|
+
explorerUrl: txHash ? getExplorerTxUrl(dstChainId ?? b3.id, txHash) : undefined,
|
|
33
43
|
onDone: () => {
|
|
34
44
|
if (returnUrl) window.location.href = returnUrl;
|
|
35
45
|
},
|
|
@@ -69,7 +79,7 @@ export function CheckoutSuccess({ txHash, orderId, returnUrl, returnLabel, class
|
|
|
69
79
|
initial={{ opacity: 0 }}
|
|
70
80
|
animate={{ opacity: 1 }}
|
|
71
81
|
transition={{ duration: 0.3, delay: 0.5, ease: "easeOut" }}
|
|
72
|
-
href={
|
|
82
|
+
href={getExplorerTxUrl(dstChainId ?? b3.id, txHash)}
|
|
73
83
|
target="_blank"
|
|
74
84
|
rel="noopener noreferrer"
|
|
75
85
|
className="anyspend-success-tx-link mt-4 flex items-center gap-1.5 text-sm text-blue-600 hover:underline dark:text-blue-400"
|
package/src/app.shared.ts
CHANGED
|
@@ -2,12 +2,11 @@ import { ClientApplication } from "@b3dotfun/b3-api";
|
|
|
2
2
|
import { AuthenticationClient } from "@feathersjs/authentication-client";
|
|
3
3
|
import Cookies from "js-cookie";
|
|
4
4
|
import { B3_AUTH_COOKIE_NAME } from "./shared/constants";
|
|
5
|
+
import { getSessionDurationDays } from "./shared/utils/session-duration";
|
|
5
6
|
|
|
6
7
|
export const B3_API_URL =
|
|
7
8
|
process.env.EXPO_PUBLIC_B3_API || process.env.NEXT_PUBLIC_B3_API || process.env.PUBLIC_B3_API || "https://api.b3.fun";
|
|
8
9
|
|
|
9
|
-
const DEV_USER_GROUP = 4;
|
|
10
|
-
|
|
11
10
|
export const authenticate = async (
|
|
12
11
|
app: ClientApplication,
|
|
13
12
|
accessToken: string,
|
|
@@ -33,12 +32,14 @@ export const authenticate = async (
|
|
|
33
32
|
},
|
|
34
33
|
);
|
|
35
34
|
|
|
36
|
-
|
|
37
|
-
if (
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
const token = Cookies.get(B3_AUTH_COOKIE_NAME);
|
|
36
|
+
if (token) {
|
|
37
|
+
const days = getSessionDurationDays(response?.user?.preferences, params?.partnerId);
|
|
38
|
+
Cookies.set(B3_AUTH_COOKIE_NAME, token, {
|
|
39
|
+
...(days > 0 ? { expires: days } : {}),
|
|
40
|
+
secure: true,
|
|
41
|
+
sameSite: "Lax",
|
|
42
|
+
});
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
return response;
|
|
@@ -27,6 +27,7 @@ import { LinkAccount } from "./LinkAccount/LinkAccount";
|
|
|
27
27
|
import { LinkNewAccount } from "./LinkAccount/LinkNewAccount";
|
|
28
28
|
import { ManageAccount } from "./ManageAccount/ManageAccount";
|
|
29
29
|
import NotificationsContent from "./ManageAccount/NotificationsContent";
|
|
30
|
+
import SessionDurationContent from "./ManageAccount/SessionDurationContent";
|
|
30
31
|
import { RequestPermissions } from "./RequestPermissions/RequestPermissions";
|
|
31
32
|
import { Send } from "./Send/Send";
|
|
32
33
|
import { SignInWithB3Flow } from "./SignInWithB3/SignInWithB3Flow";
|
|
@@ -172,6 +173,8 @@ export function B3DynamicModal() {
|
|
|
172
173
|
return <Send {...contentType} />;
|
|
173
174
|
case "notifications":
|
|
174
175
|
return <NotificationsContent {...contentType} />;
|
|
176
|
+
case "sessionDuration":
|
|
177
|
+
return <SessionDurationContent partnerId={contentType.partnerId} />;
|
|
175
178
|
// Add other modal types here
|
|
176
179
|
default:
|
|
177
180
|
return null;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import app from "@b3dotfun/sdk/global-account/app";
|
|
2
|
+
import { useAuthentication, useModalStore } from "@b3dotfun/sdk/global-account/react";
|
|
3
|
+
import {
|
|
4
|
+
getSessionDurationDays,
|
|
5
|
+
SESSION_DURATION_LABELS,
|
|
6
|
+
SESSION_DURATION_OPTIONS,
|
|
7
|
+
SessionDurationDays,
|
|
8
|
+
setSessionDurationDays,
|
|
9
|
+
} from "@b3dotfun/sdk/shared/utils/session-duration";
|
|
10
|
+
import { useState } from "react";
|
|
11
|
+
import ModalHeader from "../ModalHeader/ModalHeader";
|
|
12
|
+
|
|
13
|
+
interface SessionDurationContentProps {
|
|
14
|
+
partnerId: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const DESCRIPTIONS: Record<SessionDurationDays, string> = {
|
|
18
|
+
0: "Sign out when browser closes",
|
|
19
|
+
1: "Stay signed in for 1 day",
|
|
20
|
+
7: "Stay signed in for 7 days",
|
|
21
|
+
14: "Stay signed in for 2 weeks",
|
|
22
|
+
30: "Stay signed in for 30 days",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const SessionDurationContent = ({ partnerId }: SessionDurationContentProps) => {
|
|
26
|
+
const { user, setUser } = useAuthentication(partnerId);
|
|
27
|
+
const navigateBack = useModalStore(state => state.navigateBack);
|
|
28
|
+
const [sessionDays, setSessionDays] = useState<SessionDurationDays>(() =>
|
|
29
|
+
getSessionDurationDays(user?.preferences, partnerId),
|
|
30
|
+
);
|
|
31
|
+
const [saving, setSaving] = useState(false);
|
|
32
|
+
|
|
33
|
+
const handleSelect = async (days: SessionDurationDays) => {
|
|
34
|
+
const previous = sessionDays;
|
|
35
|
+
setSessionDurationDays(days, partnerId);
|
|
36
|
+
setSessionDays(days);
|
|
37
|
+
if (user?.userId) {
|
|
38
|
+
setSaving(true);
|
|
39
|
+
try {
|
|
40
|
+
const updated = await app.service("users").patch(user.userId, {
|
|
41
|
+
preferences: {
|
|
42
|
+
...user.preferences,
|
|
43
|
+
[partnerId]: {
|
|
44
|
+
...((((user.preferences as Record<string, unknown>) ?? {})[partnerId] as Record<string, unknown>) ?? {}),
|
|
45
|
+
sessionDuration: days,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
setUser(updated);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error("Failed to save session duration preference:", error);
|
|
52
|
+
// Revert optimistic update so UI stays consistent with server state
|
|
53
|
+
setSessionDays(previous);
|
|
54
|
+
setSessionDurationDays(previous, partnerId);
|
|
55
|
+
} finally {
|
|
56
|
+
setSaving(false);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<div className="flex h-[470px] flex-col">
|
|
63
|
+
<ModalHeader showBackButton={true} showCloseButton={false} title="Stay signed in" handleBack={navigateBack} />
|
|
64
|
+
|
|
65
|
+
<div className="flex flex-col gap-2 p-5">
|
|
66
|
+
{SESSION_DURATION_OPTIONS.map(days => (
|
|
67
|
+
<button
|
|
68
|
+
type="button"
|
|
69
|
+
key={days}
|
|
70
|
+
onClick={() => handleSelect(days)}
|
|
71
|
+
disabled={saving}
|
|
72
|
+
className={`flex items-center justify-between rounded-xl border px-4 py-3 transition-colors ${
|
|
73
|
+
sessionDays === days
|
|
74
|
+
? "border-[#3f3f46] bg-[#f4f4f5] dark:border-white dark:bg-white/10"
|
|
75
|
+
: "border-[#e4e4e7] bg-transparent hover:bg-[#f4f4f5] dark:border-white/10 dark:hover:bg-white/5"
|
|
76
|
+
}`}
|
|
77
|
+
>
|
|
78
|
+
<div className="flex flex-col items-start gap-0.5">
|
|
79
|
+
<span className="font-neue-montreal-semibold text-[14px] leading-none tracking-[-0.28px] text-[#3f3f46] dark:text-white">
|
|
80
|
+
{SESSION_DURATION_LABELS[days]}
|
|
81
|
+
</span>
|
|
82
|
+
<span className="font-neue-montreal-medium text-[13px] leading-none tracking-[-0.26px] text-[#70707b] dark:text-white/50">
|
|
83
|
+
{DESCRIPTIONS[days]}
|
|
84
|
+
</span>
|
|
85
|
+
</div>
|
|
86
|
+
{sessionDays === days && (
|
|
87
|
+
<div className="flex size-5 items-center justify-center rounded-full bg-[#3f3f46] dark:bg-white">
|
|
88
|
+
<svg width="10" height="8" viewBox="0 0 10 8" fill="none">
|
|
89
|
+
<path
|
|
90
|
+
d="M1 4L3.5 6.5L9 1"
|
|
91
|
+
stroke="white"
|
|
92
|
+
strokeWidth="1.5"
|
|
93
|
+
strokeLinecap="round"
|
|
94
|
+
strokeLinejoin="round"
|
|
95
|
+
className="dark:stroke-[#3f3f46]"
|
|
96
|
+
/>
|
|
97
|
+
</svg>
|
|
98
|
+
</div>
|
|
99
|
+
)}
|
|
100
|
+
</button>
|
|
101
|
+
))}
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export default SessionDurationContent;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useAuthentication, useModalStore } from "@b3dotfun/sdk/global-account/react";
|
|
2
2
|
import { client } from "@b3dotfun/sdk/shared/utils/thirdweb";
|
|
3
|
+
import { getSessionDurationDays, SESSION_DURATION_LABELS } from "@b3dotfun/sdk/shared/utils/session-duration";
|
|
3
4
|
import { Loader2 } from "lucide-react";
|
|
4
5
|
import { useState } from "react";
|
|
5
6
|
import { Chain } from "thirdweb";
|
|
@@ -20,46 +21,29 @@ const SettingsContent = ({
|
|
|
20
21
|
}) => {
|
|
21
22
|
const setB3ModalContentType = useModalStore(state => state.setB3ModalContentType);
|
|
22
23
|
const setB3ModalOpen = useModalStore(state => state.setB3ModalOpen);
|
|
23
|
-
const { logout } = useAuthentication(partnerId);
|
|
24
|
+
const { logout, user } = useAuthentication(partnerId);
|
|
24
25
|
const [logoutLoading, setLogoutLoading] = useState(false);
|
|
25
26
|
|
|
26
|
-
const
|
|
27
|
+
const sessionDays = getSessionDurationDays(user?.preferences, partnerId);
|
|
27
28
|
|
|
29
|
+
const { data: profilesRaw = [] } = useProfiles({ client });
|
|
28
30
|
const profiles = profilesRaw.filter((profile: any) => !["custom_auth_endpoint"].includes(profile.type));
|
|
29
31
|
|
|
30
|
-
const handleNavigate = (
|
|
32
|
+
const handleNavigate = (
|
|
33
|
+
type: "home" | "swap" | "linkAccount" | "avatarEditor" | "notifications" | "sessionDuration",
|
|
34
|
+
) => {
|
|
31
35
|
if (type === "home") {
|
|
32
|
-
setB3ModalContentType({
|
|
33
|
-
type: "manageAccount",
|
|
34
|
-
chain,
|
|
35
|
-
partnerId,
|
|
36
|
-
onLogout,
|
|
37
|
-
activeTab: "home",
|
|
38
|
-
});
|
|
36
|
+
setB3ModalContentType({ type: "manageAccount", chain, partnerId, onLogout, activeTab: "home" });
|
|
39
37
|
} else if (type === "swap") {
|
|
40
|
-
setB3ModalContentType({
|
|
41
|
-
type: "manageAccount",
|
|
42
|
-
chain,
|
|
43
|
-
partnerId,
|
|
44
|
-
onLogout,
|
|
45
|
-
activeTab: "tokens",
|
|
46
|
-
});
|
|
38
|
+
setB3ModalContentType({ type: "manageAccount", chain, partnerId, onLogout, activeTab: "tokens" });
|
|
47
39
|
} else if (type === "linkAccount") {
|
|
48
|
-
setB3ModalContentType({
|
|
49
|
-
type: "linkAccount",
|
|
50
|
-
chain,
|
|
51
|
-
partnerId,
|
|
52
|
-
});
|
|
40
|
+
setB3ModalContentType({ type: "linkAccount", chain, partnerId });
|
|
53
41
|
} else if (type === "notifications") {
|
|
54
|
-
setB3ModalContentType({
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
partnerId,
|
|
58
|
-
});
|
|
42
|
+
setB3ModalContentType({ type: "notifications", chain, partnerId });
|
|
43
|
+
} else if (type === "sessionDuration") {
|
|
44
|
+
setB3ModalContentType({ type: "sessionDuration", chain, partnerId });
|
|
59
45
|
} else {
|
|
60
|
-
setB3ModalContentType({
|
|
61
|
-
type: "avatarEditor",
|
|
62
|
-
});
|
|
46
|
+
setB3ModalContentType({ type: "avatarEditor" });
|
|
63
47
|
}
|
|
64
48
|
setB3ModalOpen(true);
|
|
65
49
|
};
|
|
@@ -111,11 +95,25 @@ const SettingsContent = ({
|
|
|
111
95
|
subtitle="Manage your notifications"
|
|
112
96
|
onClick={() => handleNavigate("notifications")}
|
|
113
97
|
/>
|
|
98
|
+
<SettingsMenuItem
|
|
99
|
+
icon={
|
|
100
|
+
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
101
|
+
<path
|
|
102
|
+
d="M0 12C0 5.37258 5.37258 0 12 0H28C34.6274 0 40 5.37258 40 12V28C40 34.6274 34.6274 40 28 40H12C5.37258 40 0 34.6274 0 28V12Z"
|
|
103
|
+
fill="#F4F4F5"
|
|
104
|
+
/>
|
|
105
|
+
</svg>
|
|
106
|
+
}
|
|
107
|
+
title="Stay signed in"
|
|
108
|
+
subtitle={SESSION_DURATION_LABELS[sessionDays] ?? `${sessionDays} days`}
|
|
109
|
+
onClick={() => handleNavigate("sessionDuration")}
|
|
110
|
+
/>
|
|
114
111
|
</div>
|
|
115
112
|
|
|
116
113
|
{/* Logout Section */}
|
|
117
114
|
<div className="mt-auto px-5 pb-5">
|
|
118
115
|
<button
|
|
116
|
+
type="button"
|
|
119
117
|
className="b3-modal-sign-out-button border-b3-line hover:bg-b3-line bg-b3-background dark:bg-b3-background dark:border-b3-line dark:hover:bg-b3-line/80 flex w-full items-center justify-center gap-1.5 rounded-xl border border-solid p-3 transition-colors"
|
|
120
118
|
onClick={onLogoutEnhanced}
|
|
121
119
|
disabled={logoutLoading}
|
|
@@ -451,6 +451,16 @@ export interface SendModalProps extends BaseModalProps {
|
|
|
451
451
|
onSuccess?: (txHash?: string) => void;
|
|
452
452
|
}
|
|
453
453
|
|
|
454
|
+
/**
|
|
455
|
+
* Props for the Session Duration modal
|
|
456
|
+
* Allows users to configure how long they stay signed in
|
|
457
|
+
*/
|
|
458
|
+
export interface SessionDurationModalProps extends BaseModalProps {
|
|
459
|
+
type: "sessionDuration";
|
|
460
|
+
partnerId: string;
|
|
461
|
+
chain: Chain;
|
|
462
|
+
}
|
|
463
|
+
|
|
454
464
|
/**
|
|
455
465
|
* Props for the Notifications modal
|
|
456
466
|
* Allows users to manage notification settings and channels
|
|
@@ -677,6 +687,7 @@ export type ModalContentType =
|
|
|
677
687
|
| DepositModalProps
|
|
678
688
|
| SendModalProps
|
|
679
689
|
| NotificationsModalProps
|
|
690
|
+
| SessionDurationModalProps
|
|
680
691
|
| AnySpendCollectorClubPurchaseProps
|
|
681
692
|
| AnySpendDepositModalProps
|
|
682
693
|
| AnySpendWorkflowTriggerModalProps
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const STORAGE_KEY_PREFIX = "b3-session-duration";
|
|
2
|
+
const DEFAULT_DAYS = 7;
|
|
3
|
+
|
|
4
|
+
// 0 = session cookie (expires when browser closes)
|
|
5
|
+
export const SESSION_DURATION_OPTIONS = [0, 1, 7, 14, 30] as const;
|
|
6
|
+
export type SessionDurationDays = (typeof SESSION_DURATION_OPTIONS)[number];
|
|
7
|
+
|
|
8
|
+
function storageKey(partnerId?: string) {
|
|
9
|
+
return partnerId ? `${STORAGE_KEY_PREFIX}_${partnerId}` : STORAGE_KEY_PREFIX;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Read session duration for a specific partner.
|
|
14
|
+
*
|
|
15
|
+
* preferences shape: { [partnerId]: { sessionDuration: number }, sessionDuration?: number }
|
|
16
|
+
*
|
|
17
|
+
* Priority: user.preferences[partnerId].sessionDuration
|
|
18
|
+
* → user.preferences.sessionDuration (global fallback)
|
|
19
|
+
* → localStorage (per-partner) → localStorage (global) → default 7d
|
|
20
|
+
*/
|
|
21
|
+
export function getSessionDurationDays(userPreferences?: Record<string, any>, partnerId?: string): SessionDurationDays {
|
|
22
|
+
if (userPreferences) {
|
|
23
|
+
if (partnerId) {
|
|
24
|
+
const v = userPreferences[partnerId]?.sessionDuration;
|
|
25
|
+
if (SESSION_DURATION_OPTIONS.includes(v as SessionDurationDays)) return v as SessionDurationDays;
|
|
26
|
+
}
|
|
27
|
+
const v = userPreferences["sessionDuration"];
|
|
28
|
+
if (SESSION_DURATION_OPTIONS.includes(v as SessionDurationDays)) return v as SessionDurationDays;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
if (partnerId) {
|
|
32
|
+
const stored = localStorage.getItem(storageKey(partnerId));
|
|
33
|
+
if (stored !== null) {
|
|
34
|
+
const parsed = Number(stored);
|
|
35
|
+
if (SESSION_DURATION_OPTIONS.includes(parsed as SessionDurationDays)) return parsed as SessionDurationDays;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const stored = localStorage.getItem(STORAGE_KEY_PREFIX);
|
|
39
|
+
if (stored !== null) {
|
|
40
|
+
const parsed = Number(stored);
|
|
41
|
+
if (SESSION_DURATION_OPTIONS.includes(parsed as SessionDurationDays)) return parsed as SessionDurationDays;
|
|
42
|
+
}
|
|
43
|
+
} catch {
|
|
44
|
+
// localStorage unavailable (e.g. SSR)
|
|
45
|
+
}
|
|
46
|
+
return DEFAULT_DAYS;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const SESSION_DURATION_LABELS: Record<SessionDurationDays, string> = {
|
|
50
|
+
0: "Session only",
|
|
51
|
+
1: "1 day",
|
|
52
|
+
7: "7 days",
|
|
53
|
+
14: "14 days",
|
|
54
|
+
30: "30 days",
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/** Cache the preference locally so it's available immediately on next login */
|
|
58
|
+
export function setSessionDurationDays(days: SessionDurationDays, partnerId?: string): void {
|
|
59
|
+
try {
|
|
60
|
+
localStorage.setItem(storageKey(partnerId), String(days));
|
|
61
|
+
} catch {
|
|
62
|
+
// ignore
|
|
63
|
+
}
|
|
64
|
+
}
|