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:
parent
89a11fd789
commit
9ff72a17a0
19 changed files with 1202 additions and 673 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -112,6 +112,67 @@ const renderContentActions = (msgId, viewHref) => {
|
|||
};
|
||||
exports.renderContentActions = renderContentActions;
|
||||
|
||||
const emptyState = ({ icon, title, text, ctaHref, ctaLabel }) => div(
|
||||
{ class: "empty-state" },
|
||||
icon ? span({ class: "empty-state-icon" }, icon) : "",
|
||||
title ? p({ class: "empty-state-title" }, title) : "",
|
||||
text ? p({ class: "empty-state-text" }, text) : "",
|
||||
(ctaHref && ctaLabel) ? a({ class: "empty-state-cta filter-btn", href: ctaHref }, ctaLabel) : ""
|
||||
);
|
||||
exports.emptyState = emptyState;
|
||||
|
||||
exports.bottomBarPickerView = () => {
|
||||
const cfg = getConfig();
|
||||
const modOn = (m) => !m || cfg.modules[m] === 'on' || cfg.modules[m] === undefined;
|
||||
const MAX = 4;
|
||||
const pinnedKeys = cfg.bottomBarPins || [];
|
||||
const pinned = pinnedKeys
|
||||
.map(key => exports.PINNABLE_MODULES.find(m => m.key === key))
|
||||
.filter(Boolean);
|
||||
const full = pinned.length >= MAX;
|
||||
const actionForm = (action, key, cls, node) => form({ method: 'POST', action: '/settings/bottombar', class: cls },
|
||||
input({ type: 'hidden', name: 'action', value: action }),
|
||||
input({ type: 'hidden', name: 'key', value: key }),
|
||||
node
|
||||
);
|
||||
const pinnedRow = (m) => actionForm('remove', m.key, 'bb-manage-form',
|
||||
button({ type: 'submit', class: 'bb-manage-btn', 'aria-label': i18n.bbRemove || 'Remove' },
|
||||
span({ class: 'bb-manage-ico' }, m.glyph),
|
||||
span({ class: 'bb-manage-lbl' }, i18n[m.labelKey] || m.key),
|
||||
span({ class: 'bb-manage-x' }, '✕')
|
||||
)
|
||||
);
|
||||
const addBtn = (m) => actionForm('add', m.key, 'bb-pick-form',
|
||||
button({ type: 'submit', class: 'bb-pick-btn' },
|
||||
span({ class: 'bb-pick-ico' }, m.glyph),
|
||||
span({ class: 'bb-pick-lbl' }, i18n[m.labelKey] || m.key)
|
||||
)
|
||||
);
|
||||
const addable = exports.PINNABLE_MODULES.filter(m => modOn(m.mod) && !pinnedKeys.includes(m.key));
|
||||
return template(
|
||||
i18n.bbManage || 'Edit bar',
|
||||
section(
|
||||
div({ class: 'tags-header' },
|
||||
h2(i18n.bbPickTitle || 'Customize the bar'),
|
||||
p(i18n.bbPickDesc || 'Choose up to 4 shortcuts for the bottom bar.')
|
||||
),
|
||||
div({ class: 'bb-manage' },
|
||||
h3(`${i18n.bbYourBar || 'Your bar'} (${pinned.length}/${MAX})`),
|
||||
pinned.length
|
||||
? div({ class: 'bb-manage-list' }, ...pinned.map(pinnedRow))
|
||||
: p({ class: 'bb-manage-empty' }, i18n.bbPickNone || 'None')
|
||||
),
|
||||
div({ class: 'bb-manage-add' },
|
||||
h3(i18n.bbAdd || 'Add'),
|
||||
full
|
||||
? p({ class: 'bb-manage-full' }, i18n.bbFull || 'Maximum reached')
|
||||
: div({ class: 'bb-pick-grid' }, ...addable.map(addBtn))
|
||||
),
|
||||
a({ class: 'filter-btn bb-manage-done', href: '/activity' }, i18n.bbDone || 'Done')
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
exports.renderStateChip = renderStateChip;
|
||||
exports.renderOpenClosedChip = renderOpenClosedChip;
|
||||
exports.renderVisibilityChip = renderVisibilityChip;
|
||||
|
|
@ -1080,34 +1141,172 @@ const renderMobileTopbar = () => div(
|
|||
a({ class: "omt-avatar", href: "/profile" }, img({ src: (typeof global !== "undefined" && global.__OASIS_SELF_AVATAR__) || "/assets/images/default-avatar.png", alt: i18n.profile || "Profile" }))
|
||||
);
|
||||
|
||||
const PINNABLE_MODULES = [
|
||||
{ key: 'search', href: '/search', glyph: 'ꔅ', labelKey: 'searchTitle', mod: null },
|
||||
{ key: 'inbox', href: '/inbox', glyph: '☂', labelKey: 'inbox', mod: null },
|
||||
{ key: 'publish', href: '/publish', glyph: '❂', labelKey: 'publish', mod: null },
|
||||
{ key: 'activity', href: '/activity', glyph: 'ꔙ', labelKey: 'activityTitle', mod: null },
|
||||
{ key: 'tribes', href: '/tribes', glyph: 'ꖥ', labelKey: 'tribesTitle', mod: 'tribesMod' },
|
||||
{ key: 'chats', href: '/chats', glyph: 'ꖒ', labelKey: 'chatsTitle', mod: 'chatsMod' },
|
||||
{ key: 'market', href: '/market', glyph: 'ꕻ', labelKey: 'marketTitle', mod: 'marketMod' },
|
||||
{ key: 'events', href: '/events', glyph: 'ꕆ', labelKey: 'eventsLabel', mod: 'eventsMod' },
|
||||
{ key: 'calendars', href: '/calendars', glyph: 'ꖯ', labelKey: 'calendarsTitle', mod: 'calendarsMod' },
|
||||
{ key: 'banking', href: '/banking', glyph: 'ꗴ', labelKey: 'bankingTitle', mod: 'bankingMod' },
|
||||
{ key: 'forum', href: '/forum', glyph: 'ꕒ', labelKey: 'forumTitle', mod: 'forumMod' },
|
||||
{ key: 'videos', href: '/videos', glyph: 'ꗟ', labelKey: 'videosLabel', mod: 'videosMod' },
|
||||
{ key: 'images', href: '/images', glyph: 'ꕥ', labelKey: 'imagesLabel', mod: 'imagesMod' },
|
||||
{ key: 'audios', href: '/audios', glyph: 'ꔿ', labelKey: 'audiosLabel', mod: 'audiosMod' },
|
||||
{ key: 'documents', href: '/documents', glyph: 'ꕨ', labelKey: 'docsLabel', mod: 'docsMod' },
|
||||
{ key: 'bookmarks', href: '/bookmarks', glyph: 'ꔪ', labelKey: 'bookmarksLabel', mod: 'bookmarksMod' },
|
||||
{ key: 'maps', href: '/maps', glyph: 'ꔌ', labelKey: 'mapsLabel', mod: 'mapsMod' },
|
||||
{ key: 'trending', href: '/trending', glyph: 'ꗝ', labelKey: 'trendingLabel', mod: 'trendingMod' },
|
||||
{ key: 'jobs', href: '/jobs', glyph: 'ꗒ', labelKey: 'jobsTitle', mod: 'jobsMod' },
|
||||
{ key: 'agenda', href: '/agenda', glyph: 'ꗤ', labelKey: 'agendaTitle', mod: 'agendaMod' },
|
||||
{ key: 'wallet', href: '/wallet', glyph: '❄', labelKey: 'wallet', mod: 'walletMod' },
|
||||
{ key: 'tasks', href: '/tasks', glyph: 'ꖧ', labelKey: 'tasksTitle', mod: 'tasksMod' },
|
||||
{ key: 'torrents', href: '/torrents', glyph: 'ꖅ', labelKey: 'torrentsLabel', mod: 'torrentsMod' },
|
||||
{ key: 'tags', href: '/tags', glyph: 'ꖶ', labelKey: 'tagsLabel', mod: 'tagsMod' },
|
||||
{ key: 'pads', href: '/pads', glyph: 'ꔗ', labelKey: 'padsTitle', mod: 'padsMod' },
|
||||
{ key: 'projects', href: '/projects', glyph: 'ꕧ', labelKey: 'projectsTitle', mod: 'projectsMod' },
|
||||
{ key: 'shops', href: '/shops', glyph: 'ꔜ', labelKey: 'shopsTitle', mod: 'shopsMod' },
|
||||
{ key: 'transfers', href: '/transfers', glyph: 'ꘉ', labelKey: 'transfersTitle', mod: 'transfersMod' },
|
||||
{ key: 'feed', href: '/feed', glyph: 'ꕿ', labelKey: 'feedTitle', mod: 'feedMod' },
|
||||
{ key: 'pixelia', href: '/pixelia', glyph: 'ꔘ', labelKey: 'pixeliaTitle', mod: 'pixeliaMod' },
|
||||
{ key: 'melody', href: '/melody', glyph: '♪', labelKey: 'melodyTitle', mod: 'melodyMod' },
|
||||
{ key: 'games', href: '/games', glyph: 'ꕇ', labelKey: 'gamesTitle', mod: 'gamesMod' },
|
||||
{ key: 'votes', href: '/votes', glyph: 'ꔰ', labelKey: 'votationsTitle', mod: 'votesMod' },
|
||||
{ key: 'reports', href: '/reports', glyph: 'ꕥ', labelKey: 'reportsTitle', mod: 'reportsMod' },
|
||||
{ key: 'opinions', href: '/opinions', glyph: 'ꔍ', labelKey: 'opinionsTitle', mod: 'opinionsMod' },
|
||||
{ key: 'parliament',href: '/parliament',glyph: 'ꗞ', labelKey: 'parliamentTitle',mod: 'parliamentMod' },
|
||||
{ key: 'courts', href: '/courts', glyph: 'ꖻ', labelKey: 'courtsTitle', mod: 'courtsMod' },
|
||||
{ key: 'larp', href: '/larp', glyph: '✦', labelKey: 'larpTitle', mod: 'larpMod' },
|
||||
{ key: 'ai', href: '/ai', glyph: 'ꘜ', labelKey: 'ai', mod: 'aiMod' },
|
||||
{ key: 'cipher', href: '/cipher', glyph: 'ꗄ', labelKey: 'cipher', mod: 'cipherMod' },
|
||||
{ key: 'fediverse', href: '/fediverse', glyph: 'ꗵ', labelKey: 'fediverseTimeline', mod: 'fediverseMod' },
|
||||
{ key: 'inhabitants', href: '/inhabitants', glyph: 'ꖘ', labelKey: 'inhabitantsLabel', mod: null },
|
||||
{ key: 'cv', href: '/cv', glyph: 'ꕛ', labelKey: 'cvTitle', mod: null },
|
||||
{ key: 'stats', href: '/stats', glyph: 'ꕷ', labelKey: 'statistics', mod: null },
|
||||
{ key: 'blockexplorer', href: '/blockexplorer', glyph: 'ꖸ', labelKey: 'blockchain', mod: null },
|
||||
];
|
||||
exports.PINNABLE_MODULES = PINNABLE_MODULES;
|
||||
|
||||
const renderBottomBar = () => {
|
||||
const inboxN = sharedState.getInboxCount();
|
||||
const cfg = getConfig();
|
||||
const modOn = (m) => !m || cfg.modules[m] === 'on' || cfg.modules[m] === undefined;
|
||||
const bbLink = (href, ico, label, badge) => a(
|
||||
{ class: "bb-item", href },
|
||||
span({ class: "bb-ico" }, ico),
|
||||
(badge && badge > 0) ? span({ class: "bb-badge" }, String(badge)) : "",
|
||||
span({ class: "bb-lbl" }, label)
|
||||
);
|
||||
const MAX = 4;
|
||||
const pins = (cfg.bottomBarPins || [])
|
||||
.map(key => PINNABLE_MODULES.find(m => m.key === key))
|
||||
.filter(m => m && modOn(m.mod))
|
||||
.slice(0, MAX);
|
||||
const slots = pins.map(m => bbLink(
|
||||
m.href, m.glyph, i18n[m.labelKey] || m.key, m.key === 'inbox' ? inboxN : 0
|
||||
));
|
||||
const editSlot = a(
|
||||
{ class: "bb-item bb-add", href: "/settings/bottombar" },
|
||||
span({ class: "bb-ico" }, pins.length < MAX ? "+" : "✎"),
|
||||
span({ class: "bb-lbl" }, pins.length < MAX ? (i18n.bbAdd || "Add") : (i18n.bbManage || "Edit"))
|
||||
);
|
||||
const pair = [ bbLink("/peers", "⧖", i18n.peers || "Peers") ];
|
||||
if (modOn('invitesMod')) pair.push(bbLink("/invites", "ꔮ", i18n.invites || "Invites"));
|
||||
return nav(
|
||||
{ class: "oasis-bottombar", style: "display:none", "aria-label": "Quick actions" },
|
||||
bbLink("/search", "ꔅ", i18n.searchTitle || "Buscar"),
|
||||
bbLink("/inbox", "☂", i18n.inbox || "Inbox", inboxN),
|
||||
bbLink("/publish", "❂", i18n.publish || "Write"),
|
||||
bbLink("/activity", "ꔙ", i18n.activityTitle || "Activity"),
|
||||
div({ class: "bb-pair" },
|
||||
bbLink("/peers", "⧖", i18n.peers || "Peers"),
|
||||
bbLink("/invites", "ꔮ", i18n.invites || "Invites")
|
||||
)
|
||||
{ class: "oasis-bottombar", style: "display:none", "aria-label": i18n.bbQuickActions || "Quick actions" },
|
||||
...slots,
|
||||
editSlot,
|
||||
div({ class: "bb-pair" }, ...pair)
|
||||
);
|
||||
};
|
||||
|
||||
const RADIAL_ITEMS = [
|
||||
{ key: 'search', href: '/search', glyph: 'ꔅ', labelKey: 'searchTitle', mod: null },
|
||||
{ key: 'chats', href: '/chats', glyph: 'ꖒ', labelKey: 'chatsTitle', mod: 'chatsMod' },
|
||||
{ key: 'tribes', href: '/tribes', glyph: 'ꖥ', labelKey: 'tribesTitle', mod: 'tribesMod' },
|
||||
{ key: 'market', href: '/market', glyph: 'ꕻ', labelKey: 'marketTitle', mod: 'marketMod' },
|
||||
{ key: 'events', href: '/events', glyph: 'ꕆ', labelKey: 'eventsLabel', mod: 'eventsMod' },
|
||||
{ key: 'forum', href: '/forum', glyph: 'ꕒ', labelKey: 'forumTitle', mod: 'forumMod' },
|
||||
{ key: 'agenda', href: '/agenda', glyph: 'ꗤ', labelKey: 'agendaTitle', mod: 'agendaMod' },
|
||||
];
|
||||
|
||||
const renderHiveNav = () => {
|
||||
if (process.env.OASIS_MOBILE !== '1') return "";
|
||||
const cfg = getConfig();
|
||||
const modOn = (m) => !m || cfg.modules[m] === 'on' || cfg.modules[m] === undefined;
|
||||
const CATS = [
|
||||
{ id: 'personal', emoji: '⚉', title: i18n.menuPersonal, mods: [
|
||||
{ href: '/profile', glyph: '⚉', labelKey: 'profile', mod: null }, { href: '/cv', glyph: 'ꕛ', labelKey: 'cvTitle', mod: null },
|
||||
{ href: '/agenda', glyph: 'ꗤ', labelKey: 'agendaTitle', mod: 'agendaMod' }, { href: '/favorites', glyph: 'ꘝ', labelKey: 'favoritesTitle', mod: 'favoritesMod' },
|
||||
{ href: '/logs', glyph: 'ꗯ', labelKey: 'logsTitle', mod: 'logsMod' }, { href: '/wallet', glyph: '❄', labelKey: 'wallet', mod: 'walletMod' },
|
||||
{ href: '/modules', glyph: 'ꗣ', labelKey: 'modules', mod: null }, { href: '/settings', glyph: '⚙', labelKey: 'settings', mod: null } ] },
|
||||
{ id: 'governance', emoji: '⚖', title: i18n.menuGovernance, mods: [
|
||||
{ href: '/inhabitants', glyph: 'ꖘ', labelKey: 'inhabitantsLabel', mod: null }, { href: '/tribes', glyph: 'ꖥ', labelKey: 'tribesTitle', mod: 'tribesMod' },
|
||||
{ href: '/larp', glyph: '✦', labelKey: 'larpTitle', mod: 'larpMod' }, { href: '/parliament', glyph: 'ꗞ', labelKey: 'parliamentTitle', mod: 'parliamentMod' },
|
||||
{ href: '/courts', glyph: 'ꖻ', labelKey: 'courtsTitle', mod: 'courtsMod' } ] },
|
||||
{ id: 'office', emoji: '⌂', title: i18n.menuOffice, mods: [
|
||||
{ href: '/votes', glyph: 'ꔰ', labelKey: 'votationsTitle', mod: 'votesMod' }, { href: '/events', glyph: 'ꕆ', labelKey: 'eventsLabel', mod: 'eventsMod' },
|
||||
{ href: '/calendars', glyph: 'ꖯ', labelKey: 'calendarsTitle', mod: 'calendarsMod' }, { href: '/tasks', glyph: 'ꖧ', labelKey: 'tasksTitle', mod: 'tasksMod' },
|
||||
{ href: '/reports', glyph: 'ꕥ', labelKey: 'reportsTitle', mod: 'reportsMod' } ] },
|
||||
{ id: 'economy', emoji: '¤', title: i18n.menuEconomy, mods: [
|
||||
{ href: '/banking', glyph: 'ꗴ', labelKey: 'bankingTitle', mod: 'bankingMod' }, { href: '/market', glyph: 'ꕻ', labelKey: 'marketTitle', mod: 'marketMod' },
|
||||
{ href: '/projects', glyph: 'ꕧ', labelKey: 'projectsTitle', mod: 'projectsMod' }, { href: '/jobs', glyph: 'ꗒ', labelKey: 'jobsTitle', mod: 'jobsMod' },
|
||||
{ href: '/shops', glyph: 'ꔜ', labelKey: 'shopsTitle', mod: 'shopsMod' }, { href: '/transfers', glyph: 'ꘉ', labelKey: 'transfersTitle', mod: 'transfersMod' } ] },
|
||||
{ id: 'tools', emoji: '⚒', title: i18n.menuTools, mods: [
|
||||
{ href: '/ai', glyph: 'ꘜ', labelKey: 'ai', mod: 'aiMod' }, { href: '/blockexplorer', glyph: 'ꖸ', labelKey: 'blockchain', mod: null },
|
||||
{ href: '/cipher', glyph: 'ꗄ', labelKey: 'cipher', mod: 'cipherMod' }, { href: '/invites', glyph: 'ꔹ', labelKey: 'invites', mod: 'invitesMod' },
|
||||
{ href: '/legacy', glyph: 'ꖤ', labelKey: 'legacy', mod: 'legacyMod' }, { href: '/stats', glyph: 'ꕷ', labelKey: 'statistics', mod: null } ] },
|
||||
{ id: 'network', emoji: '☍', title: i18n.menuNetwork, mods: [
|
||||
{ href: '/activity', glyph: 'ꔙ', labelKey: 'activityTitle', mod: null }, { href: '/tags', glyph: 'ꖶ', labelKey: 'tagsLabel', mod: 'tagsMod' },
|
||||
{ href: '/trending', glyph: 'ꗝ', labelKey: 'trendingLabel', mod: 'trendingMod' }, { href: '/opinions', glyph: 'ꔍ', labelKey: 'opinionsTitle', mod: 'opinionsMod' },
|
||||
{ href: '/pads', glyph: 'ꔗ', labelKey: 'padsTitle', mod: 'padsMod' }, { href: '/forum', glyph: 'ꕒ', labelKey: 'forumTitle', mod: 'forumMod' },
|
||||
{ href: '/maps', glyph: 'ꔌ', labelKey: 'mapsLabel', mod: 'mapsMod' }, { href: '/chats', glyph: 'ꖒ', labelKey: 'chatsTitle', mod: 'chatsMod' } ] },
|
||||
{ id: 'blogs', emoji: '✦', title: i18n.menuBlogs, mods: [
|
||||
{ href: '/mentions', glyph: '✺', labelKey: 'mentions', mod: null }, { href: '/public/latest', glyph: '☄', labelKey: 'latest', mod: 'latestMod' },
|
||||
{ href: '/public/latest/threads', glyph: '♺', labelKey: 'threads', mod: 'threadsMod' }, { href: '/public/latest/topics', glyph: 'ϟ', labelKey: 'topics', mod: 'topicsMod' },
|
||||
{ href: '/public/latest/summaries', glyph: '※', labelKey: 'summaries', mod: 'summariesMod' }, { href: '/public/popular/day', glyph: '⌘', labelKey: 'popular', mod: 'popularMod' },
|
||||
{ href: '/public/latest/extended', glyph: '∞', labelKey: 'multiverse', mod: 'multiverseMod' } ] },
|
||||
{ id: 'creative', emoji: '✎', title: i18n.menuCreative, mods: [
|
||||
{ href: '/feed', glyph: 'ꕿ', labelKey: 'feedTitle', mod: 'feedMod' }, { href: '/games', glyph: 'ꕇ', labelKey: 'gamesTitle', mod: 'gamesMod' },
|
||||
{ href: '/pixelia', glyph: 'ꔘ', labelKey: 'pixeliaTitle', mod: 'pixeliaMod' }, { href: '/melody', glyph: '♪', labelKey: 'melodyTitle', mod: 'melodyMod' } ] },
|
||||
{ id: 'media', emoji: '▤', title: i18n.menuMedia, mods: [
|
||||
{ href: '/audios', glyph: 'ꔿ', labelKey: 'audiosLabel', mod: 'audiosMod' }, { href: '/bookmarks', glyph: 'ꔪ', labelKey: 'bookmarksLabel', mod: 'bookmarksMod' },
|
||||
{ href: '/documents', glyph: 'ꕨ', labelKey: 'docsLabel', mod: 'docsMod' }, { href: '/images', glyph: 'ꕥ', labelKey: 'imagesLabel', mod: 'imagesMod' },
|
||||
{ href: '/torrents', glyph: 'ꖅ', labelKey: 'torrentsLabel', mod: 'torrentsMod' }, { href: '/videos', glyph: 'ꗟ', labelKey: 'videosLabel', mod: 'videosMod' } ] },
|
||||
{ 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' },
|
||||
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 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; }
|
||||
const grid = div({ class: 'hive-grid hive-cats' }, ...catRows);
|
||||
const panels = CATS.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);
|
||||
};
|
||||
exports.renderHiveNav = renderHiveNav;
|
||||
|
||||
const template = (titlePrefix, ...elements) => {
|
||||
const currentConfig = getConfig();
|
||||
const theme = currentConfig.themes.current || "Dark-SNH";
|
||||
const uxMode = currentConfig.ux?.current === "ainav" ? "ainav" : "blocks";
|
||||
const baseTheme = theme === "OasisMobileUX" ? "Dark-SNH" : theme;
|
||||
const mobileUXActive = process.env.OASIS_MOBILE === '1' ? theme !== "OasisMobile" : theme === "OasisMobileUX";
|
||||
const mobileUXLink = mobileUXActive
|
||||
? link({ rel: "stylesheet", href: "/assets/themes/OasisMobileUX.css", ...(process.env.OASIS_MOBILE === '1' ? {} : { media: "(max-width: 768px)" }) })
|
||||
: "";
|
||||
const themeLink = link({
|
||||
rel: "stylesheet",
|
||||
href: `/assets/themes/${theme}.css`
|
||||
href: `/assets/themes/${baseTheme}.css`
|
||||
});
|
||||
const nodes = html(
|
||||
{ lang: "en" },
|
||||
|
|
@ -1116,6 +1315,7 @@ const template = (titlePrefix, ...elements) => {
|
|||
link({ rel: "stylesheet", href: "/assets/styles/style.css" }),
|
||||
themeLink,
|
||||
link({ rel: "stylesheet", href: "/assets/styles/mobile.css", ...(process.env.OASIS_MOBILE === '1' ? {} : { media: "(max-width: 768px)" }) }),
|
||||
mobileUXLink,
|
||||
link({ rel: "icon", href: "/assets/images/favicon.svg" }),
|
||||
meta({ charset: "utf-8" }),
|
||||
meta({ name: "description", content: i18n.oasisDescription }),
|
||||
|
|
@ -1431,14 +1631,20 @@ exports.template = template;
|
|||
exports.ainavHomeView = ({ recentTags = [] } = {}) => {
|
||||
const currentConfig = getConfig();
|
||||
const theme = currentConfig.themes.current || "Dark-SNH";
|
||||
const baseTheme = theme === "OasisMobileUX" ? "Dark-SNH" : theme;
|
||||
const mobileUXActive = process.env.OASIS_MOBILE === '1' ? theme !== "OasisMobile" : theme === "OasisMobileUX";
|
||||
const mobileUXLink = mobileUXActive
|
||||
? link({ rel: "stylesheet", href: "/assets/themes/OasisMobileUX.css", ...(process.env.OASIS_MOBILE === '1' ? {} : { media: "(max-width: 768px)" }) })
|
||||
: "";
|
||||
const placeholder = i18n.aiNavPlaceholder || 'Where do you want to go?';
|
||||
const nodes = html(
|
||||
{ lang: "en" },
|
||||
head(
|
||||
title(placeholder, " | Oasis"),
|
||||
link({ rel: "stylesheet", href: "/assets/styles/style.css" }),
|
||||
link({ rel: "stylesheet", href: `/assets/themes/${theme}.css` }),
|
||||
link({ rel: "stylesheet", href: `/assets/themes/${baseTheme}.css` }),
|
||||
link({ rel: "stylesheet", href: "/assets/styles/mobile.css", ...(process.env.OASIS_MOBILE === '1' ? {} : { media: "(max-width: 768px)" }) }),
|
||||
mobileUXLink,
|
||||
link({ rel: "icon", href: "/assets/images/favicon.svg" }),
|
||||
meta({ charset: "utf-8" }),
|
||||
meta({ name: "description", content: i18n.oasisDescription }),
|
||||
|
|
@ -3402,7 +3608,7 @@ exports.privateView = async (messagesInput, filter, decrypted = null) => {
|
|||
threadGroups[tid].push(msg)
|
||||
}
|
||||
|
||||
if (!threadOrder.length) return p({ class: 'empty' }, i18n.noPrivateMessages)
|
||||
if (!threadOrder.length) return emptyState({ icon: "✉", title: i18n.noPrivateMessages, text: i18n.emptyConnectHint, ctaHref: "/invites", ctaLabel: i18n.emptyConnectCta })
|
||||
|
||||
return threadOrder.map(tid => {
|
||||
const msgs = threadGroups[tid]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
const { form, button, div, h2, p, section, input, label, select, option, img, audio: audioHyperaxe, video: videoHyperaxe, table, hr, hd, br, td, tr, th, a, span } = require("../server/node_modules/hyperaxe");
|
||||
const { template, i18n, userLink, renderContentActions } = require('./main_views');
|
||||
const { template, i18n, userLink, renderContentActions, emptyState } = require('./main_views');
|
||||
const moment = require("../server/node_modules/moment");
|
||||
const { renderTextWithStyles } = require('../backend/renderTextWithStyles');
|
||||
const { renderUrl } = require('../backend/renderUrl');
|
||||
|
|
@ -601,7 +601,7 @@ const searchView = ({ messages = [], blobs = {}, query = "", type = "", types =
|
|||
})
|
||||
)
|
||||
)
|
||||
: div({ class: 'no-results' }, p(i18n.noResultsFound));
|
||||
: emptyState({ icon: "🔍", title: i18n.noResultsFound });
|
||||
|
||||
let html = template(
|
||||
hashtag ? `#${hashtag}` : i18n.searchTitle,
|
||||
|
|
|
|||
|
|
@ -37,7 +37,8 @@ const settingsView = ({ version, aiPrompt, fediverseAccount, fediverseError }) =
|
|||
option({ value: "Clear-SNH", selected: theme === "Clear-SNH" ? true : undefined }, "Clear-SNH"),
|
||||
option({ value: "Purple-SNH", selected: theme === "Purple-SNH" ? true : undefined }, "Purple-SNH"),
|
||||
option({ value: "Matrix-SNH", selected: theme === "Matrix-SNH" ? true : undefined }, "Matrix-SNH"),
|
||||
option({ value: "OasisMobile", selected: theme === "OasisMobile" ? true : undefined }, "Oasis-Mobile")
|
||||
option({ value: "OasisMobile", selected: theme === "OasisMobile" ? true : undefined }, "Oasis-Mobile"),
|
||||
option({ value: "OasisMobileUX", selected: theme === "OasisMobileUX" ? true : undefined }, "Oasis-Mobile-UX")
|
||||
];
|
||||
|
||||
const languageOption = (longName, shortName) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue