fuzzyworld

word-waltz / commit

share pasta

d3fd1d8a92faa2292bab336f3df3cba2b8c1c573 · Pavel Safronov · 2026-03-26 21:13:19 -0700 · parents 58ec0f1f4f9f

share pasta
 src/main.ts | 41 ++++++++++++++++++++---------------------
 1 file changed, 20 insertions(+), 21 deletions(-)

diff --git a/src/main.ts b/src/main.ts
index 67ed717..a859762 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -105,23 +105,36 @@ function navigateToGame(gameId: string, key: string): void {
 
 let shareModal: HTMLElement | null = null;
 
-function setShareButton(inviteUrl: string | null): void {
+function updateShareButton(): void {
     const btn = document.getElementById('share-btn') as HTMLButtonElement | null;
     if (!btn) return;
 
-    if (!inviteUrl) {
+    const { gameId, key } = parseLink();
+    if (!gameId || !key) {
         btn.hidden = true;
         btn.onclick = null;
         return;
     }
 
+    const inviteUrl = buildLink(gameId, key, 'b');
+
+    if (!btn.hidden && btn.querySelector('img')) {
+        // Already set up — just refresh the onclick with the current URL
+        btn.onclick = openShareModal(inviteUrl);
+        return;
+    }
+
     btn.textContent = '';
     const shareImg = document.createElement('img');
     shareImg.src = urlIconUrl;
     shareImg.alt = 'Share';
     btn.appendChild(shareImg);
     btn.hidden = false;
-    btn.onclick = () => {
+    btn.onclick = openShareModal(inviteUrl);
+}
+
+function openShareModal(inviteUrl: string): () => void {
+    return () => {
         if (shareModal) { shareModal.remove(); shareModal = null; return; }
 
         const overlay = document.createElement('div');
@@ -342,7 +355,6 @@ function showSoloGame(app: HTMLElement): void {
     unsubscribeGame = null;
     cleanupView?.();
     cleanupView = null;
-    setShareButton(null);
     app.innerHTML = '';
 
     let state = loadSoloGame();
@@ -421,7 +433,6 @@ function showSoloGame(app: HTMLElement): void {
 // ---------------------------------------------------------------------------
 
 function showHomeScreen(app: HTMLElement): void {
-    setShareButton(null);
     app.innerHTML = '';
 
     const root = document.createElement('div');
@@ -718,7 +729,6 @@ function promptPlayerBName(
 // ---------------------------------------------------------------------------
 
 function showError(app: HTMLElement, message: string): void {
-    setShareButton(null);
     app.innerHTML = '';
     const p = document.createElement('p');
     p.className = 'error';
@@ -730,10 +740,7 @@ function showError(app: HTMLElement, message: string): void {
 // Bootstrap
 // ---------------------------------------------------------------------------
 
-let bootstrapSeq = 0;
-
 async function bootstrap(): Promise<void> {
-    const mySeq = ++bootstrapSeq;
     const app = document.getElementById('app');
     if (!app) throw new Error('#app element not found');
 
@@ -803,12 +810,6 @@ async function bootstrap(): Promise<void> {
 
     const role = myRole as PlayerId;
     const shareUrl = buildLink(gameId, key);
-    const inviteUrl = buildLink(gameId, key, 'b');
-
-    // Another navigation happened while we were awaiting — bail out.
-    if (mySeq !== bootstrapSeq) return;
-
-    setShareButton(inviteUrl);
 
     // Register the service worker and subscribe to push notifications.
     // Fire-and-forget: push is a best-effort enhancement.
@@ -850,14 +851,13 @@ async function bootstrap(): Promise<void> {
 // full page reload. This works regardless of the deployment base path.
 document.getElementById('home-link')?.addEventListener('click', (e) => {
     e.preventDefault();
-    ++bootstrapSeq;
     unsubscribeGame?.();
     unsubscribeGame = null;
     cleanupView?.();
     cleanupView = null;
     clearNotification();
-    setShareButton(null);
     history.replaceState(null, '', window.location.pathname);
+    updateShareButton();
     const app = document.getElementById('app');
     if (app) showHomeScreen(app);
 });
@@ -867,15 +867,13 @@ document.addEventListener('visibilitychange', () => {
     if (document.visibilityState === 'visible') clearNotification();
 });
 
-// Re-run bootstrap whenever the fragment changes (e.g. after game creation
-// navigates to the game URL — a hash-only change doesn't reload the page).
+// Re-run bootstrap whenever the fragment changes.
 window.addEventListener('hashchange', () => {
-    ++bootstrapSeq;
     unsubscribeGame?.();
     unsubscribeGame = null;
     cleanupView?.();
     cleanupView = null;
-    setShareButton(null);
+    updateShareButton();
     bootstrap().catch((err: unknown) => {
         console.error('Bootstrap failed:', err);
         const app = document.getElementById('app');
@@ -883,6 +881,7 @@ window.addEventListener('hashchange', () => {
     });
 });
 
+updateShareButton();
 bootstrap().catch((err: unknown) => {
     console.error('Bootstrap failed:', err);
     const app = document.getElementById('app');