views: main_views a 0.9.5 conservando la interfaz de la rama

main_views.js pasa a la version de 0.9.5 y se le reinyectan los seis puntos propios:
el enganche a fork/hive_nav, la topbar y los hexagonos en la cabecera, la barra
inferior antes del pie, el orden invertido de hojas de estilo en movil (en los dos
templates), el campo para copiar el Oasis ID y el chip de dispositivo.

Esto era necesario, no cosmetico: el renderContentActions de la rama tenia dos
parametros y el de 0.9.5 tiene tres. Las vistas ya integradas lo llaman con el
tercero (favoritos, difusion, reportar) y la rama lo ignoraba en silencio, de modo
que esos botones no llegaban a dibujarse.

Cuidado con el orden de hojas: 0.9.5 carga el tema antes de mobile.css, lo que en
movil dejaba a mobile.css ganando por cascada. Corregido en los dos templates.

bookmark_view pasa a 0.9.5 con el renderPMButton de la rama reinyectado. El
renderFavoriteToggle propio desaparece porque 0.9.5 integra los favoritos dentro de
renderContentActions, que cubre lo mismo.

Verificado con la app en marcha: 15 rutas OK, orden de hojas correcto en el HTML
servido (style.css, mobile.css, OasisMobile.css), 10 categorias y 47 modulos en los
hexagonos, topbar con Personal/Community y avatar, 7 accesos rapidos, campo de Oasis
ID presente y 0 etiquetas vacias.
This commit is contained in:
SITO 2026-08-18 22:45:35 +02:00
parent c662e37668
commit 373fba5d96
2 changed files with 582 additions and 705 deletions

View file

