diff --git a/index.html b/index.html index f6dcab1..778e47f 100644 --- a/index.html +++ b/index.html @@ -1894,7 +1894,6 @@

Related resources

// Cleanup detail view state const giscusContainer = document.getElementById('giscusContainer'); if (giscusContainer) giscusContainer.innerHTML = ''; - giscusClearPending(); giscusState.slug = null; document.getElementById('catalogView').classList.remove('d-none'); @@ -2314,38 +2313,28 @@

Browse all 54 scripts →

parseTextBlock(source.slice(cursor)); } - // Tracks the giscus embed so we can work around giscus bug #1312: - // the FIRST reaction on a resource creates the backing Discussion but giscus - // fails to refetch, so the 👍 visually "reverts". We detect that the discussion - // does not exist yet (via emit-metadata) and, once the user interacts, re-mount - // giscus to pull the freshly-created discussion + reaction so it sticks. - // existedAtMount: whether a backing discussion already existed the first time - // giscus reported metadata for the current slug (null = not yet known). It lets us - // tell a pre-existing discussion apart from one the user just created by reacting. - // speculativeRefetch: true when the current refetch was triggered by iframe focus - // (blur fallback) rather than an observed discussion creation, so we can re-arm if - // it turns out no discussion was actually created. - const giscusState = { slug: null, hasDiscussion: false, hasReactions: false, refetched: false, existedAtMount: null, speculativeRefetch: false, pendingTimer: null }; - - function giscusClearPending() { - if (giscusState.pendingTimer) { clearTimeout(giscusState.pendingTimer); giscusState.pendingTimer = null; } - } - - function mountGiscus(slug, theme, isRefetch) { + // giscus like/reaction persistence — DELIBERATELY no remount workaround. + // + // History: giscus bug #1312 makes the FIRST reaction on a resource (the one that + // creates the backing Discussion) appear to visually "revert" in-session. Two earlier + // fixes tried to work around this by REMOUNTING the giscus iframe (container.innerHTML='') + // when emit-metadata reported the discussion existed. Server-side evidence proved those + // remounts were DESTROYING reactions: the remount fired while the addReaction mutation was + // still in flight and tore down the iframe before the commit landed, leaving discussions + // with zero reactions. giscus persists reactions natively server-side with no help from us + // (a resource reacted-to before any workaround shipped kept its 👍 perfectly). + // + // Any metadata-driven remount races giscus's own optimistic reactionCount update, so there + // is no safe signal to remount on. We therefore do nothing: reactions persist natively and + // show correctly on reload. Do NOT reintroduce a remount here — it reopens #1312-by-us and + // silently drops likes. + const giscusState = { slug: null }; + + function mountGiscus(slug, theme) { const container = document.getElementById('giscusContainer'); if (!container) return; container.innerHTML = ''; - giscusClearPending(); giscusState.slug = slug; - if (isRefetch) { - giscusState.refetched = true; - } else { - giscusState.hasDiscussion = false; - giscusState.hasReactions = false; - giscusState.refetched = false; - giscusState.existedAtMount = null; - giscusState.speculativeRefetch = false; - } const script = document.createElement('script'); script.src = "https://giscus.app/client.js"; script.setAttribute("data-repo", "microsoft/FastTrack"); @@ -2356,7 +2345,6 @@

Browse all 54 scripts →

script.setAttribute("data-term", slug); script.setAttribute("data-strict", "1"); script.setAttribute("data-reactions-enabled", "1"); - script.setAttribute("data-emit-metadata", "1"); script.setAttribute("data-input-position", "bottom"); script.setAttribute("data-theme", theme === "dark" ? "dark_dimmed" : "light"); script.setAttribute("data-lang", "en"); @@ -2366,62 +2354,6 @@

Browse all 54 scripts →

container.appendChild(script); } - // Learn whether a backing discussion exists for the open resource. giscus emits - // metadata on load and whenever the discussion changes; an empty id means "not - // created yet", so a reaction would hit the #1312 create-and-revert path. - window.addEventListener('message', (event) => { - if (event.origin !== 'https://giscus.app') return; - const payload = event.data && event.data.giscus; - if (!payload || !('discussion' in payload)) return; - const d = payload.discussion; - const exists = !!(d && d.id); - const reactionCount = (d && typeof d.reactionCount === 'number') ? d.reactionCount : 0; - giscusState.hasDiscussion = exists; - // A discussion with zero reactions is "empty" — either brand new, or a #1312 "ghost" - // (created by an earlier first-reaction that reverted). Both need remount recovery; - // only a discussion that already has reactions is treated as active and left alone. - giscusState.hasReactions = exists && reactionCount > 0; - // Record whether a discussion already existed the first time giscus reported in. - // Kept as a standalone `if` (not part of the chain below) so the re-arm branch can - // still run on the same message even when this is the first metadata we receive. - if (giscusState.existedAtMount === null) { - giscusState.existedAtMount = exists; - } - if (exists && giscusState.existedAtMount === false && !giscusState.refetched) { - // The discussion just came into existence — the user's first reaction created it - // (#1312). giscus won't refetch on its own, so remount once to make the 👍 stick. - giscusState.speculativeRefetch = false; - mountGiscus(giscusState.slug, document.documentElement.getAttribute('data-theme'), true); - } else if (!giscusState.hasReactions && giscusState.refetched && giscusState.speculativeRefetch) { - // A blur-triggered (speculative) refetch happened but there are still no reactions, - // so it was "wasted" (the iframe focus was a sign-in/comment, not a reaction). - // Re-arm recovery so a later genuine first reaction can still be refetched. - giscusState.refetched = false; - } - }); - - // When the user clicks into the giscus iframe (e.g. to react) on a resource whose - // discussion has no reactions yet, schedule a single re-mount so the reaction is - // refetched instead of reverting. - window.addEventListener('blur', () => { - setTimeout(() => { - const active = document.activeElement; - const inGiscus = active && active.tagName === 'IFRAME' && active.classList.contains('giscus-frame'); - if (!inGiscus) return; - if (!giscusState.slug || giscusState.hasReactions || giscusState.refetched || giscusState.pendingTimer) return; - giscusState.pendingTimer = setTimeout(() => { - giscusState.pendingTimer = null; - // Do NOT re-check hasDiscussion here: by now the reaction may have (just) created - // the discussion, which is exactly the case that needs a refetch. A remount is - // harmless (it only refetches current server state) and must fire so the 👍 sticks. - // Mark it speculative so we re-arm if it turns out no discussion was created. - if (!giscusState.slug || giscusState.refetched) return; - giscusState.speculativeRefetch = true; - mountGiscus(giscusState.slug, document.documentElement.getAttribute('data-theme'), true); - }, 3000); - }, 0); - }); - function renderDetailView(resource) { document.getElementById('catalogView').classList.add('d-none'); document.getElementById('detailView').classList.remove('d-none');