fix: collection source suppression + back button + home click
Collection source:
- Collection list: don't default to @author as source label
- Collection detail: suppress SOURCE when it equals @author
- Collection detail: 'curated by' instead of 'authored by'
- No source shown when field is empty
Back button:
- isRouting guard on route() prevents showDetail, showCollectionDetail,
showUser, and navigate from setting location.hash when called from
a hashchange event (back button). This prevents duplicate history
entries that trap the user.
Home click:
- navigate('library') no longer blocked by currentRoute guard
(view !== 'library' exception), so clicking the site name always
returns to the homepage.
This commit is contained in:
+19
-11
@@ -63,6 +63,7 @@ async function init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function route() {
|
function route() {
|
||||||
|
isRouting = true;
|
||||||
const hash = location.hash.slice(1) || 'library';
|
const hash = location.hash.slice(1) || 'library';
|
||||||
if (hash.startsWith('protocols/')) {
|
if (hash.startsWith('protocols/')) {
|
||||||
showDetail(hash.split('/')[1]);
|
showDetail(hash.split('/')[1]);
|
||||||
@@ -81,14 +82,15 @@ function route() {
|
|||||||
} else {
|
} else {
|
||||||
navigate('library');
|
navigate('library');
|
||||||
}
|
}
|
||||||
|
isRouting = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let searchTimer = null;
|
let searchTimer = null;
|
||||||
let currentRoute = '';
|
let currentRoute = '';
|
||||||
|
let isRouting = false;
|
||||||
|
|
||||||
function navigate(view) {
|
function navigate(view) {
|
||||||
// Prevent double navigation from hashchange
|
if (currentRoute === view && view !== 'library') return;
|
||||||
if (currentRoute === view) return;
|
|
||||||
currentRoute = view;
|
currentRoute = view;
|
||||||
document.querySelectorAll('.view').forEach(v => v.style.display = 'none');
|
document.querySelectorAll('.view').forEach(v => v.style.display = 'none');
|
||||||
document.getElementById('view-' + view).style.display = 'block';
|
document.getElementById('view-' + view).style.display = 'block';
|
||||||
@@ -96,11 +98,15 @@ function navigate(view) {
|
|||||||
document.querySelectorAll('.mobile-panel a').forEach(a => a.classList.toggle('active', a.dataset.view === view));
|
document.querySelectorAll('.mobile-panel a').forEach(a => a.classList.toggle('active', a.dataset.view === view));
|
||||||
closeMenu();
|
closeMenu();
|
||||||
updateBreadcrumb(view);
|
updateBreadcrumb(view);
|
||||||
if (view === 'library') { location.hash = 'library'; loadProtocols(); }
|
if (!isRouting) {
|
||||||
if (view === 'collections') { location.hash = 'collections'; loadCollections(); }
|
if (view === 'library') location.hash = 'library';
|
||||||
|
if (view === 'collections') location.hash = 'collections';
|
||||||
|
if (view === 'about') location.hash = 'about';
|
||||||
|
}
|
||||||
|
if (view === 'library') loadProtocols();
|
||||||
|
if (view === 'collections') loadCollections();
|
||||||
if (view === 'create') { editingSlug = null; resetForm(); }
|
if (view === 'create') { editingSlug = null; resetForm(); }
|
||||||
if (view === 'create-collection') { editingCollectionSlug = null; resetCollectionForm(); }
|
if (view === 'create-collection') { editingCollectionSlug = null; resetCollectionForm(); }
|
||||||
if (view === 'about') { location.hash = 'about'; }
|
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,7 +319,7 @@ function renderProtocolGrid(protocols) {
|
|||||||
|
|
||||||
// --- Detail ---
|
// --- Detail ---
|
||||||
async function showDetail(slug) {
|
async function showDetail(slug) {
|
||||||
location.hash = 'protocols/' + slug;
|
if (!isRouting && location.hash !== '#protocols/' + slug) location.hash = 'protocols/' + slug;
|
||||||
document.querySelectorAll('.view').forEach(v => v.style.display = 'none');
|
document.querySelectorAll('.view').forEach(v => v.style.display = 'none');
|
||||||
document.getElementById('view-detail').style.display = 'block';
|
document.getElementById('view-detail').style.display = 'block';
|
||||||
document.querySelectorAll('header nav a').forEach(a => a.classList.remove('active'));
|
document.querySelectorAll('header nav a').forEach(a => a.classList.remove('active'));
|
||||||
@@ -564,10 +570,11 @@ async function loadCollections() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
grid.innerHTML = collections.map(function(c) {
|
grid.innerHTML = collections.map(function(c) {
|
||||||
var sourceLabel = c.author ? '@' + c.author : (c.source || '');
|
var sourceLabel = c.source || '';
|
||||||
return '<div class="protocol-pill collection" onclick="showCollectionDetail(\'' + c.slug + '\')">' +
|
return '<div class="protocol-pill collection" onclick="showCollectionDetail(\'' + c.slug + '\')">' +
|
||||||
'<div class="info"><h3>' + escapeHtml(c.title) + '</h3><p>' + escapeHtml(c.description || '') + '</p></div>' +
|
'<div class="info"><h3>' + escapeHtml(c.title) + '</h3><p>' + escapeHtml(c.description || '') + '</p></div>' +
|
||||||
'<div class="meta"><div class="steps">' + (c.item_count || 0) + ' protocols</div><div>' + escapeHtml(sourceLabel) + '</div></div></div>';
|
'<div class="meta"><div class="steps">' + (c.item_count || 0) + ' protocols</div>' +
|
||||||
|
(sourceLabel ? '<div>' + escapeHtml(sourceLabel) + '</div>' : '') + '</div></div>';
|
||||||
}).join('');
|
}).join('');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
document.getElementById('collectionsGrid').innerHTML = '<div class="empty">' + escapeHtml(e.message) + '</div>';
|
document.getElementById('collectionsGrid').innerHTML = '<div class="empty">' + escapeHtml(e.message) + '</div>';
|
||||||
@@ -713,7 +720,7 @@ async function saveCollection(e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function showCollectionDetail(slug) {
|
async function showCollectionDetail(slug) {
|
||||||
location.hash = 'collections/' + slug;
|
if (!isRouting && location.hash !== '#collections/' + slug) location.hash = 'collections/' + slug;
|
||||||
document.querySelectorAll('.view').forEach(v => v.style.display = 'none');
|
document.querySelectorAll('.view').forEach(v => v.style.display = 'none');
|
||||||
document.getElementById('view-collection-detail').style.display = 'block';
|
document.getElementById('view-collection-detail').style.display = 'block';
|
||||||
document.querySelectorAll('header nav a').forEach(a => a.classList.remove('active'));
|
document.querySelectorAll('header nav a').forEach(a => a.classList.remove('active'));
|
||||||
@@ -739,8 +746,8 @@ async function showCollectionDetail(slug) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var sourceLine = c.source ? 'SOURCE: ' + escapeHtml(c.source) : '';
|
var sourceLine = (c.source && c.source !== '@' + (c.author || '')) ? 'SOURCE: ' + escapeHtml(c.source) : '';
|
||||||
var authorLine = c.author ? ' · authored by <a href="#user/' + c.author + '" onclick="showUser(\'' + c.author + '\');return false">@' + escapeHtml(c.author) + '</a>' : '';
|
var authorLine = c.author ? (sourceLine ? ' · ' : '') + 'curated by <a href="#users/' + c.author + '" onclick="showUser(\'' + c.author + '\');return false">@' + escapeHtml(c.author) + '</a>' : '';
|
||||||
var dateLine = c.created_at ? ' · added ' + formatDate(c.created_at) : '';
|
var dateLine = c.created_at ? ' · added ' + formatDate(c.created_at) : '';
|
||||||
|
|
||||||
document.getElementById('collectionDetailContent').innerHTML =
|
document.getElementById('collectionDetailContent').innerHTML =
|
||||||
@@ -786,6 +793,7 @@ async function deleteCollection(slug) {
|
|||||||
|
|
||||||
// --- User Profile ---
|
// --- User Profile ---
|
||||||
async function showUser(username) {
|
async function showUser(username) {
|
||||||
|
if (!isRouting && location.hash !== '#users/' + username) location.hash = 'users/' + username;
|
||||||
document.querySelectorAll('.view').forEach(v => v.style.display = 'none');
|
document.querySelectorAll('.view').forEach(v => v.style.display = 'none');
|
||||||
document.getElementById('view-detail').style.display = 'block';
|
document.getElementById('view-detail').style.display = 'block';
|
||||||
document.querySelectorAll('header nav a').forEach(a => a.classList.remove('active'));
|
document.querySelectorAll('header nav a').forEach(a => a.classList.remove('active'));
|
||||||
|
|||||||
Reference in New Issue
Block a user