@ -1,7 +1,8 @@
const { form, button, div, h2, p, section, input, label, textarea, br, a, span, select, option } =
require("../server/node_modules/hyperaxe");
const { renderCommentsSection: renderSharedCommentsSection, renderCommentsLink } = require("./comments_view");
const { template, i18n, renderOpinionsVoting, userLink, renderSpreadButton, renderEcoTax, renderLifespanChip, renderContentActions } = require("./main_views");
const { template, i18n, renderOpinionsVoting, renderEngagement, userLink, renderSpreadButton, renderEcoTax, renderLifespanChip, renderContentActions, renderSpreadEditWarning } = require("./main_views");
const moment = require("../server/node_modules/moment");
const { config } = require("../server/SSB_server.js");
const { renderUrl } = require("../backend/renderUrl");
@ -58,70 +59,11 @@ const renderBookmarkActions = (filter, bookmark, params = {}) => {
};
const renderBookmarkCommentsSection = (bookmarkId, rootId, comments = [], returnTo = null) => {
const list = safeArr(comments).filter(c => {
const t = c && c.value && c.value.content && c.value.content.text;
return t && String(t).trim();
return renderSharedCommentsSection({
action: `/bookmarks/${encodeURIComponent(bookmarkId)}/comments`,
comments: comments,
returnTo: returnTo
});
const commentsCount = list.length;
return div(
{ class: "vote-comments-section" },
div(
{ class: "comments-count" },
span({ class: "card-label" }, i18n.voteCommentsLabel + ": "),
span({ class: "card-value" }, String(commentsCount))
),
div(
{ class: "comment-form-wrapper" },
h2({ class: "comment-form-title" }, i18n.voteNewCommentLabel),
form(
{ method: "POST", action: `/bookmarks/${encodeURIComponent(bookmarkId)}/comments`, class: "comment-form", enctype: "multipart/form-data" },
returnTo ? input({ type: "hidden", name: "returnTo", value: returnTo }) : null,
rootId ? input({ type: "hidden", name: "rootId", value: rootId }) : null,
textarea({
id: "comment-text",
name: "text",
rows: 4,
class: "comment-textarea",
placeholder: i18n.voteNewCommentPlaceholder
}),
div({ class: "comment-file-upload" }, label(i18n.uploadMedia), input({ type: "file", name: "blob" })),
br(),
button({ type: "submit", class: "comment-submit-btn" }, i18n.voteNewCommentButton)
)
),
list.length
? div(
{ class: "comments-list" },
list.map((c) => {
const author = c?.value?.author || "";
const ts = c?.value?.timestamp || c?.timestamp;
const absDate = ts ? moment(ts).format("YYYY/MM/DD HH:mm:ss") : "";
const relDate = ts ? moment(ts).fromNow() : "";
const content = c?.value?.content || {};
const text = content.text || "";
const threadRoot = content.fork || content.root || null;
return div(
{ class: "votations-comment-card" },
span(
{ class: "created-at" },
span(i18n.createdBy),
author ? userLink(author) : span("(unknown)"),
absDate ? span(" | ") : "",
absDate ? span({ class: "votations-comment-date" }, absDate) : "",
relDate ? span({ class: "votations-comment-date" }, " | ", i18n.sendTime) : "",
relDate && threadRoot
? a({ href: `/thread/${encodeURIComponent(threadRoot)}#${encodeURIComponent(c.key)}` }, relDate)
: ""
),
p({ class: "votations-comment-text" }, ...renderUrl(text))
);
})
)
: p({ class: "votations-no-comments" }, i18n.voteNoCommentsYet)
);
};
const renderCardField = (labelText, value) =>
@ -131,22 +73,6 @@ const renderCardField = (labelText, value) =>
span({ class: "card-value" }, value)
);
const renderFavoriteToggle = (bookmark, returnTo) =>
form(
{
method: "POST",
action: bookmark.isFavorite
? `/bookmarks/favorites/remove/${encodeURIComponent(bookmark.id)}`
: `/bookmarks/favorites/add/${encodeURIComponent(bookmark.id)}`,
class: "bookmark-favorite-form"
},
returnTo ? input({ type: "hidden", name: "returnTo", value: returnTo }) : null,
button(
{ type: "submit", class: "filter-btn" },
bookmark.isFavorite ? i18n.bookmarkRemoveFavoriteButton : i18n.bookmarkAddFavoriteButton
)
);
const renderTags = (tags) => {
const list = safeArr(tags).map((t) => String(t || "").trim()).filter(Boolean);
return list.length
@ -167,7 +93,7 @@ const renderBookmarkList = (filteredBookmarks, filter, params = {}) => {
const lastVisit = bookmark.lastVisit ? moment(bookmark.lastVisit) : null;
const lastVisitTxt =
lastVisit && lastVisit.isValid()
? `${lastVisit.format("YYYY/MM/DD HH:mm:ss")} (${lastVisit.fromNow()})`
? `${lastVisit.format("YYYY/MM/DD HH:mm")} (${lastVisit.fromNow()})`
: i18n.noLastVisit;
const urlLink = bookmark.url
@ -180,52 +106,19 @@ const renderBookmarkList = (filteredBookmarks, filter, params = {}) => {
div(
{ class: "card-header activity-card-header" },
span(),
renderContentActions(bookmark.id, `/bookmarks/${encodeURIComponent(bookmark.id)}`)
renderContentActions(bookmark.id, `/bookmarks/${encodeURIComponent(bookmark.id)}`, { spread: (params.spreadMap && params.spreadMap.get(bookmark.id)) || params.spreads || null, author: bookmark.author, favKind: 'bookmarks', isFavorite: bookmark.isFavorite, reportTitle: bookmark.title })
),
div(
{ class: "card-section bookmark-card-body" },
div(
{ class: "bookmark-topbar" },
div(
{ class: "bookmark-topbar-left" },
renderPMButton(bookmark.author),
renderFavoriteToggle(bookmark, returnTo)
),
div(
{ class: "bookmark-topbar-right" },
renderBookmarkActions(filter, bookmark, params)
)
),
h2({ class: "bookmark-title" }, bookmark.category || bookmark.url || ""),
h2({ class: "bookmark-title" }, bookmark.url ? urlLink : (bookmark.title || "")),
bookmark.lifetime ? div({ class: "card-chips-row" }, renderLifespanChip(bookmark.lifetime, i18n)) : null,
renderCardField(i18n.bookmarkUrlLabel + ":", urlLink),
bookmark.title && bookmark.url ? p({ class: "bookmark-subtitle" }, bookmark.title) : null,
renderCardField(i18n.bookmarkLastVisitLabel + ":", lastVisitTxt),
br,
div(
{ class: "card-comments-summary" },
span({ class: "card-label" }, i18n.voteCommentsLabel + ":"),
span({ class: "card-value" }, String(commentCount)),
br(),
br(),
form(
{ method: "GET", action: `/bookmarks/${encodeURIComponent(bookmark.id)}` },
input({ type: "hidden", name: "returnTo", value: returnTo }),
input({ type: "hidden", name: "filter", value: filter || "all" }),
params.q ? input({ type: "hidden", name: "q", value: params.q }) : null,
params.sort ? input({ type: "hidden", name: "sort", value: params.sort }) : null,
button({ type: "submit", class: "filter-btn" }, i18n.voteCommentsForumButton)
)
renderEngagement(bookmark.id,
renderOpinionsVoting('/bookmarks/opinions', bookmark.id, bookmark.opinions, returnTo, bookmark.opinions_inhabitants),
renderCommentsLink({ href: `/bookmarks/${encodeURIComponent(bookmark.id)}`, count: commentCount })
),
div(
{ class: "card-comments-summary feed-opinions-section" },
div(
{ class: "comments-count" },
span({ class: "card-label" }, (i18n.opinionsTitle || "Opinions") + ": "),
span({ class: "card-value" }, String(Object.values(bookmark.opinions || {}).reduce((s, n) => s + (Number(n) || 0), 0)))
),
renderOpinionsVoting('/bookmarks/opinions', bookmark.id, bookmark.opinions, returnTo, bookmark.opinions_inhabitants)
),
div({ class: "card-spread-left" }, renderSpreadButton(bookmark.id, (params.spreadMap && params.spreadMap.get(bookmark.id)) || params.spreads)),
(() => {
const createdTs = bookmark.createdAt ? new Date(bookmark.createdAt).getTime() : NaN;
const updatedTs = bookmark.updatedAt ? new Date(bookmark.updatedAt).getTime() : NaN;
@ -233,12 +126,12 @@ const renderBookmarkList = (filteredBookmarks, filter, params = {}) => {
return p(
{ class: "card-footer" },
span({ class: "date-link" }, `${moment(bookmark.createdAt).format("YYYY/MM/DD HH:mm:ss")} ${i18n.performed} `),
span({ class: "date-link" }, `${moment(bookmark.createdAt).format("YYYY/MM/DD HH:mm")} ${i18n.performed} `),
userLink(bookmark.author),
showUpdated
? span(
{ class: "votations-comment-date" },
` | ${i18n.bookmarkUpdatedAt}: ${moment(bookmark.updatedAt).format("YYYY/MM/DD HH:mm:ss")}`
` | ${i18n.bookmarkUpdatedAt}: ${moment(bookmark.updatedAt).format("YYYY/MM/DD HH:mm")}`
)
: null
);
@ -262,6 +155,7 @@ const renderBookmarkForm = (filter, bookmarkId, bookmarkToEdit, tags, params = {
return div(
{ class: "div-center bookmark-form" },
params.spreadWarning || null,
form(
{ action: filter === "edit" ? `/bookmarks/update/${encodeURIComponent(bookmarkId)}` : "/bookmarks/create", method: "POST" },
input({ type: "hidden", name: "returnTo", value: returnTo }),
@ -294,16 +188,6 @@ const renderBookmarkForm = (filter, bookmarkId, bookmarkToEdit, tags, params = {
}),
br(),
br(),
label(i18n.bookmarkCategoryLabel),
br(),
input({
type: "text",
name: "category",
id: "category",
placeholder: i18n.bookmarkCategoryPlaceholder,
value: filter === "edit" ? bookmarkToEdit.category || "" : ""
}),
br(),
label(i18n.bookmarkTagsLabel),
br(),
input({
@ -321,20 +205,8 @@ const renderBookmarkForm = (filter, bookmarkId, bookmarkToEdit, tags, params = {
};
exports.bookmarkView = async (bookmarks, filter = "all", bookmarkId = null, params = {}) => {
const title =
filter === "mine"
? i18n.bookmarkMineSectionTitle
: filter === "create"
? i18n.bookmarkCreateSectionTitle
: filter === "edit"
? i18n.bookmarkUpdateSectionTitle
: filter === "recent"
? i18n.bookmarkRecentSectionTitle
: filter === "top"
? i18n.bookmarkTopSectionTitle
: filter === "favorites"
? i18n.bookmarkFavoritesSectionTitle
: i18n.bookmarkAllSectionTitle;
const bookmarkEditWarning = filter === "edit" ? await renderSpreadEditWarning(bookmarkId) : null;
const title = i18n.bookmarkTitle;
const q = safeText(params.q || "");
const sort = safeText(params.sort || "recent");
@ -359,18 +231,18 @@ exports.bookmarkView = async (bookmarks, filter = "all", bookmarkId = null, para
{ method: "GET", action: "/bookmarks", class: "ui-toolbar ui-toolbar--filters" },
input({ type: "hidden", name: "q", value: q }),
input({ type: "hidden", name: "sort", value: sort }),
button({ type: "submit", name: "filter", value: "all", class: filter === "all" ? "filter-btn active" : "filter-btn" }, i18n.bookmarkFilterAll),
button({ type: "submit", name: "filter", value: "mine", class: filter === "mine" ? "filter-btn active" : "filter-btn" }, i18n.bookmarkFilterMine),
button({ type: "submit", name: "filter", value: "top", class: filter === "top" ? "filter-btn active" : "filter-btn" }, i18n.bookmarkFilterTop),
button({ type: "submit", name: "filter", value: "favorites", class: filter === "favorites" ? "filter-btn active" : "filter-btn" }, i18n.bookmarkFilterFavorites),
button({ type: "submit", name: "filter", value: "recent", class: filter === "recent" ? "filter-btn active" : "filter-btn" }, i18n.bookmarkFilterRecent),
button({ type: "submit", name: "filter", value: "all", class: filter === "all" ? "filter-btn active" : "filter-btn" }, String(i18n.bookmarkFilterAll).toUpperCase()),
button({ type: "submit", name: "filter", value: "mine", class: filter === "mine" ? "filter-btn active" : "filter-btn" }, String(i18n.bookmarkFilterMine).toUpperCase()),
button({ type: "submit", name: "filter", value: "recent", class: filter === "recent" ? "filter-btn active" : "filter-btn" }, String(i18n.bookmarkFilterRecent).toUpperCase()),
button({ type: "submit", name: "filter", value: "favorites", class: filter === "favorites" ? "filter-btn active" : "filter-btn" }, String(i18n.bookmarkFilterFavorites).toUpperCase()),
button({ type: "submit", name: "filter", value: "top", class: filter === "top" ? "filter-btn active" : "filter-btn" }, String(i18n.bookmarkFilterTop).toUpperCase()),
button({ type: "submit", name: "filter", value: "create", class: "create-button" }, i18n.bookmarkCreateButton)
)
)
),
section(
filter === "edit" || filter === "create"
? renderBookmarkForm(filter, bookmarkId, bookmarkToEdit || {}, tags, { ...params, filter })
? renderBookmarkForm(filter, bookmarkId, bookmarkToEdit || {}, tags, { ...params, filter, spreadWarning: bookmarkEditWarning })
: section(
div(
{ class: "bookmarks-search" },
@ -382,9 +254,9 @@ exports.bookmarkView = async (bookmarks, filter = "all", bookmarkId = null, para
{ class: "filter-box__controls" },
select(
{ name: "sort", class: "filter-box__select" },
option({ value: "recent", selected: sort === "recent" }, i18n.bookmarkSortRecent),
option({ value: "oldest", selected: sort === "oldest" }, i18n.bookmarkSortOldest),
option({ value: "top", selected: sort === "top" }, i18n.bookmarkSortTop)
option({ value: "recent", ...(sort === "recent" ? { selected: true } : {})}, i18n.bookmarkSortRecent),
option({ value: "oldest", ...(sort === "oldest" ? { selected: true } : {})}, i18n.bookmarkSortOldest),
option({ value: "top", ...(sort === "top" ? { selected: true } : {})}, i18n.bookmarkSortTop)
),
button({ type: "submit", class: "filter-box__button" }, i18n.bookmarkSearchButton)
)
@ -409,7 +281,7 @@ exports.singleBookmarkView = async (bookmark, filter = "all", comments = [], par
const lastVisit = bookmark.lastVisit ? moment(bookmark.lastVisit) : null;
const lastVisitTxt =
lastVisit && lastVisit.isValid()
? `${lastVisit.format("YYYY/MM/DD HH:mm:ss")} (${lastVisit.fromNow()})`
? `${lastVisit.format("YYYY/MM/DD HH:mm")} (${lastVisit.fromNow()})`
: i18n.noLastVisit;
const urlLink = bookmark.url
@ -422,10 +294,7 @@ exports.singleBookmarkView = async (bookmark, filter = "all", comments = [], par
].filter(Boolean);
const sideActions = [];
sideActions.push(renderFavoriteToggle(bookmark, returnTo));
if (bookmark.author && String(bookmark.author) !== String(userId)) {
sideActions.push(renderPMButton(bookmark.author));
}
sideActions.push(renderPMButton(bookmark.author));
if (isAuthor && !hasOpinions) {
sideActions.push(form(
{ method: "GET", action: `/bookmarks/edit/${encodeURIComponent(bookmark.id)}` },
@ -443,25 +312,33 @@ exports.singleBookmarkView = async (bookmark, filter = "all", comments = [], par
const tagsNode = renderTags(bookmark.tags);
const detailActions = div({ class: "card-header activity-card-header" },
renderContentActions(bookmark.id, null, {
author: bookmark.author,
favKind: 'bookmarks',
isFavorite: bookmark.isFavorite,
spread: params.spreads || null,
returnTo,
reportTitle: bookmark.title
})
);
const bookmarkSide = div({ class: "tribe-side" },
div({ class: "shop-title-row" },
h2({ class: "tribe-card-title" }, bookmark.category || bookmark.url || ""),
h2({ class: "tribe-card-title" }, bookmark.url ? urlLink : (bookmark.title || "")),
renderReachChip(isClearnet, i18n)
),
bookmark.title && bookmark.url ? p({ class: "bookmark-subtitle" }, bookmark.title) : null,
chips.length ? div({ class: "card-chips-row" }, ...chips) : null,
safeText(bookmark.description)
? p({ class: "tribe-side-description" }, ...renderUrl(bookmark.description))
: null,
safeText(bookmark.category)
? renderCardField(i18n.bookmarkCategoryLabel + ":", safeText(bookmark.category))
: null,
tagsNode,
div({ class: "card-spread-centered" }, renderSpreadButton(bookmark.id, params.spreads)),
sideActions.length ? div({ class: "tribe-side-actions" }, ...sideActions) : null
);
const bookmarkMain = div({ class: "tribe-main" },
renderCardField(i18n.bookmarkUrlLabel + ":", urlLink),
detailActions,
renderCardField(i18n.bookmarkLastVisitLabel + ":", lastVisitTxt),
(() => {
const createdTs = bookmark.createdAt ? new Date(bookmark.createdAt).getTime() : NaN;
@ -469,18 +346,20 @@ exports.singleBookmarkView = async (bookmark, filter = "all", comments = [], par
const showUpdated = Number.isFinite(updatedTs) && (!Number.isFinite(createdTs) || updatedTs !== createdTs);
return p(
{ class: "card-footer" },
span({ class: "date-link" }, `${moment(bookmark.createdAt).format("YYYY/MM/DD HH:mm:ss")} ${i18n.performed} `),
span({ class: "date-link" }, `${moment(bookmark.createdAt).format("YYYY/MM/DD HH:mm")} ${i18n.performed} `),
userLink(bookmark.author),
showUpdated
? span(
{ class: "votations-comment-date" },
` | ${i18n.bookmarkUpdatedAt}: ${moment(bookmark.updatedAt).format("YYYY/MM/DD HH:mm:ss")}`
` | ${i18n.bookmarkUpdatedAt}: ${moment(bookmark.updatedAt).format("YYYY/MM/DD HH:mm")}`
)
: null
);
})(),
renderOpinionsVoting('/bookmarks/opinions', bookmark.id, bookmark.opinions, returnTo, bookmark.opinions_inhabitants),
renderBookmarkCommentsSection(bookmark.id, bookmark.rootId, comments, returnTo)
renderEngagement(bookmark.id,
renderOpinionsVoting('/bookmarks/opinions', bookmark.id, bookmark.opinions, returnTo, bookmark.opinions_inhabitants),
renderBookmarkCommentsSection(bookmark.id, bookmark.rootId, comments, returnTo)
)
);
return template(
@ -496,11 +375,11 @@ exports.singleBookmarkView = async (bookmark, filter = "all", comments = [], par
{ method: "GET", action: "/bookmarks", class: "ui-toolbar ui-toolbar--filters" },
input({ type: "hidden", name: "q", value: q }),
input({ type: "hidden", name: "sort", value: sort }),
button({ type: "submit", name: "filter", value: "all", class: filter === "all" ? "filter-btn active" : "filter-btn" }, i18n.bookmarkFilterAll),
button({ type: "submit", name: "filter", value: "mine", class: filter === "mine" ? "filter-btn active" : "filter-btn" }, i18n.bookmarkFilterMine),
button({ type: "submit", name: "filter", value: "top", class: filter === "top" ? "filter-btn active" : "filter-btn" }, i18n.bookmarkFilterTop),
button({ type: "submit", name: "filter", value: "favorites", class: filter === "favorites" ? "filter-btn active" : "filter-btn" }, i18n.bookmarkFilterFavorites),
button({ type: "submit", name: "filter", value: "recent", class: filter === "recent" ? "filter-btn active" : "filter-btn" }, i18n.bookmarkFilterRecent),
button({ type: "submit", name: "filter", value: "all", class: filter === "all" ? "filter-btn active" : "filter-btn" }, String(i18n.bookmarkFilterAll).toUpperCase()),
button({ type: "submit", name: "filter", value: "mine", class: filter === "mine" ? "filter-btn active" : "filter-btn" }, String(i18n.bookmarkFilterMine).toUpperCase()),
button({ type: "submit", name: "filter", value: "recent", class: filter === "recent" ? "filter-btn active" : "filter-btn" }, String(i18n.bookmarkFilterRecent).toUpperCase()),
button({ type: "submit", name: "filter", value: "favorites", class: filter === "favorites" ? "filter-btn active" : "filter-btn" }, String(i18n.bookmarkFilterFavorites).toUpperCase()),
button({ type: "submit", name: "filter", value: "top", class: filter === "top" ? "filter-btn active" : "filter-btn" }, String(i18n.bookmarkFilterTop).toUpperCase()),
button({ type: "submit", name: "filter", value: "create", class: "create-button" }, i18n.bookmarkCreateButton)
)
),

File diff suppressed because it is too large Load diff