Skip to content

Commit 609f4e9

Browse files
authored
fix: improve Disclaimer popup UX (#735)
* fix: add allowClickOutside prop to Modal for improved interaction * fix: enhance DisclaimerModal with tooltip and animation effects * fix: update ProtectedLayout to include disclaimer modal with enhanced styling and interaction * fix: enhance disclaimer modal with pulsing animation and tooltip for improved user interaction * fix: improve disclaimer handling with localStorage check and user hint * fix: update Modal to use onClickOutside prop for improved interaction and animation * fix: implement safe localStorage access methods for disclaimer handling across components * fix: improve disclaimer handling with enhanced localStorage access and user interaction
1 parent 33f1939 commit 609f4e9

File tree

7 files changed

+471
-204
lines changed

7 files changed

+471
-204
lines changed

platforms/blabsy/src/components/layout/common-layout.tsx

Lines changed: 108 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,77 +2,139 @@ import { useRequireAuth } from '@lib/hooks/useRequireAuth';
22
import { Aside } from '@components/aside/aside';
33
import { Suggestions } from '@components/aside/suggestions';
44
import { Placeholder } from '@components/common/placeholder';
5-
import { type ReactNode, useState } from 'react';
5+
import { type ReactNode, useState, useEffect, useRef } from 'react';
66
import { Modal } from '@components/modal/modal';
77
import { Button } from '@components/ui/button';
8-
import { useAuth } from '@lib/context/auth-context';
98

109
export type LayoutProps = {
1110
children: ReactNode;
1211
};
1312

13+
const DISCLAIMER_KEY = 'blabsy-disclaimer-accepted';
14+
15+
// Safe localStorage access for restricted environments
16+
const safeGetItem = (key: string): string | null => {
17+
try {
18+
return localStorage.getItem(key);
19+
} catch {
20+
return null;
21+
}
22+
};
23+
24+
const safeSetItem = (key: string, value: string): void => {
25+
try {
26+
localStorage.setItem(key, value);
27+
} catch {
28+
// Silently fail in restricted environments
29+
}
30+
};
31+
1432
export function ProtectedLayout({ children }: LayoutProps): JSX.Element {
1533
const user = useRequireAuth();
16-
const { signOut } = useAuth();
1734

1835
const [disclaimerAccepted, setDisclaimerAccepted] = useState(false);
36+
const [disclaimerChecked, setDisclaimerChecked] = useState(false);
37+
const [showHint, setShowHint] = useState(false);
38+
const [isPulsing, setIsPulsing] = useState(false);
39+
40+
useEffect(() => {
41+
let accepted = false;
42+
try {
43+
accepted = localStorage.getItem(DISCLAIMER_KEY) === 'true';
44+
} catch {
45+
// Storage may be unavailable; fall back to session-only acceptance.
46+
}
47+
setDisclaimerAccepted(accepted);
48+
setDisclaimerChecked(true);
49+
}, []);
50+
51+
const pulseTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
52+
const handleOutsideClick = () => {
53+
setIsPulsing(true);
54+
setShowHint(true);
55+
if (pulseTimeoutRef.current) clearTimeout(pulseTimeoutRef.current);
56+
pulseTimeoutRef.current = setTimeout(() => setIsPulsing(false), 400);
57+
};
1958
if (!user) return <Placeholder />;
59+
if (!disclaimerChecked) return <></>;
60+
if (disclaimerAccepted) return <>{children}</>;
2061

2162
return (
2263
<>
2364
{children}
24-
{!disclaimerAccepted ? (
25-
<Modal
26-
open={!disclaimerAccepted}
27-
closeModal={() => signOut()}
28-
className='max-w-lg mx-auto mt-24'
29-
modalClassName='bg-black backdrop-blur-md p-6 rounded-lg flex flex-col gap-2'
65+
<Modal
66+
open={true}
67+
closeModal={handleOutsideClick}
68+
className='max-w-lg mx-auto mt-24'
69+
modalClassName={`bg-black backdrop-blur-md p-6 rounded-lg flex flex-col gap-2 ${isPulsing ? 'animate-pulse-scale' : ''}`}
70+
>
71+
<style>{`
72+
@keyframes pulse-scale {
73+
0% { transform: scale(1); }
74+
25% { transform: scale(1.01); }
75+
50% { transform: scale(0.99); }
76+
75% { transform: scale(1.005); }
77+
100% { transform: scale(1); }
78+
}
79+
.animate-pulse-scale {
80+
animation: pulse-scale 0.4s ease-in-out;
81+
}
82+
`}</style>
83+
<h1 className='text-xl text-center font-bold'>
84+
Disclaimer from MetaState Foundation
85+
</h1>
86+
<p className='font-bold'>⚠️ Please note:</p>
87+
<p>
88+
Blabsy is a <b>functional prototype</b>, intended to
89+
showcase <b>interoperability</b> and core concepts of
90+
the W3DS ecosystem.
91+
</p>
92+
<p>
93+
<b>It is not a production-grade platform</b> and may
94+
lack full reliability, performance, and security
95+
guarantees.
96+
</p>
97+
<p>
98+
We <b>strongly recommend</b> that you avoid sharing{' '}
99+
<b>sensitive or private content</b>, and kindly ask for
100+
your understanding regarding any bugs, incomplete
101+
features, or unexpected behaviours.
102+
</p>
103+
<p>
104+
The app is still in development, so we kindly ask for
105+
your understanding regarding any potential issues. If
106+
you experience issues or have feedback, feel free to
107+
contact us at:
108+
</p>
109+
<a
110+
href='mailto:info@metastate.foundation'
111+
className='focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-400'
30112
>
31-
<h1 className='text-xl text-center font-bold'>
32-
Disclaimer from MetaState Foundation
33-
</h1>
34-
<p className='font-bold'>⚠️ Please note:</p>
35-
<p>
36-
Blabsy is a <b>functional prototype</b>, intended to
37-
showcase <b>interoperability</b> and core concepts of
38-
the W3DS ecosystem.
39-
</p>
40-
<p>
41-
<b>It is not a production-grade platform</b> and may
42-
lack full reliability, performance, and security
43-
guarantees.
44-
</p>
45-
<p>
46-
We <b>strongly recommend</b> that you avoid sharing{' '}
47-
<b>sensitive or private content</b>, and kindly ask for
48-
your understanding regarding any bugs, incomplete
49-
features, or unexpected behaviours.
50-
</p>
51-
<p>
52-
The app is still in development, so we kindly ask for
53-
your understanding regarding any potential issues. If
54-
you experience issues or have feedback, feel free to
55-
contact us at:
56-
</p>
57-
<a
58-
href='mailto:info@metastate.foundation'
59-
className='outline-none'
60-
>
61-
info@metastate.foundation
62-
</a>
113+
info@metastate.foundation
114+
</a>
115+
<div className='relative mt-4'>
116+
{showHint && (
117+
<div className='mb-2 text-xs text-center text-yellow-400 bg-yellow-900/30 px-3 py-2 rounded'>
118+
💡 You must accept the disclaimer to continue. This will only appear once.
119+
</div>
120+
)}
63121
<Button
64122
type='button'
65-
className='mt-4 bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600'
123+
className='w-full bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600'
124+
66125
onClick={() => {
126+
try {
127+
localStorage.setItem(DISCLAIMER_KEY, 'true');
128+
} catch {
129+
// Ignore storage failures; allow access for this session.
130+
}
67131
setDisclaimerAccepted(true);
68132
}}
69133
>
70134
I Understand
71135
</Button>
72-
</Modal>
73-
) : (
74-
<></>
75-
)}
136+
</div>
137+
</Modal>
76138
</>
77139
);
78140
}

0 commit comments

Comments
 (0)