Add Oasis-Mobile-UX theme with hexagon navigation

- Selectable Oasis-Mobile-UX theme; mobile.css kept intact
- Hexagon category launcher on home (10 categories with expandable modules)
- Customizable bottom bar (up to 4 shortcuts, Peers/Invites fixed)
- Empty states and collapsible activity filters
This commit is contained in:
s1to 2026-07-17 12:57:50 +02:00
parent 89a11fd789
commit 9ff72a17a0
19 changed files with 1202 additions and 673 deletions

View file

@ -1,5 +1,5 @@
const { div, h2, p, section, button, form, a, input, img, textarea, br, span, video: videoHyperaxe, audio: audioHyperaxe, table, tr, td, th, details, summary, ul, li, label } = require("../server/node_modules/hyperaxe");
const { template, i18n, userLink, userLinkLabel, renderSpreadButton, renderContentActions } = require('./main_views');
const { template, i18n, userLink, userLinkLabel, renderSpreadButton, renderContentActions, emptyState, renderHiveNav } = require('./main_views');
const opinionCategories = require('../backend/opinion_categories');
const OPINION_TYPES = new Set(['bookmark','votes','feed','image','audio','video','document','torrent','transfer']);
@ -285,7 +285,7 @@ function renderActionCards(actions, userId, allActions, spreadMap = new Map()) {
: validActions;
if (!deduped.length) {
return div({ class: "no-actions" }, p(i18n.noActions));
return emptyState({ icon: "📭", title: i18n.noActions, text: i18n.emptyConnectHint, ctaHref: "/invites", ctaLabel: i18n.emptyConnectCta });
}
const seenDocumentTitles = new Set();
@ -1596,7 +1596,7 @@ function renderActionCards(actions, userId, allActions, spreadMap = new Map()) {
const filteredCards = cards.filter(Boolean);
if (!filteredCards.length) {
return div({ class: "no-actions" }, p(i18n.noActions));
return emptyState({ icon: "📭", title: i18n.noActions, text: i18n.emptyConnectHint, ctaHref: "/invites", ctaLabel: i18n.emptyConnectCta });
}
return filteredCards;
}
@ -1841,6 +1841,7 @@ exports.activityView = (actions, filter, userId, q = '', extras = {}) => {
h2(i18n.activityList),
p(desc)
),
renderHiveNav(),
(() => {
const labelOf = {};
activityTypes.forEach(t => { labelOf[t.type] = t.label; });
@ -1849,33 +1850,18 @@ exports.activityView = (actions, filter, userId, q = '', extras = {}) => {
button({ type: 'submit', class: filter === type ? 'filter-btn active' : 'filter-btn' }, labelOf[type] || type)
);
const QUICK = ['recent', 'all', 'mine'];
const GROUPS = [
{ id: 'act-comunidad', emoji: '👥', title: i18n.activityGroupCommunity || 'Community & Governance', types: ['inhabitants', 'tribe', 'larp', 'parliament', 'courts', 'votes', 'report'] },
{ id: 'act-organizacion', emoji: '🗓', title: i18n.activityGroupOrg || 'Organization', types: ['event', 'calendar', 'task', 'project', 'job'] },
{ id: 'act-comunicacion', emoji: '💬', title: i18n.activityGroupComm || 'Communication', types: ['post', 'feed', 'chat', 'pad', 'forum'] },
{ id: 'act-economia', emoji: '💰', title: i18n.activityGroupEconomy || 'Economy', types: ['banking', 'market', 'shop', 'transfer'] },
{ id: 'act-media', emoji: '🎬', title: i18n.activityGroupMedia || 'Content & Media', types: ['audio', 'bookmark', 'document', 'image', 'torrent', 'video'] },
{ id: 'act-otros', emoji: '⋯', title: i18n.activityGroupOther || 'Others', types: ['map', 'curriculum'] },
];
const isTypeFilter = filter && !QUICK.includes(filter) && labelOf[filter];
if (isTypeFilter) {
return div({ class: 'activity-filter-collapsed' },
a({ class: 'activity-back filter-btn', href: '/activity' },
span({ class: 'activity-back-arrow' }, '‹'),
span(i18n.activityChangeFilter || 'Change filter')
),
span({ class: 'activity-active-filter' }, labelOf[filter] || filter)
);
}
return div({ class: 'activity-filter-acc' },
div({ class: 'activity-quick-modes' }, QUICK.map(mkBtn)),
ul({ class: 'oasis-nav-groups activity-groups' },
GROUPS.map(g => {
const openByFilter = g.types.includes(filter);
return li({ class: 'oasis-nav-group' },
input({ type: 'checkbox', id: `oasis-nav-group-${g.id}`, class: 'oasis-nav-toggle', ...(openByFilter ? { checked: true } : {}) }),
label({ for: `oasis-nav-group-${g.id}`, class: 'oasis-nav-header' },
span({ class: 'emoji' }, g.emoji),
span({ class: 'nav-text' }, g.title),
span({ class: 'act-count' }, String(g.types.length)),
span({ class: 'oasis-nav-arrow' }, '▾')
),
ul({ class: 'oasis-nav-list' },
g.types.map(t => li(mkBtn(t)))
)
);
})
)
div({ class: 'activity-quick-modes' }, QUICK.map(mkBtn))
);
})(),
sub