@@ -2,77 +2,139 @@ import { useRequireAuth } from '@lib/hooks/useRequireAuth';
22import { Aside } from '@components/aside/aside' ;
33import { Suggestions } from '@components/aside/suggestions' ;
44import { Placeholder } from '@components/common/placeholder' ;
5- import { type ReactNode , useState } from 'react' ;
5+ import { type ReactNode , useState , useEffect , useRef } from 'react' ;
66import { Modal } from '@components/modal/modal' ;
77import { Button } from '@components/ui/button' ;
8- import { useAuth } from '@lib/context/auth-context' ;
98
109export 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+
1432export 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