@@ -104,22 +104,29 @@ document.addEventListener("DOMContentLoaded", () => {
104104 alternate : 'HOME'
105105 } ;
106106
107+ const CONTACT_HASH = '#contact' ;
108+
107109 // --- Selectors ---
108- const toggleBtn = document . querySelector ( 'sub a[href="#"]' ) ;
110+ const toggleBtn = document . querySelector ( 'sub a[href="#contact "]' ) ;
109111 const mainBlockquote = document . querySelector ( 'blockquote' ) ;
110112 const paperItems = document . querySelectorAll ( '.paper-item' ) ;
111113 const footer = document . querySelector ( 'footer' ) ;
112114
115+ // If there's no CONTACT link (or markup changed), skip the contact-view feature.
116+ if ( ! toggleBtn ) {
117+ return ;
118+ }
119+
113120 // --- Inject CSS class for hiding elements ---
114121 const style = document . createElement ( 'style' ) ;
115122 style . textContent = `.hidden-view { display: none !important; }` ;
116123 document . head . appendChild ( style ) ;
117124
118125 function escapeHtml ( str ) {
119- const div = document . createElement ( 'div' ) ;
120- div . textContent = str ;
121- return div . innerHTML ;
122- }
126+ const div = document . createElement ( 'div' ) ;
127+ div . textContent = str ;
128+ return div . innerHTML ;
129+ }
123130
124131 // --- Build email block from data ---
125132 function createEmailBlockquote ( { active, inactive, error = false } ) {
@@ -183,15 +190,20 @@ document.addEventListener("DOMContentLoaded", () => {
183190 return emailBlockquote ;
184191 }
185192
186- // --- Toggle view handler ---
187- async function toggleView ( event ) {
188- event . preventDefault ( ) ;
193+ function isContactViewFromHash ( ) {
194+ return window . location . hash . toLowerCase ( ) === CONTACT_HASH ;
195+ }
196+
197+ function setHidden ( el , hidden ) {
198+ if ( ! el ) return ;
199+ el . classList . toggle ( 'hidden-view' , hidden ) ;
200+ }
201+
202+ function startEmailFetchIfNeeded ( ) {
203+ if ( emailDataPromise ) return ;
204+
205+ toggleBtn . textContent = '...' ;
189206
190- // If we're about to show emails and haven't loaded them yet, fetch now
191- if ( ! showingEmails && ! emailDataPromise ) {
192- // Show a temporary loading state (optional)
193- toggleBtn . textContent = '...' ;
194-
195207 const timeout = new Promise ( ( _ , reject ) =>
196208 setTimeout ( ( ) => reject ( new Error ( 'Request timed out' ) ) , 3000 )
197209 ) ;
@@ -202,32 +214,54 @@ document.addEventListener("DOMContentLoaded", () => {
202214 return response . json ( ) ;
203215 } ) ,
204216 timeout
205- ] )
206- . catch ( error => {
217+ ] ) . catch ( error => {
207218 console . error ( 'Could not load emails:' , error ) ;
208219 return { active : [ ] , inactive : [ ] , error : true } ;
209220 } ) ;
210- }
221+ }
211222
212- // Wait for the data if we're opening the view for the first time
213- if ( ! showingEmails && emailDataPromise ) {
223+ let viewSyncId = 0 ;
224+
225+ async function syncContactViewFromHash ( ) {
226+ const syncId = ++ viewSyncId ;
227+ const shouldShowEmails = isContactViewFromHash ( ) ;
228+
229+ if ( shouldShowEmails ) {
230+ startEmailFetchIfNeeded ( ) ;
214231 const emailData = await emailDataPromise ;
215232 insertEmailBlock ( emailData ) ;
216233 }
217234
218- // Toggle visibility
219- if ( emailBlockquote ) {
220- emailBlockquote . classList . toggle ( 'hidden-view' ) ;
221- }
222- elementsToToggle . forEach ( el => el . classList . toggle ( 'hidden-view' ) ) ;
235+ // If another hash change happened while we were awaiting, ignore this pass.
236+ if ( syncId !== viewSyncId ) return ;
237+
238+ // Apply visibility deterministically (avoid toggle drift)
239+ elementsToToggle . forEach ( el => setHidden ( el , shouldShowEmails ) ) ;
240+ setHidden ( emailBlockquote , ! shouldShowEmails ) ;
241+
242+ toggleBtn . textContent = shouldShowEmails ? toggleText . alternate : toggleText . default ;
243+ showingEmails = shouldShowEmails ;
244+ }
223245
224- // Update button text
225- toggleBtn . textContent = showingEmails ? toggleText . default : toggleText . alternate ;
246+ // --- Click handler: drives state via URL hash ---
247+ function onToggleClick ( event ) {
248+ event . preventDefault ( ) ;
226249
227- // Flip state
228- showingEmails = ! showingEmails ;
250+ if ( isContactViewFromHash ( ) ) {
251+ window . location . hash = '' ;
252+ } else {
253+ window . location . hash = CONTACT_HASH ;
254+ }
229255 }
230256
257+ // Keep the view in sync with back/forward and direct links.
258+ window . addEventListener ( 'hashchange' , ( ) => {
259+ syncContactViewFromHash ( ) ;
260+ } ) ;
261+
262+ // Initial sync (supports landing on /#contact)
263+ syncContactViewFromHash ( ) ;
264+
231265 // --- Attach event listener ---
232- toggleBtn . addEventListener ( 'click' , toggleView ) ;
266+ toggleBtn . addEventListener ( 'click' , onToggleClick ) ;
233267} ) ;
0 commit comments