Empty states and collapsible activity filters

- Activity and search show an empty state with a call to action.
- Selecting an activity type collapses the filters and adds a back arrow to change it.
This commit is contained in:
s1to 2026-07-09 16:44:17 +02:00
parent 9c100ed773
commit bc0f55e914
2 changed files with 15 additions and 5 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 } = 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;
}
@ -1857,6 +1857,16 @@ exports.activityView = (actions, filter, userId, q = '', extras = {}) => {
{ 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' },

View file

@ -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,