11import React , { useState , useCallback } from 'react' ;
22import { Settings , Info , MoreVertical , PictureInPicture2 , Wifi } from 'lucide-react' ;
3- import { Tooltip } from '@/component-library' ;
3+ import { Tooltip , Modal } from '@/component-library' ;
44import { useI18n } from '@/infrastructure/i18n/hooks/useI18n' ;
55import { useSceneManager } from '../../../hooks/useSceneManager' ;
66import { useToolbarModeContext } from '@/flow_chat/components/toolbar-mode/ToolbarModeContext' ;
@@ -10,6 +10,16 @@ import NotificationButton from '../../TitleBar/NotificationButton';
1010import { AboutDialog } from '../../AboutDialog' ;
1111import { RemoteConnectDialog } from '../../RemoteConnectDialog' ;
1212
13+ const REMOTE_CONNECT_DISCLAIMER_KEY = 'bitfun:remote-connect:disclaimer-agreed:v1' ;
14+
15+ const getRemoteDisclaimerAgreed = ( ) : boolean => {
16+ try {
17+ return localStorage . getItem ( REMOTE_CONNECT_DISCLAIMER_KEY ) === 'true' ;
18+ } catch {
19+ return false ;
20+ }
21+ } ;
22+
1323const PersistentFooterActions : React . FC = ( ) => {
1424 const { t } = useI18n ( 'common' ) ;
1525 const { openScene } = useSceneManager ( ) ;
@@ -21,6 +31,8 @@ const PersistentFooterActions: React.FC = () => {
2131 const [ menuClosing , setMenuClosing ] = useState ( false ) ;
2232 const [ showAbout , setShowAbout ] = useState ( false ) ;
2333 const [ showRemoteConnect , setShowRemoteConnect ] = useState ( false ) ;
34+ const [ showRemoteDisclaimer , setShowRemoteDisclaimer ] = useState ( false ) ;
35+ const [ hasAgreedRemoteDisclaimer , setHasAgreedRemoteDisclaimer ] = useState < boolean > ( ( ) => getRemoteDisclaimerAgreed ( ) ) ;
2436
2537 const closeMenu = useCallback ( ( ) => {
2638 setMenuClosing ( true ) ;
@@ -53,14 +65,33 @@ const PersistentFooterActions: React.FC = () => {
5365 enableToolbarMode ( ) ;
5466 } ;
5567
56- const handleRemoteConnect = ( ) => {
68+ const handleRemoteConnect = useCallback ( async ( ) => {
5769 if ( ! hasWorkspace ) {
5870 warning ( t ( 'header.remoteConnectRequiresWorkspace' ) ) ;
5971 return ;
6072 }
73+
6174 closeMenu ( ) ;
75+
76+ if ( hasAgreedRemoteDisclaimer || getRemoteDisclaimerAgreed ( ) ) {
77+ setHasAgreedRemoteDisclaimer ( true ) ;
78+ setShowRemoteConnect ( true ) ;
79+ return ;
80+ }
81+
82+ setShowRemoteDisclaimer ( true ) ;
83+ } , [ hasWorkspace , warning , t , closeMenu , hasAgreedRemoteDisclaimer ] ) ;
84+
85+ const handleAgreeDisclaimer = useCallback ( ( ) => {
86+ try {
87+ localStorage . setItem ( REMOTE_CONNECT_DISCLAIMER_KEY , 'true' ) ;
88+ } catch {
89+ // Ignore storage failures and keep in-memory consent for current session.
90+ }
91+ setHasAgreedRemoteDisclaimer ( true ) ;
92+ setShowRemoteDisclaimer ( false ) ;
6293 setShowRemoteConnect ( true ) ;
63- } ;
94+ } , [ ] ) ;
6495
6596 return (
6697 < div className = "bitfun-nav-panel__footer" >
@@ -140,6 +171,53 @@ const PersistentFooterActions: React.FC = () => {
140171 < NotificationButton className = "bitfun-nav-panel__footer-btn" />
141172 < AboutDialog isOpen = { showAbout } onClose = { ( ) => setShowAbout ( false ) } />
142173 < RemoteConnectDialog isOpen = { showRemoteConnect } onClose = { ( ) => setShowRemoteConnect ( false ) } />
174+ < Modal
175+ isOpen = { showRemoteDisclaimer }
176+ onClose = { ( ) => setShowRemoteDisclaimer ( false ) }
177+ title = { t ( 'remoteConnect.disclaimerTitle' ) }
178+ showCloseButton
179+ size = "large"
180+ contentInset
181+ >
182+ < div className = "bitfun-nav-panel__remote-disclaimer" >
183+ < p className = "bitfun-nav-panel__remote-disclaimer-text" > { t ( 'remoteConnect.disclaimerIntro' ) } </ p >
184+ < ol className = "bitfun-nav-panel__remote-disclaimer-list" >
185+ < li > { t ( 'remoteConnect.disclaimerItemBeta' ) } </ li >
186+ < li > { t ( 'remoteConnect.disclaimerItemSecurity' ) } </ li >
187+ < li > { t ( 'remoteConnect.disclaimerItemEncryption' ) } </ li >
188+ < li > { t ( 'remoteConnect.disclaimerItemOpenSource' ) } </ li >
189+ < li > { t ( 'remoteConnect.disclaimerItemPrivacy' ) } </ li >
190+ < li > { t ( 'remoteConnect.disclaimerItemDataUsage' ) } </ li >
191+ < li > { t ( 'remoteConnect.disclaimerItemCredentials' ) } </ li >
192+ < li > { t ( 'remoteConnect.disclaimerItemQrCode' ) } </ li >
193+ < li > { t ( 'remoteConnect.disclaimerItemNgrok' ) } </ li >
194+ < li > { t ( 'remoteConnect.disclaimerItemSelfHosted' ) } </ li >
195+ < li > { t ( 'remoteConnect.disclaimerItemNetwork' ) } </ li >
196+ < li > { t ( 'remoteConnect.disclaimerItemBot' ) } </ li >
197+ < li > { t ( 'remoteConnect.disclaimerItemBotPersistence' ) } </ li >
198+ < li > { t ( 'remoteConnect.disclaimerItemMobileBrowser' ) } </ li >
199+ < li > { t ( 'remoteConnect.disclaimerItemCompliance' ) } </ li >
200+ < li > { t ( 'remoteConnect.disclaimerItemLiability' ) } </ li >
201+ </ ol >
202+
203+ < div className = "bitfun-nav-panel__remote-disclaimer-actions" >
204+ < button
205+ type = "button"
206+ className = "bitfun-nav-panel__remote-disclaimer-btn bitfun-nav-panel__remote-disclaimer-btn--secondary"
207+ onClick = { ( ) => setShowRemoteDisclaimer ( false ) }
208+ >
209+ { t ( 'remoteConnect.disclaimerDecline' ) }
210+ </ button >
211+ < button
212+ type = "button"
213+ className = "bitfun-nav-panel__remote-disclaimer-btn bitfun-nav-panel__remote-disclaimer-btn--primary"
214+ onClick = { handleAgreeDisclaimer }
215+ >
216+ { t ( 'remoteConnect.disclaimerAgree' ) }
217+ </ button >
218+ </ div >
219+ </ div >
220+ </ Modal >
143221 </ div >
144222 ) ;
145223} ;
0 commit comments