feat: скрывать отключённые/недоступные модули с galaxy map по feature flags
This commit is contained in:
+67
-26
@@ -335,11 +335,31 @@ const CAT_INFO = {
|
|||||||
personal: { label:'Личное', rgb:[255,179,71] },
|
personal: { label:'Личное', rgb:[255,179,71] },
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ══ Build HTML nodes ══ */
|
/* ══ Feature flag → module ID mapping ══ */
|
||||||
|
const FEAT_MAP = {
|
||||||
|
hangman: ['hangman'],
|
||||||
|
crossword: ['crossword'],
|
||||||
|
pet: ['pet'],
|
||||||
|
red_book: ['redbook'],
|
||||||
|
collection: ['collection'],
|
||||||
|
lab: ['lab'],
|
||||||
|
knowledge_map: ['knowledge'],
|
||||||
|
board: ['board'],
|
||||||
|
biochem: ['biochem'],
|
||||||
|
};
|
||||||
|
const NO_CLASS_IDS = new Set(['board','lab','hangman','crossword','pet','collection','knowledge','redbook']);
|
||||||
|
|
||||||
|
/* activeModules — updated after features load; canvas uses this for connections */
|
||||||
|
let activeModules = MODULES;
|
||||||
|
|
||||||
|
/* ══ Build HTML nodes (called after features are known) ══ */
|
||||||
const nodesWrap = document.getElementById('gx-nodes');
|
const nodesWrap = document.getElementById('gx-nodes');
|
||||||
const nodeMap = {};
|
const nodeMap = {};
|
||||||
|
|
||||||
MODULES.forEach((m, i) => {
|
function buildNodes(mods) {
|
||||||
|
nodesWrap.innerHTML = '';
|
||||||
|
Object.keys(nodeMap).forEach(k => delete nodeMap[k]);
|
||||||
|
mods.forEach((m, i) => {
|
||||||
const [r,g,b] = CAT_INFO[m.cat].rgb;
|
const [r,g,b] = CAT_INFO[m.cat].rgb;
|
||||||
const el = document.createElement('div');
|
const el = document.createElement('div');
|
||||||
el.className = 'gx-node' + (m.id === 'dashboard' ? ' gx-main' : '');
|
el.className = 'gx-node' + (m.id === 'dashboard' ? ' gx-main' : '');
|
||||||
@@ -354,10 +374,49 @@ MODULES.forEach((m, i) => {
|
|||||||
el.addEventListener('click', () => location.href = m.href);
|
el.addEventListener('click', () => location.href = m.href);
|
||||||
nodesWrap.appendChild(el);
|
nodesWrap.appendChild(el);
|
||||||
nodeMap[m.id] = el;
|
nodeMap[m.id] = el;
|
||||||
});
|
});
|
||||||
|
if (window.lucide) lucide.createIcons();
|
||||||
|
document.getElementById('gx-count').textContent = mods.length + ' модулей';
|
||||||
|
}
|
||||||
|
|
||||||
if (window.lucide) lucide.createIcons();
|
/* ══ Mobile fallback (called after features are known) ══ */
|
||||||
document.getElementById('gx-count').textContent = MODULES.length + ' модулей';
|
function buildMobile(mods) {
|
||||||
|
const mobEl = document.getElementById('gx-mobile');
|
||||||
|
mobEl.innerHTML = '';
|
||||||
|
const byCat = {};
|
||||||
|
mods.forEach(m => { (byCat[m.cat] = byCat[m.cat]||[]).push(m); });
|
||||||
|
['study','practice','games','personal'].forEach(cat => {
|
||||||
|
if (!byCat[cat]?.length) return;
|
||||||
|
const info = CAT_INFO[cat];
|
||||||
|
const [r,g,b] = info.rgb;
|
||||||
|
let html = `<div class="gx-mob-sec-title"><span class="gx-mob-dot" style="background:rgb(${r},${g},${b})"></span>${info.label}</div><div class="gx-mob-grid">`;
|
||||||
|
byCat[cat].forEach(m => {
|
||||||
|
html += `<a href="${m.href}" class="gx-mob-card" data-cat="${cat}" style="--nc:${r},${g},${b}">
|
||||||
|
<div class="gx-mob-icon"><i data-lucide="${m.icon}"></i></div>
|
||||||
|
<div><div class="gx-mob-name">${m.label}</div><div class="gx-mob-cat">${info.label}</div></div>
|
||||||
|
</a>`;
|
||||||
|
});
|
||||||
|
html += '</div>';
|
||||||
|
mobEl.insertAdjacentHTML('beforeend', html);
|
||||||
|
});
|
||||||
|
if (window.lucide) lucide.createIcons();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ══ Async init: load features, filter modules, render ══ */
|
||||||
|
(async () => {
|
||||||
|
let disabled = new Set();
|
||||||
|
try {
|
||||||
|
const feats = await LS.loadFeatures();
|
||||||
|
for (const [key, ids] of Object.entries(FEAT_MAP)) {
|
||||||
|
if (feats[key] === false) ids.forEach(id => disabled.add(id));
|
||||||
|
}
|
||||||
|
if (feats._no_class) NO_CLASS_IDS.forEach(id => disabled.add(id));
|
||||||
|
} catch { /* если не залогинен — показываем всё */ }
|
||||||
|
|
||||||
|
activeModules = disabled.size ? MODULES.filter(m => !disabled.has(m.id)) : MODULES;
|
||||||
|
buildNodes(activeModules);
|
||||||
|
buildMobile(activeModules);
|
||||||
|
})();
|
||||||
|
|
||||||
/* ══ Tooltip ══ */
|
/* ══ Tooltip ══ */
|
||||||
const tt = document.getElementById('gx-tt');
|
const tt = document.getElementById('gx-tt');
|
||||||
@@ -479,12 +538,12 @@ function drawFrame() {
|
|||||||
});
|
});
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
|
|
||||||
// Connection lines
|
// Connection lines (only between visible modules)
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.setLineDash([3, 10]);
|
ctx.setLineDash([3, 10]);
|
||||||
CONNECTIONS.forEach(([a, b]) => {
|
CONNECTIONS.forEach(([a, b]) => {
|
||||||
const ma = MODULES.find(m => m.id === a);
|
const ma = activeModules.find(m => m.id === a);
|
||||||
const mb = MODULES.find(m => m.id === b);
|
const mb = activeModules.find(m => m.id === b);
|
||||||
if (!ma || !mb) return;
|
if (!ma || !mb) return;
|
||||||
const isHovered = hoveredId && (a === hoveredId || b === hoveredId);
|
const isHovered = hoveredId && (a === hoveredId || b === hoveredId);
|
||||||
ctx.strokeStyle = isHovered ? 'rgba(255,255,255,0.38)' : 'rgba(255,255,255,0.065)';
|
ctx.strokeStyle = isHovered ? 'rgba(255,255,255,0.38)' : 'rgba(255,255,255,0.065)';
|
||||||
@@ -513,24 +572,6 @@ document.querySelectorAll('.gx-fil').forEach(btn => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/* ══ Mobile fallback: card grid ══ */
|
|
||||||
const mobEl = document.getElementById('gx-mobile');
|
|
||||||
const byCat = {};
|
|
||||||
MODULES.forEach(m => { (byCat[m.cat] = byCat[m.cat]||[]).push(m); });
|
|
||||||
['study','practice','games','personal'].forEach(cat => {
|
|
||||||
const info = CAT_INFO[cat];
|
|
||||||
const [r,g,b] = info.rgb;
|
|
||||||
let html = `<div class="gx-mob-sec-title"><span class="gx-mob-dot" style="background:rgb(${r},${g},${b})"></span>${info.label}</div><div class="gx-mob-grid">`;
|
|
||||||
(byCat[cat]||[]).forEach(m => {
|
|
||||||
html += `<a href="${m.href}" class="gx-mob-card" data-cat="${cat}" style="--nc:${r},${g},${b}">
|
|
||||||
<div class="gx-mob-icon"><i data-lucide="${m.icon}"></i></div>
|
|
||||||
<div><div class="gx-mob-name">${m.label}</div><div class="gx-mob-cat">${info.label}</div></div>
|
|
||||||
</a>`;
|
|
||||||
});
|
|
||||||
html += '</div>';
|
|
||||||
mobEl.insertAdjacentHTML('beforeend', html);
|
|
||||||
});
|
|
||||||
if (window.lucide) lucide.createIcons();
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user