@@ -7,6 +7,24 @@ import UsernameModal from '../components/UsernameModal';
77
88const ClientContext = createContext < ClientContextType | null > ( null ) ;
99
10+ let timeOffset = 0 ;
11+ let timeSynced = false ;
12+
13+ export const getAdjustedTimestamp = ( ) : string => {
14+ return new Date ( Date . now ( ) + timeOffset ) . toISOString ( ) ;
15+ } ;
16+
17+ const calculateTimeOffset = ( serverTimestamp : number ) : number => {
18+ const clientTime = Date . now ( ) ;
19+ const offset = serverTimestamp - clientTime ;
20+ console . log ( `Server time: ${ new Date ( serverTimestamp ) . toISOString ( ) } ` ) ;
21+ console . log ( `Client time: ${ new Date ( clientTime ) . toISOString ( ) } ` ) ;
22+ console . log ( `Calculated offset: ${ offset } ms` ) ;
23+ return offset ;
24+ } ;
25+
26+
27+
1028const validateUsername = ( username : string ) : boolean => {
1129 const regex = / ^ [ a - z A - Z 0 - 9 _ - ] { 3 , 16 } $ / ;
1230 return regex . test ( username ) ;
@@ -18,6 +36,7 @@ export const ClientProvider = ({ children }: { children: ComponentChildren }) =>
1836 const [ loading , setLoading ] = useState ( true ) ;
1937 const [ showUsernameModal , setShowUsernameModal ] = useState ( false ) ;
2038 const [ usernamePromptReason , setUsernamePromptReason ] = useState < string > ( '' ) ;
39+ const [ isTimeSynced , setIsTimeSynced ] = useState ( false ) ;
2140
2241 const promptForUsername = ( reason : string = 'Enter a username' ) => {
2342 setUsernamePromptReason ( reason ) ;
@@ -39,6 +58,15 @@ export const ClientProvider = ({ children }: { children: ComponentChildren }) =>
3958 }
4059 } ;
4160
61+
62+ useEffect ( ( ) => {
63+ if ( status !== "open" ) return ;
64+
65+
66+ setIsTimeSynced ( false ) ;
67+ timeSynced = false ;
68+ } , [ status ] ) ;
69+
4270 useEffect ( ( ) => {
4371 if ( status !== "open" ) return ;
4472
@@ -58,26 +86,42 @@ export const ClientProvider = ({ children }: { children: ComponentChildren }) =>
5886 }
5987
6088 setUsername ( savedUsername ) ;
61- sendAuthenticatedMessage ( sendMessage , { command : "heartbeat" , client_id : savedUsername } ) ;
89+
90+
91+ sendMessage ( { command : "heartbeat" , client_id : savedUsername } ) ;
6292 } ) ( ) ;
6393 } , [ status , sendMessage ] ) ;
6494
95+
6596 useEffect ( ( ) => {
6697 if ( ! messages . length ) return ;
6798
6899 const lastMessage = messages [ messages . length - 1 ] ;
69100
101+
102+ if ( ( lastMessage . command === "upload_public_key" || lastMessage . command === "heartbeat" ) && lastMessage . timestamp ) {
103+ const newOffset = calculateTimeOffset ( lastMessage . timestamp ) ;
104+ timeOffset = newOffset ;
105+ timeSynced = true ;
106+ setIsTimeSynced ( true ) ;
107+ console . log ( 'Time synchronized with server via' , lastMessage . command ) ;
108+ }
109+
110+
70111 if ( typeof lastMessage . command === 'string' && lastMessage . command === "upload_public_key" && lastMessage . client_id ) {
71112 localStorage . setItem ( 'username' , lastMessage . client_id ) ;
72113 setUsername ( lastMessage . client_id ) ;
73114 setLoading ( false ) ;
74115 return ;
75116 }
76117
118+
77119 if ( lastMessage . command === 'heartbeat' ) {
78120 if ( lastMessage . error ) {
79121 localStorage . removeItem ( 'username' ) ;
80122 setUsername ( null ) ;
123+ setIsTimeSynced ( false ) ;
124+ timeSynced = false ;
81125 promptForUsername ( 'Session expired. Please re-enter your username.' ) ;
82126 }
83127 else {
@@ -88,8 +132,9 @@ export const ClientProvider = ({ children }: { children: ComponentChildren }) =>
88132 }
89133 } , [ messages ] ) ;
90134
135+
91136 useEffect ( ( ) => {
92- if ( ! username || status !== 'open' ) return ;
137+ if ( ! username || status !== 'open' || ! isTimeSynced ) return ;
93138
94139 const heartbeatInterval = setInterval ( async ( ) => {
95140 try {
@@ -104,10 +149,13 @@ export const ClientProvider = ({ children }: { children: ComponentChildren }) =>
104149 } , 25000 ) ;
105150
106151 return ( ) => clearInterval ( heartbeatInterval ) ;
107- } , [ username , status , sendMessage ] ) ;
152+ } , [ username , status , sendMessage , isTimeSynced ] ) ;
108153
109154 return (
110- < ClientContext . Provider value = { { username, loading } } >
155+ < ClientContext . Provider value = { {
156+ username,
157+ loading : loading || ! isTimeSynced
158+ } } >
111159 { children }
112160 < UsernameModal
113161 isOpen = { showUsernameModal }
0 commit comments