From 7f85389a88eb8387e93515c522080b1d8d8a2b14 Mon Sep 17 00:00:00 2001 From: s1to Date: Tue, 4 Aug 2026 11:46:19 +0200 Subject: [PATCH] [0.9.1] Mobile: Personal/Community filter the home hive from any page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Turn the header Personal/Community tabs into links to /activity?hive=personal|community, so they work on every page (they take you to the home and filter the hexagons), not only while already on /activity — no more dead buttons off the home. - Drop the "All" tab: entering shows all 10 categories; clicking a filter that is already active toggles it off and shows all again. - Filter the hive server-side (renderHiveNav reads the request filter set by the middleware) instead of the CSS radio-hack, and drop the now-dead half-filter CSS. Active tab is highlighted via the .active class. --- src/backend/backend.js | 2 ++ src/client/assets/themes/OasisMobile.css | 15 +++------------ src/views/activity_view.js | 2 +- src/views/main_views.js | 24 +++++++++++++----------- 4 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/backend/backend.js b/src/backend/backend.js index 40acaf8..02649d1 100644 --- a/src/backend/backend.js +++ b/src/backend/backend.js @@ -8598,6 +8598,8 @@ const middleware = [ }, async (ctx, next) => { if (!ctx.path.startsWith('/assets/') && !ctx.path.startsWith('/image/') && !ctx.path.startsWith('/blob/') && !ctx.path.startsWith('/qr') && !ctx.path.startsWith('/c/')) { + // filtro del hive (Personal/Community) del home; lo leen la topbar (enlaces) y renderHiveNav (categorías mostradas) + global.__OASIS_HIVE_FILTER__ = (ctx.path === '/activity' && (ctx.query.hive === 'personal' || ctx.query.hive === 'community')) ? ctx.query.hive : ''; const now = Date.now(); if (now - sharedState.getLastRefresh() > 60000) { sharedState.setLastRefresh(now); diff --git a/src/client/assets/themes/OasisMobile.css b/src/client/assets/themes/OasisMobile.css index 036cc6a..95a4f4c 100644 --- a/src/client/assets/themes/OasisMobile.css +++ b/src/client/assets/themes/OasisMobile.css @@ -83,9 +83,7 @@ body { padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px)); } font-weight: bold; letter-spacing: 1px; cursor: pointer; } -#hive-filter-all:checked ~ .header .oasis-menu-btn[for="hive-filter-all"], -#hive-filter-personal:checked ~ .header .oasis-menu-btn[for="hive-filter-personal"], -#hive-filter-community:checked ~ .header .oasis-menu-btn[for="hive-filter-community"] { +.oasis-mobile-menus .oasis-menu-btn.active { background: #FFA500; color: #121212; border-color: #FFA500; } @@ -192,17 +190,10 @@ body { overscroll-behavior: none; } .main-content, .main-column, .header { overflow: visible; } -/* === Usabilidad 0.9.1: los tabs All/Personal/Community filtran el hive + desplegable FILTER === */ -.hive-filter-radio { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; margin: 0; } - -/* filtro por mitades: Personal oculta la mitad community, Community oculta la personal (All = todo) */ -#hive-filter-personal:checked ~ .main-content .hive-nav .hex-cat.hive-half-c { display: none; } -#hive-filter-community:checked ~ .main-content .hive-nav .hex-cat.hive-half-p { display: none; } -/* también el panel de la mitad oculta si venía abierto (#content sube la especificidad para ganar al reveal de 2 IDs, sin !important) */ -#hive-filter-personal:checked ~ .main-content #content .hive-panel.hive-half-c { display: none; } -#hive-filter-community:checked ~ .main-content #content .hive-panel.hive-half-p { display: none; } +/* === Usabilidad 0.9.1: Personal/Community filtran el hive server-side (enlaces ?hive=) + desplegable FILTER === */ /* desplegable FILTER de la ristra de filtros de Activity (oculto por defecto) */ +.activity-filter-toggle { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; margin: 0; } .activity-filter-btn { display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; min-height: 44px; margin: 4px 0 8px; padding: 10px 12px; diff --git a/src/views/activity_view.js b/src/views/activity_view.js index 165f351..184a97f 100644 --- a/src/views/activity_view.js +++ b/src/views/activity_view.js @@ -1994,7 +1994,7 @@ exports.activityView = (actions, filter, userId, q = '', extras = {}) => { p(desc) ), renderHiveNav(), - input({ type: 'checkbox', id: 'activity-filter-toggle', class: 'hive-filter-radio' }), + input({ type: 'checkbox', id: 'activity-filter-toggle', class: 'activity-filter-toggle' }), label({ for: 'activity-filter-toggle', class: 'activity-filter-btn' }, span(i18n.filter || 'Filter'), span({ class: 'activity-filter-caret' }, '▾')), div({ class: 'activity-filter-panel' }, diff --git a/src/views/main_views.js b/src/views/main_views.js index 1ec0a4e..48cac74 100644 --- a/src/views/main_views.js +++ b/src/views/main_views.js @@ -1239,17 +1239,20 @@ const renderHiveNav = () => { { id: 'fediverse', emoji: '⌬', title: i18n.fediverse, mods: [ { href: '/fediverse', glyph: 'ꗵ', labelKey: 'fediverseTimeline', mod: 'fediverseMod' } ] }, ]; - const half = (c) => (CATS.indexOf(c) < 5 ? 'hive-half-p' : 'hive-half-c'); - const catHex = (c) => label({ for: `hive-${c.id}`, class: `hex-cat ${half(c)}` }, + // filtro server-side (lo pone el middleware desde ?hive=): personal = CATS[0..4], community = CATS[5..9], vacío = las 10 + const hiveFilter = (typeof global !== "undefined" && global.__OASIS_HIVE_FILTER__) || ""; + const shown = hiveFilter === "personal" ? CATS.slice(0, 5) : hiveFilter === "community" ? CATS.slice(5) : CATS; + const rowSizes = shown.length <= 5 ? [3, 2] : [3, 4, 3]; + const catHex = (c) => label({ for: `hive-${c.id}`, class: 'hex-cat' }, span({ class: 'hx-ico' }, c.emoji), span({ class: 'hx-lbl' }, c.title)); const modHex = (m) => a({ class: 'hex-mod', href: m.href }, span({ class: 'hx-ico' }, m.glyph), span({ class: 'hx-lbl' }, i18n[m.labelKey] || '')); - const radios = CATS.map(c => input({ type: 'radio', name: 'hive-cat', id: `hive-${c.id}`, class: 'hive-radio' })); + const radios = shown.map(c => input({ type: 'radio', name: 'hive-cat', id: `hive-${c.id}`, class: 'hive-radio' })); const catRows = []; let idx = 0; - for (const size of [3, 4, 3]) { catRows.push(div({ class: 'hive-row' }, ...CATS.slice(idx, idx + size).map(catHex))); idx += size; } + for (const size of rowSizes) { catRows.push(div({ class: 'hive-row' }, ...shown.slice(idx, idx + size).map(catHex))); idx += size; } const grid = div({ class: 'hive-grid hive-cats' }, ...catRows); - const panels = CATS.map(c => div({ class: `hive-panel ${half(c)}`, id: `hive-panel-${c.id}` }, + const panels = shown.map(c => div({ class: 'hive-panel', id: `hive-panel-${c.id}` }, div({ class: 'hive-mods' }, ...c.mods.filter(m => modOn(m.mod)).map(modHex)) )); return div({ class: 'hive-nav' }, ...radios, grid, ...panels); @@ -1260,6 +1263,7 @@ const template = (titlePrefix, ...elements) => { const currentConfig = getConfig(); const theme = currentConfig.themes.current || "Dark-SNH"; const uxMode = currentConfig.ux?.current === "ainav" ? "ainav" : "blocks"; + const hiveFilter = (typeof global !== "undefined" && global.__OASIS_HIVE_FILTER__) || ""; const themeLink = link({ rel: "stylesheet", href: `/assets/themes/${theme}.css` @@ -1285,17 +1289,15 @@ const template = (titlePrefix, ...elements) => { }) ), body( - input({ type: "radio", name: "hive-filter", id: "hive-filter-all", class: "hive-filter-radio", checked: true }), - input({ type: "radio", name: "hive-filter", id: "hive-filter-personal", class: "hive-filter-radio" }), - input({ type: "radio", name: "hive-filter", id: "hive-filter-community", class: "hive-filter-radio" }), div( { class: uxMode === "ainav" ? "header ainav-only" : "header" }, renderMobileTopbar(), div( { class: "oasis-mobile-menus" }, - label({ for: "hive-filter-all", class: "oasis-menu-btn" }, span(i18n.menuAll || "All")), - label({ for: "hive-filter-personal", class: "oasis-menu-btn" }, span(i18n.menuPersonal || "Personal")), - label({ for: "hive-filter-community", class: "oasis-menu-btn" }, span(i18n.menuCommunity || "Community")) + // enlaces (no checkboxes): funcionan en CUALQUIER página → navegan al home /activity con el filtro. + // toggle: si el filtro ya está activo, el enlace lleva a /activity (se deselecciona → todos) + a({ class: hiveFilter === "personal" ? "oasis-menu-btn active" : "oasis-menu-btn", href: hiveFilter === "personal" ? "/activity" : "/activity?hive=personal" }, span(i18n.menuPersonal || "Personal")), + a({ class: hiveFilter === "community" ? "oasis-menu-btn active" : "oasis-menu-btn", href: hiveFilter === "community" ? "/activity" : "/activity?hive=community" }, span(i18n.menuCommunity || "Community")) ), (() => { const aiNavOn = getConfig().modules.aiNavMod === 'on';