From 734b67cbc7884de3127cef185396c5241028a028 Mon Sep 17 00:00:00 2001 From: Jukka Lampikoski Date: Sun, 8 Mar 2026 14:31:10 +0200 Subject: [PATCH] Add shareable URLs for posts with copy link button Hash-based URLs (#resepti/slug) allow sharing individual posts on social media. Modal shows "Kopioi linkki" button, hash auto-opens post on page load. Co-Authored-By: Claude Opus 4.6 --- script.js | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/script.js b/script.js index fb573ae..cbd7a9b 100644 --- a/script.js +++ b/script.js @@ -84,6 +84,16 @@ function applyTranslations() { // =========================== // HELPERS // =========================== +function postSlug(title) { + return title.toLowerCase() + .replace(/[äå]/g, 'a').replace(/ö/g, 'o').replace(/ü/g, 'u') + .replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''); +} + +function findPostBySlug(slug) { + return APP.posts.find(p => postSlug(p.title) === slug); +} + function sortSubcategories(subs) { return [...subs].sort((a, b) => { const aM = a.fi.toLowerCase() === 'muut' ? 1 : 0; @@ -343,6 +353,7 @@ async function openPost(id) { ${bodyHTML}
@@ -365,6 +376,9 @@ async function openPost(id) { document.getElementById('modalOverlay').classList.add('open'); document.body.style.overflow = 'hidden'; + // Update URL hash for shareable link + history.replaceState(null, '', '#resepti/' + postSlug(p.title)); + const qEl = document.getElementById('captcha-question'); if (qEl) qEl.textContent = generateCaptcha(); } @@ -420,9 +434,19 @@ async function submitComment(e, postId) { if (headEl) headEl.textContent = `${t('modal_comments')} (${comments.length})`; } +function copyPostLink(slug) { + const url = window.location.origin + window.location.pathname + '#resepti/' + slug; + navigator.clipboard.writeText(url).then(() => { + const btn = document.querySelector('.copy-link-btn'); + if (btn) { btn.textContent = '✓ Kopioitu!'; setTimeout(() => { btn.textContent = '🔗 Kopioi linkki'; }, 2000); } + }); +} + function closeModal() { document.getElementById('modalOverlay').classList.remove('open'); document.body.style.overflow = ''; + // Clear URL hash + history.replaceState(null, '', window.location.pathname + window.location.search); } // =========================== @@ -896,6 +920,26 @@ async function init() { renderCategoryFilters(); renderSubFilters(); renderCards(); + + // Open post from URL hash (e.g. #resepti/pinaattiletut) + const hash = window.location.hash; + if (hash.startsWith('#resepti/')) { + const slug = hash.slice('#resepti/'.length); + const post = findPostBySlug(slug); + if (post) openPost(post.id); + } } +// Handle browser back/forward with hash +window.addEventListener('hashchange', () => { + const hash = window.location.hash; + if (hash.startsWith('#resepti/')) { + const slug = hash.slice('#resepti/'.length); + const post = findPostBySlug(slug); + if (post) openPost(post.id); + } else { + closeModal(); + } +}); + init();