From c088d176c148bc1973c60066e40d77c1d2e30fdf Mon Sep 17 00:00:00 2001 From: s1to Date: Tue, 4 Aug 2026 11:07:48 +0200 Subject: [PATCH] [0.9.1] Mobile usability: filter hexagons by tab, collapse activity filters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Personal/Community/All header tabs now filter the home hexagon nav (default All shows the 10; Personal/Community show their 5) via the CSS radio-hack. Removed the square-accordion sidebars (.sidebar-left/.sidebar-right) they replaced. - Wrap the dense activity filter strip (RECENT/ALL/MINE/…) in a collapsible FILTER dropdown, hidden by default, keeping the hexagons visible above. - Add the missing English i18n keys (menuCommunity, menuAll, filter, bb*). --- src/client/assets/themes/OasisMobile.css | 40 +++-- src/client/assets/translations/oasis_en.js | 13 ++ src/views/activity_view.js | 47 ++--- src/views/main_views.js | 197 ++------------------- 4 files changed, 79 insertions(+), 218 deletions(-) diff --git a/src/client/assets/themes/OasisMobile.css b/src/client/assets/themes/OasisMobile.css index b412861..036cc6a 100644 --- a/src/client/assets/themes/OasisMobile.css +++ b/src/client/assets/themes/OasisMobile.css @@ -83,14 +83,12 @@ body { padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px)); } font-weight: bold; letter-spacing: 1px; cursor: pointer; } -#nav-left-toggle:checked ~ .header .oasis-menu-btn[for="nav-left-toggle"] { +#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"] { background: #FFA500; color: #121212; border-color: #FFA500; } -#nav-right-toggle:checked ~ .header .oasis-menu-btn[for="nav-right-toggle"] { - background: #FFD700; color: #121212; border-color: #FFD700; -} - :root { --space-1: 4px; --space-2: 8px; --space-3: 16px; --space-4: 24px; --space-5: 32px; --fs-caption: 12px; --fs-body: 14px; --fs-body-lg: 16px; --fs-title: 20px; @@ -192,12 +190,34 @@ a, button, label, .tap, .oasis-menu-btn, .bb-item, .oasis-nav-header, .oasis-nav } body { overscroll-behavior: none; } -.main-content, .main-column, .header, -.sidebar-left, .sidebar-right { overflow: visible; } +.main-content, .main-column, .header { overflow: visible; } -.sidebar-left, .sidebar-right { display: none; } -#nav-left-toggle:checked ~ .main-content .sidebar-left { display: block; } -#nav-right-toggle:checked ~ .main-content .sidebar-right { display: block; } +/* === 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; } + +/* desplegable FILTER de la ristra de filtros de Activity (oculto por defecto) */ +.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; + border: 1.5px solid #333; border-radius: 10px; + background: #121212; color: #ccc; + font-weight: bold; letter-spacing: 1px; text-transform: uppercase; cursor: pointer; +} +.activity-filter-caret { transition: transform 150ms ease; } +#activity-filter-toggle:checked ~ .activity-filter-btn { background: #FFA500; color: #121212; border-color: #FFA500; } +#activity-filter-toggle:checked ~ .activity-filter-btn .activity-filter-caret { transform: rotate(180deg); } +.activity-filter-panel { display: none; } +#activity-filter-toggle:checked ~ .activity-filter-panel { display: block; } +.activity-filter-panel .activity-filter-grid { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; margin-top: 8px; } +.activity-filter-panel .activity-filter-col { display: contents; } +.activity-filter-panel form { margin: 0; } .bb-manage { margin: 8px 0 16px; } .bb-manage h3, .bb-manage-add h3 { diff --git a/src/client/assets/translations/oasis_en.js b/src/client/assets/translations/oasis_en.js index 9af65c7..6b8c9d8 100644 --- a/src/client/assets/translations/oasis_en.js +++ b/src/client/assets/translations/oasis_en.js @@ -387,6 +387,19 @@ module.exports = { menuEconomy: "Economy", menuMedia: "Media", menuTools: "Tools", + menuCommunity: "Community", + menuAll: "All", + filter: "Filter", + bbAdd: "Add", + bbManage: "Edit", + bbRemove: "Remove", + bbQuickActions: "Quick actions", + bbYourBar: "Your bar", + bbFull: "Maximum reached", + bbDone: "Done", + bbPickTitle: "Customize the bar", + bbPickDesc: "Choose up to 4 shortcuts for the bottom bar.", + bbPickNone: "None", comment: "Comment", subtopic: "Subtopic", json: "JSON", diff --git a/src/views/activity_view.js b/src/views/activity_view.js index 5be2861..165f351 100644 --- a/src/views/activity_view.js +++ b/src/views/activity_view.js @@ -1,4 +1,4 @@ -const { div, h2, p, section, button, form, a, input, img, textarea, br, span, video: videoHyperaxe, audio: audioHyperaxe, table, tr, td, th, details, summary } = require("../server/node_modules/hyperaxe"); +const { div, h2, p, section, button, form, a, input, label, img, textarea, br, span, video: videoHyperaxe, audio: audioHyperaxe, table, tr, td, th, details, summary } = require("../server/node_modules/hyperaxe"); const { template, i18n, userLink, userLinkLabel, renderSpreadButton, renderContentActions, renderHiveNav } = require('./main_views'); const opinionCategories = require('../backend/opinion_categories'); @@ -1994,30 +1994,35 @@ exports.activityView = (actions, filter, userId, q = '', extras = {}) => { p(desc) ), renderHiveNav(), - div({ class: 'activity-filter-grid' }, - ...[ - activityTypes.slice(0, 3), - activityTypes.slice(3, 8), - activityTypes.slice(8, 13), - activityTypes.slice(13, 19), - activityTypes.slice(19, 26), - activityTypes.slice(26) - ].map(col => - div({ class: 'activity-filter-col' }, - col.map(({ type, label }) => - form({ method: 'GET', action: '/activity' }, - input({ type: 'hidden', name: 'filter', value: type }), - button({ type: 'submit', class: filter === type ? 'filter-btn active' : 'filter-btn' }, label) + input({ type: 'checkbox', id: 'activity-filter-toggle', class: 'hive-filter-radio' }), + label({ for: 'activity-filter-toggle', class: 'activity-filter-btn' }, + span(i18n.filter || 'Filter'), span({ class: 'activity-filter-caret' }, '▾')), + div({ class: 'activity-filter-panel' }, + div({ class: 'activity-filter-grid' }, + ...[ + activityTypes.slice(0, 3), + activityTypes.slice(3, 8), + activityTypes.slice(8, 13), + activityTypes.slice(13, 19), + activityTypes.slice(19, 26), + activityTypes.slice(26) + ].map(col => + div({ class: 'activity-filter-col' }, + col.map(({ type, label }) => + form({ method: 'GET', action: '/activity' }, + input({ type: 'hidden', name: 'filter', value: type }), + button({ type: 'submit', class: filter === type ? 'filter-btn active' : 'filter-btn' }, label) + ) ) ) ) - ) + ), + sub + ? div({ class: 'activity-sub-filter' }, + sub.filters.map(f => a({ href: `${sub.url}?filter=${encodeURIComponent(f)}`, class: 'filter-btn' }, String(f).toUpperCase())) + ) + : null ), - sub - ? div({ class: 'activity-sub-filter' }, - sub.filters.map(f => a({ href: `${sub.url}?filter=${encodeURIComponent(f)}`, class: 'filter-btn' }, String(f).toUpperCase())) - ) - : null, section({ class: 'feed-container' }, renderActionCards(filteredActions, userId, actions, spreadMap)) ) ); diff --git a/src/views/main_views.js b/src/views/main_views.js index 1f96e7e..1ec0a4e 100644 --- a/src/views/main_views.js +++ b/src/views/main_views.js @@ -1239,7 +1239,8 @@ const renderHiveNav = () => { { id: 'fediverse', emoji: '⌬', title: i18n.fediverse, mods: [ { href: '/fediverse', glyph: 'ꗵ', labelKey: 'fediverseTimeline', mod: 'fediverseMod' } ] }, ]; - const catHex = (c) => label({ for: `hive-${c.id}`, class: 'hex-cat' }, + 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)}` }, 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] || '')); @@ -1248,7 +1249,7 @@ const renderHiveNav = () => { 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; } const grid = div({ class: 'hive-grid hive-cats' }, ...catRows); - const panels = CATS.map(c => div({ class: 'hive-panel', id: `hive-panel-${c.id}` }, + const panels = CATS.map(c => div({ class: `hive-panel ${half(c)}`, 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); @@ -1284,17 +1285,17 @@ const template = (titlePrefix, ...elements) => { }) ), body( - input({ type: "checkbox", id: "nav-left-toggle", class: "oasis-nav-side-toggle" }), - input({ type: "checkbox", id: "nav-right-toggle", class: "oasis-nav-side-toggle" }), + 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: "nav-left-toggle", class: "oasis-menu-btn" }, - span({ class: "oasis-menu-ico" }, "☰"), span(i18n.menuPersonal || "Personal")), - label({ for: "nav-right-toggle", class: "oasis-menu-btn" }, - span({ class: "oasis-menu-ico" }, "☰"), span(i18n.menuCommunity || "Community")) + 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")) ), (() => { const aiNavOn = getConfig().modules.aiNavMod === 'on'; @@ -1349,185 +1350,7 @@ const template = (titlePrefix, ...elements) => { })(), div( { class: uxMode === "ainav" ? "main-content ainav-only" : "main-content" }, - uxMode === "ainav" ? null : div( - { class: "sidebar-left" }, - nav( - ul( - navGroup( - { - id: "personal", - emoji: "⚉", - title: i18n.menuPersonal - }, - navLink({ - href: "/profile", - emoji: "⚉", - text: i18n.profile - }), - navLink({ - href: "/cv", - emoji: "ꕛ", - text: i18n.cvTitle - }), - renderAgendaLink(), - renderFavoritesLink(), - renderLogsLink(), - renderWalletLink(), - navLink({ - href: "/modules", - emoji: "ꗣ", - text: i18n.modules - }), - navLink({ - href: "/settings", - emoji: "⚙", - text: i18n.settings - }) - ), - navGroup( - { - id: "governance", - emoji: "⚖", - title: i18n.menuGovernance - }, - navLink({ - href: "/inhabitants", - emoji: "ꖘ", - text: i18n.inhabitantsLabel - }), - renderTribesLink(), - renderLarpLink(), - renderParliamentLink(), - renderCourtsLink() - ), - navGroup( - { - id: "office", - emoji: "⌂", - title: i18n.menuOffice - }, - renderVotationsLink(), - renderEventsLink(), - renderCalendarsLink(), - renderTasksLink(), - renderReportsLink() - ), - navGroup( - { - id: "economy", - emoji: "¤", - title: i18n.menuEconomy - }, - renderBankingLink(), - renderMarketLink(), - renderProjectsLink(), - renderIndustryLink(), - renderJobsLink(), - renderShopsLink(), - renderTransfersLink() - ), - navGroup( - { - id: "tools", - emoji: "⚒", - title: i18n.menuTools - }, - renderAILink(), - navLink({ - href: "/blockexplorer", - emoji: "ꖸ", - text: i18n.blockchain - }), - renderDevLink(), - renderCipherLink(), - renderInvitesLink(), - renderLegacyLink(), - navLink({ - href: "/stats", - emoji: "ꕷ", - text: i18n.statistics - }) - ) - ) - ) - ), - main({ id: "content", class: "main-column" }, elements), - uxMode === "ainav" ? null : div( - { class: "sidebar-right" }, - nav( - ul( - navGroup( - { - id: "network", - emoji: "☍", - title: i18n.menuNetwork - }, - navLink({ - href: "/activity", - emoji: "ꔙ", - text: i18n.activityTitle - }), - renderTagsLink(), - renderTrendingLink(), - renderOpinionsLink(), - renderPadsLink(), - renderForumLink(), - renderMapsLink(), - renderChatsLink() - ), - navGroup( - { - id: "blogs", - emoji: "✦", - title: i18n.menuBlogs - }, - navLink({ - href: "/mentions", - emoji: "✺", - text: i18n.mentions - }), - renderLatestLink(), - renderThreadsLink(), - renderTopicsLink(), - renderSummariesLink(), - renderPopularLink(), - renderMultiverseLink() - ), - navGroup( - { - id: "creative", - emoji: "✎", - title: i18n.menuCreative - }, - renderFeedLink(), - renderGamesLink(), - renderPixeliaLink(), - renderMelodyLink() - ), - navGroup( - { - id: "media", - emoji: "▤", - title: i18n.menuMedia - }, - renderAudiosLink(), - renderBookmarksLink(), - renderDocsLink(), - renderImagesLink(), - renderTorrentsLink(), - renderVideosLink() - ), - navGroup( - { - id: "fediverse", - emoji: "⌬", - title: i18n.fediverse - }, - renderMastodonLink() - ) - ) - ) - ) + main({ id: "content", class: "main-column" }, elements) ), renderBottomBar(), renderFooter()