upstream: integrar Oasis 1.3.1
Base del merge a tres bandas: git 0.9.5 de epsylon, esta rama y el src de la
APK oficial 1.3.1 (la variante movil: OASIS_MOBILE, regex sin \p{}, audio y
video de Android). Corre en el mismo nodejs-mobile 18.20.4 que ya usabamos.
Se conserva de la rama: Karvan, identidades y multicuenta, topbar y barra
inferior, borrado de EXIF sin sharp, filtro hot del foro, RTC sin STUN y los
modulos apagados en movil (wallet, banking, torrents).
Se toma de upstream lo que ya hace de serie: respuesta citada en chats, ssb-lan
en movil, configuracion en ~/.ssb/oasis (sobrevive a reinstalar el backend).
El asistente de bienvenida deja elegir workflow tambien en movil.
This commit is contained in:
parent
8d838dd8b1
commit
c332afd6ea
265 changed files with 80970 additions and 29167 deletions
383
src/views/wiki_view.js
Normal file
383
src/views/wiki_view.js
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
const { div, h2, h3, p, section, button, form, a, span, br, textarea, input, label, select, option, ul, li, img } = require("../server/node_modules/hyperaxe");
|
||||
const { clearnetItemHref, template, i18n, userLink, renderStateChip, renderContentActions, renderSubscriptionBox, renderModuleStats, moduleIsEmpty, renderCardMetaRow, paged } = require("./main_views");
|
||||
const { renderEncryptedChip, renderReachChip, renderClearnetSelector, renderClearnetSwitch, renderLicenseChip, renderLicenseSelect, renderTribeOriginChip } = require("./clearnet_view");
|
||||
const { renderStyledText, richTextarea } = require("../backend/renderStyledText");
|
||||
const { WIKILINK_RE, slugify, linkTarget } = require("../models/wiki_model");
|
||||
const moment = require("../server/node_modules/moment");
|
||||
const { config } = require("../server/SSB_server.js");
|
||||
|
||||
const userId = config.keys.id;
|
||||
const FILTERS = ["all", "recent", "mine", "linked"];
|
||||
|
||||
const pageHref = (idOrSlug, tribeId = null) =>
|
||||
`/wiki/${encodeURIComponent(idOrSlug)}${tribeId ? `?tribeId=${encodeURIComponent(tribeId)}` : ""}`;
|
||||
|
||||
const listHref = (filter, q, tribeId = null) => {
|
||||
const parts = [`filter=${encodeURIComponent(filter || "all")}`];
|
||||
if (q) parts.push(`q=${encodeURIComponent(q)}`);
|
||||
if (tribeId) parts.push(`tribeId=${encodeURIComponent(tribeId)}`);
|
||||
return `/wiki?${parts.join("&")}`;
|
||||
};
|
||||
|
||||
const renderWikiBody = (body, opts = {}) => {
|
||||
const missing = new Set(opts.missing || []);
|
||||
const tribeId = opts.tribeId || null;
|
||||
const wikiLink = (target, slug, label) => {
|
||||
const isMissing = missing.has(slug);
|
||||
return a({
|
||||
href: isMissing ? `/wiki?filter=create&title=${encodeURIComponent(target)}${tribeId ? `&tribeId=${encodeURIComponent(tribeId)}` : ""}` : pageHref(slug, tribeId),
|
||||
class: isMissing ? "wiki-link wiki-link-missing" : "wiki-link",
|
||||
title: isMissing ? (i18n.wikiMissingLink || "Page does not exist yet") : label
|
||||
}, label);
|
||||
};
|
||||
const paragraphs = String(body || "").replace(/\r\n/g, "\n").split(/\n{2,}/);
|
||||
return div({ class: "wiki-body" },
|
||||
...paragraphs.filter(par => par.trim()).map(par =>
|
||||
p({ class: "wiki-paragraph" }, ...renderStyledText(par, { wikiLink, zoomImages: true }))
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const diffLines = (oldText, newText) => {
|
||||
const a = String(oldText || "").replace(/\r\n/g, "\n").split("\n");
|
||||
const b = String(newText || "").replace(/\r\n/g, "\n").split("\n");
|
||||
const n = a.length, m = b.length;
|
||||
const lcs = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
|
||||
for (let i = n - 1; i >= 0; i--) {
|
||||
for (let j = m - 1; j >= 0; j--) {
|
||||
lcs[i][j] = a[i] === b[j] ? lcs[i + 1][j + 1] + 1 : Math.max(lcs[i + 1][j], lcs[i][j + 1]);
|
||||
}
|
||||
}
|
||||
const out = [];
|
||||
let i = 0, j = 0;
|
||||
while (i < n && j < m) {
|
||||
if (a[i] === b[j]) { out.push({ type: "ctx", text: a[i] }); i++; j++; }
|
||||
else if (lcs[i + 1][j] >= lcs[i][j + 1]) { out.push({ type: "del", text: a[i] }); i++; }
|
||||
else { out.push({ type: "add", text: b[j] }); j++; }
|
||||
}
|
||||
while (i < n) { out.push({ type: "del", text: a[i++] }); }
|
||||
while (j < m) { out.push({ type: "add", text: b[j++] }); }
|
||||
return out;
|
||||
};
|
||||
|
||||
const renderDiff = (oldText, newText) =>
|
||||
div({ class: "wiki-diff" },
|
||||
...diffLines(oldText, newText).map(l =>
|
||||
div({ class: `wiki-diff-line wiki-diff-${l.type}` }, span({ class: "wiki-diff-sign" }, l.type === "add" ? "+" : l.type === "del" ? "−" : " "), span({ class: "wiki-diff-text" }, l.text || " "))
|
||||
)
|
||||
);
|
||||
|
||||
const reachChip = (page) =>
|
||||
page.tribeId || page.encrypted
|
||||
? null
|
||||
: renderReachChip(!!page.clearnet, i18n, page.clearnet ? clearnetItemHref('wiki', page.title, page.id) : null);
|
||||
|
||||
const statusChip = (page) =>
|
||||
renderStateChip(page.editPolicy === "author" ? "closed" : "open", "", page.editPolicy === "author" ? i18n.wikiStatusClosed : i18n.wikiStatusOpen);
|
||||
|
||||
const pageChips = (page, params = {}, { withStatus = true } = {}) => [
|
||||
page.encrypted ? renderEncryptedChip(i18n) : null,
|
||||
withStatus ? statusChip(page) : null,
|
||||
page.versionCount > 1 ? renderStateChip("neutral", "", `${page.versionCount} ${i18n.wikiVersions || "versions"}`) : null,
|
||||
page.isLinked ? renderStateChip("open", "", i18n.wikiLinkedChip || "Linked") : null
|
||||
].filter(Boolean);
|
||||
|
||||
const renderTags = (tags) => (Array.isArray(tags) && tags.length)
|
||||
? div({ class: "card-tags" }, ...tags.map(t => a({ href: `/search?query=%23${encodeURIComponent(t)}`, class: "tag-link" }, `#${t}`)))
|
||||
: null;
|
||||
|
||||
const renderPageCard = (page, params = {}) => {
|
||||
const origin = page.tribeOrigin || null;
|
||||
const href = origin ? origin.href : pageHref(page.tribeId ? page.id : page.slug, page.tribeId);
|
||||
return div({ class: "tribe-card wiki-card" },
|
||||
div({ class: "card-header activity-card-header" },
|
||||
span(),
|
||||
renderContentActions(page.id, href, origin
|
||||
? { author: page.author, reportTitle: page.title }
|
||||
: { author: page.author, reportTitle: page.title, favKind: 'wiki', isFavorite: page.isFavorite, spread: (params.spreadMap && params.spreadMap.get(page.id)) || null, deleteAction: !page.tribeId && String(page.author) === String(userId) ? `/wiki/delete/${encodeURIComponent(page.id)}` : undefined })
|
||||
),
|
||||
div({ class: "tribe-card-body" },
|
||||
page.image ? a({ href }, img({ loading: 'lazy', class: "wiki-card-cover", src: `/blob/${encodeURIComponent(page.image)}`, alt: page.title || "" })) : null,
|
||||
div({ class: "shop-title-row" }, h2({ class: "tribe-card-title" }, a({ href }, page.title || "—"))),
|
||||
div({ class: "card-chips-row" }, ...(origin ? [renderTribeOriginChip(origin)] : pageChips(page)), !origin && page.clearnet === true ? reachChip(page) : null, renderLicenseChip(page.license)),
|
||||
origin && params.withBody && page.body ? renderWikiBody(page.body) : null
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const renderFilters = (filter, q, params = {}, census = []) => {
|
||||
const tribeId = params.tribeId || null;
|
||||
const chipVisible = (mode) => {
|
||||
if (mode === filter || mode === "all") return true;
|
||||
if (mode === "mine") return census.some(p => String(p.author) === String(userId) || (p.versions || []).some(v => String(v.author) === String(userId)));
|
||||
if (mode === "recent") return census.length > 0;
|
||||
if (mode === "linked") return census.some(p => p.isLinked);
|
||||
return true;
|
||||
};
|
||||
const labelOf = (mode) => String(i18n[`wikiFilter${mode.charAt(0).toUpperCase() + mode.slice(1)}`] || mode).toUpperCase();
|
||||
return div({ class: "filters" },
|
||||
form({ method: "GET", action: "/wiki", class: "ui-toolbar ui-toolbar--filters" },
|
||||
input({ type: "hidden", name: "q", value: q || "" }),
|
||||
tribeId ? input({ type: "hidden", name: "tribeId", value: tribeId }) : null,
|
||||
...(params.emptyMod ? [] : [
|
||||
...FILTERS.filter(chipVisible).map(mode => button({ type: "submit", name: "filter", value: mode, class: filter === mode ? "filter-btn active" : "filter-btn" }, labelOf(mode))),
|
||||
...((census.length > 0 || filter === "changes") ? [button({ type: "submit", name: "filter", value: "changes", class: filter === "changes" ? "filter-btn active" : "filter-btn" }, String(i18n.wikiChanges || "Recent changes").toUpperCase())] : [])
|
||||
]),
|
||||
button({ type: "submit", name: "filter", value: "create", class: "create-button" }, i18n.wikiCreate)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const renderForm = (page, params = {}) => {
|
||||
const tribeId = (page && page.tribeId) || params.tribeId || "";
|
||||
const draft = params.draft || null;
|
||||
const status = draft ? (String(draft.status || "OPEN").toUpperCase() === "CLOSED" ? "CLOSED" : "OPEN") : (page && page.editPolicy === "author" ? "CLOSED" : "OPEN");
|
||||
const titleValue = draft ? draft.title : (page ? page.title : (params.title || ""));
|
||||
const bodyValue = draft ? draft.body : (page ? page.body : "");
|
||||
const tagsValue = draft ? draft.tags : (page ? page.tags.join(", ") : "");
|
||||
const summaryValue = draft ? draft.summary : "";
|
||||
const licenseValue = draft && draft.license ? draft.license : (page ? page.license : "");
|
||||
const canLicense = !page || String(page.author) === String(userId);
|
||||
const statusOptions = [
|
||||
{ value: "OPEN", label: i18n.wikiStatusOpen },
|
||||
{ value: "CLOSED", label: i18n.wikiStatusClosed }
|
||||
];
|
||||
return div({ class: "div-center audio-form" },
|
||||
h2(page ? i18n.wikiUpdateSectionTitle : i18n.wikiCreateSectionTitle),
|
||||
draft
|
||||
? section({ class: "post-preview wiki-preview" },
|
||||
div({ class: "preview-content" },
|
||||
h2(i18n.messagePreview),
|
||||
h3({ class: "wiki-page-title" }, titleValue || "—"),
|
||||
renderWikiBody(bodyValue, { missing: [], tribeId: tribeId || null })
|
||||
)
|
||||
)
|
||||
: null,
|
||||
form({ method: "POST", action: page ? `/wiki/update/${encodeURIComponent(page.id)}` : "/wiki/create", enctype: "multipart/form-data" },
|
||||
tribeId ? input({ type: "hidden", name: "tribeId", value: tribeId }) : null,
|
||||
page ? input({ type: "hidden", name: "pageId", value: page.id }) : null,
|
||||
params.returnTo ? input({ type: "hidden", name: "returnTo", value: params.returnTo }) : null,
|
||||
label(i18n.wikiTitleLabel), br(),
|
||||
input({ type: "text", name: "title", maxlength: "100", required: true, placeholder: i18n.wikiTitlePlaceholder, value: titleValue }), br(),
|
||||
label(i18n.wikiBodyLabel), br(),
|
||||
richTextarea({ name: "body", rows: 14, maxlength: "20000", placeholder: i18n.wikiBodyPlaceholder }, bodyValue), br(),
|
||||
label(i18n.uploadMedia), br(),
|
||||
input({ type: "file", name: "blob", accept: "image/*,video/*,audio/*,application/pdf,.torrent" }), br(), br(),
|
||||
page ? [label(i18n.wikiSummaryLabel), br(), input({ type: "text", name: "summary", maxlength: "200", placeholder: i18n.wikiSummaryPlaceholder, value: summaryValue }), br()] : null,
|
||||
page
|
||||
? null
|
||||
: [label(i18n.wikiStatusLabel), br(), select({ name: "status" }, ...statusOptions.map(o => option({ value: o.value, ...(o.value === status ? { selected: true } : {}) }, o.label))), br(), br()],
|
||||
label(i18n.wikiTagsLabel), br(),
|
||||
input({ type: "text", name: "tags", maxlength: "200", placeholder: i18n.wikiTagsPlaceholder, value: tagsValue }), br(),
|
||||
canLicense ? renderLicenseSelect(licenseValue, i18n) : null,
|
||||
!tribeId && canLicense ? renderClearnetSelector(draft ? draft.clearnet === true : !!(page && page.clearnet), i18n) : null,
|
||||
br(),
|
||||
button({ type: "submit", class: "filter-btn", formaction: "/wiki/preview", attrs: { formmethod: "POST" } }, i18n.wikiPreview),
|
||||
" ",
|
||||
button({ type: "submit", class: "create-button" }, page ? i18n.wikiUpdate : i18n.wikiCreate)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const renderHeader = (tribe = null) =>
|
||||
div({ class: "tags-header module-header-line" },
|
||||
h2(tribe ? `${i18n.wikiTitle} · ${tribe.title}` : i18n.wikiTitle),
|
||||
p(i18n.wikiDescription)
|
||||
);
|
||||
|
||||
exports.wikiView = async (pages, filter = "all", params = {}) => {
|
||||
const list = Array.isArray(pages) ? pages : [];
|
||||
const q = String(params.q || "").trim();
|
||||
const census = Array.isArray(params.censusList) ? params.censusList : list;
|
||||
const isForm = filter === "create" || filter === "edit";
|
||||
const emptyMod = !isForm && census.length === 0 && !q;
|
||||
return template(
|
||||
i18n.wikiTitle,
|
||||
section(
|
||||
div({ class: "tags-header module-header-line" },
|
||||
h2(params.tribe ? `${i18n.wikiTitle} · ${params.tribe.title}` : i18n.wikiTitle),
|
||||
p(i18n.wikiDescription)
|
||||
),
|
||||
renderFilters(isForm ? "all" : filter, q, { ...params, emptyMod }, census)
|
||||
),
|
||||
section(
|
||||
isForm
|
||||
? renderForm(filter === "edit" ? params.page : null, params)
|
||||
: [
|
||||
emptyMod ? null : div({ class: "filters activity-filter-chips activity-toolbar-row" },
|
||||
renderModuleStats(list.length),
|
||||
form({ method: "GET", action: "/wiki", class: "filter-box" },
|
||||
input({ type: "hidden", name: "filter", value: filter }),
|
||||
params.tribeId ? input({ type: "hidden", name: "tribeId", value: params.tribeId }) : null,
|
||||
input({ type: "text", name: "q", value: q, placeholder: i18n.wikiSearchPlaceholder, class: "filter-box__input" }),
|
||||
div({ class: "filter-box__controls" }, button({ type: "submit", class: "filter-box__button" }, i18n.searchButton))
|
||||
)
|
||||
),
|
||||
div({ class: "tribe-grid" },
|
||||
list.length ? paged(list).map(pg => renderPageCard(pg, params)) : p(i18n.wikiNoPages)
|
||||
)
|
||||
]
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
exports.wikiChangesView = async (changes, params = {}) => {
|
||||
const list = Array.isArray(changes) ? changes : [];
|
||||
const census = Array.isArray(params.censusList) ? params.censusList : [];
|
||||
return template(
|
||||
i18n.wikiTitle,
|
||||
section(renderHeader(params.tribe || null), renderFilters("changes", "", { ...params, emptyMod: census.length === 0 }, census)),
|
||||
section(
|
||||
div({ class: "tags-header" }, h2(i18n.wikiChanges)),
|
||||
list.length
|
||||
? div({ class: "wiki-changes" }, ...paged(list).map(c =>
|
||||
div({ class: "card-section wiki-change" },
|
||||
div({ class: "card-field" },
|
||||
span({ class: "card-label" }, `#${c.index}`),
|
||||
span({ class: "card-value" }, a({ href: `${pageHref(c.pageId, c.tribeId)}${c.tribeId ? "&" : "?"}version=${encodeURIComponent(c.versionKey)}` }, c.title || c.slug))
|
||||
),
|
||||
c.summary ? p({ class: "wiki-change-summary" }, c.summary.startsWith("restore:") ? i18n.wikiRestoredSummary : c.summary) : null,
|
||||
p({ class: "card-footer" }, span({ class: "date-link" }, `${moment(c.createdAt).format("YYYY/MM/DD HH:mm")}`), userLink(c.author))
|
||||
)
|
||||
))
|
||||
: div({ class: "no-content-box" }, p({ class: "no-content" }, i18n.wikiNoChanges))
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const versionRow = (page, v, idx, current, tribeId) => {
|
||||
const isCurrent = v.key === page.tipId;
|
||||
const base = pageHref(page.tribeId ? page.id : page.slug, tribeId);
|
||||
const sep = tribeId ? "&" : "?";
|
||||
return div({ class: "card-section wiki-version" + (isCurrent ? " wiki-version-current" : "") },
|
||||
div({ class: "card-field" },
|
||||
span({ class: "card-label" }, `${i18n.wikiVersion} ${idx + 1}`),
|
||||
isCurrent ? span({ class: "card-value" }, renderStateChip("mutuals", "", i18n.wikiCurrentVersion)) : span({ class: "card-value" })
|
||||
),
|
||||
v.summary ? [br(), p({ class: "wiki-change-summary" }, v.summary.startsWith("restore:") ? i18n.wikiRestoredSummary : v.summary), br()] : null,
|
||||
div({ class: "tribe-side-actions wiki-version-actions" },
|
||||
form({ method: "GET", action: base },
|
||||
tribeId ? input({ type: "hidden", name: "tribeId", value: tribeId }) : null,
|
||||
input({ type: "hidden", name: "version", value: v.key }),
|
||||
button({ type: "submit", class: "update-btn" }, i18n.wikiViewVersion)),
|
||||
idx > 0
|
||||
? form({ method: "GET", action: base },
|
||||
tribeId ? input({ type: "hidden", name: "tribeId", value: tribeId }) : null,
|
||||
input({ type: "hidden", name: "diff", value: v.key }),
|
||||
button({ type: "submit", class: "update-btn" }, i18n.wikiDiff))
|
||||
: null,
|
||||
!isCurrent && page.canEdit
|
||||
? form({ method: "POST", action: `/wiki/restore/${encodeURIComponent(page.id)}` },
|
||||
input({ type: "hidden", name: "version", value: v.key }),
|
||||
tribeId ? input({ type: "hidden", name: "tribeId", value: tribeId }) : null,
|
||||
button({ type: "submit", class: "update-btn" }, i18n.wikiRestore))
|
||||
: null
|
||||
),
|
||||
p({ class: "card-footer" }, span({ class: "date-link" }, `${moment(v.createdAt).format("YYYY/MM/DD HH:mm")}`), userLink(v.author))
|
||||
);
|
||||
};
|
||||
|
||||
exports.wikiHistoryView = async (page, params = {}) => {
|
||||
const tribeId = page.tribeId || null;
|
||||
const versions = page.versions.slice().reverse();
|
||||
const census = Array.isArray(params.censusList) ? params.censusList : [];
|
||||
return template(
|
||||
page.title,
|
||||
section(renderHeader(params.tribe || null), renderFilters("all", "", { ...params, emptyMod: census.length === 0 }, census)),
|
||||
section(
|
||||
div({ class: "tags-header" }, h2(i18n.wikiHistory)),
|
||||
div({ class: "wiki-history" }, ...versions.map((v, i) => versionRow(page, v, page.versions.length - 1 - i, page.tipId, tribeId)))
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
exports.wikiPageView = async (page, params = {}) => {
|
||||
const tribeId = page.tribeId || null;
|
||||
const base = pageHref(tribeId ? page.id : page.slug, tribeId);
|
||||
const sep = tribeId ? "&" : "?";
|
||||
const version = params.version || null;
|
||||
const diffVersion = params.diff || null;
|
||||
const shownTitle = version ? version.title : page.title;
|
||||
const shownBody = version ? version.body : page.body;
|
||||
const census = Array.isArray(params.censusList) ? params.censusList : [];
|
||||
const subscriptionNodes = params.subscription
|
||||
? renderSubscriptionBox({ target: page.id, scope: "wiki", subscribed: params.subscription.subscribed, count: params.subscription.count, isOwner: page.isOwner, returnTo: base, inline: true, compact: true })
|
||||
: null;
|
||||
const barChips = [reachChip(page), statusChip(page)].filter(Boolean);
|
||||
const actions = div({ class: "tribe-side-actions wiki-actions-top" },
|
||||
span({ class: "wiki-actions-left" }, ...barChips),
|
||||
page.isOwner && !tribeId && !page.encrypted ? renderClearnetSwitch("wiki", page.rootId || page.id, !!page.clearnet) : null,
|
||||
...(subscriptionNodes && subscriptionNodes.length ? subscriptionNodes : []),
|
||||
page.canEdit && !version
|
||||
? form({ method: "GET", action: "/wiki" },
|
||||
input({ type: "hidden", name: "filter", value: "edit" }),
|
||||
input({ type: "hidden", name: "id", value: page.id }),
|
||||
tribeId ? input({ type: "hidden", name: "tribeId", value: tribeId }) : null,
|
||||
button({ type: "submit", class: "update-btn" }, i18n.wikiEdit))
|
||||
: null,
|
||||
form({ method: "GET", action: `/wiki/${encodeURIComponent(page.id)}/history` },
|
||||
tribeId ? input({ type: "hidden", name: "tribeId", value: tribeId }) : null,
|
||||
button({ type: "submit", class: "update-btn" }, `${i18n.wikiHistory} (${page.versionCount})`)),
|
||||
form({ method: "GET", action: `/wiki/${encodeURIComponent(page.id)}/pdf` },
|
||||
button({ type: "submit", class: "update-btn" }, i18n.generatePdf)),
|
||||
page.isOwner && tribeId
|
||||
? form({ method: "POST", action: `/wiki/delete/${encodeURIComponent(page.id)}` }, input({ type: "hidden", name: "tribeId", value: tribeId }), button({ type: "submit", class: "delete-btn" }, i18n.wikiDelete))
|
||||
: null
|
||||
);
|
||||
let diffBlock = null;
|
||||
if (diffVersion) {
|
||||
const idx = page.versions.findIndex(v => v.key === diffVersion.key);
|
||||
const prev = idx > 0 ? page.versions[idx - 1] : null;
|
||||
diffBlock = div({ class: "card-section" },
|
||||
h3(`${i18n.wikiDiff}: ${i18n.wikiVersion} ${idx} → ${idx + 1}`),
|
||||
renderDiff(prev ? prev.body : "", diffVersion.body)
|
||||
);
|
||||
}
|
||||
return template(
|
||||
page.title,
|
||||
section(renderHeader(params.tribe || null), renderFilters("all", "", { ...params, emptyMod: census.length === 0 }, census)),
|
||||
section(
|
||||
div({ class: "trending-card wiki-page" },
|
||||
div({ class: "card-header activity-card-header" },
|
||||
span(),
|
||||
renderContentActions(page.id, base, { author: page.author, reportTitle: page.title, favKind: 'wiki', isFavorite: page.isFavorite, spread: params.spread || null, deleteAction: page.isOwner && !tribeId ? `/wiki/delete/${encodeURIComponent(page.id)}` : undefined })
|
||||
),
|
||||
div({ class: "card-section" },
|
||||
actions,
|
||||
div({ class: "shop-title-row" }, h2({ class: "wiki-page-title" }, shownTitle), renderLicenseChip(page.license)),
|
||||
(() => { const rest = pageChips(page, params, { withStatus: false }); return rest.length ? div({ class: "card-chips-row" }, ...rest) : null; })(),
|
||||
version ? p({ class: "wiki-version-notice" }, `${i18n.wikiViewingVersion} (${moment(version.createdAt).format("YYYY/MM/DD HH:mm")}) — `, a({ href: base }, i18n.wikiBackToPage)) : null,
|
||||
diffBlock || renderWikiBody(shownBody, { missing: page.missingLinks, tribeId }),
|
||||
renderTags(page.tags),
|
||||
renderCardMetaRow(p({ class: "card-footer" },
|
||||
span({ class: "date-link" }, `${moment(page.createdAt).format("YYYY/MM/DD HH:mm")}`),
|
||||
userLink(page.author)
|
||||
))
|
||||
),
|
||||
),
|
||||
div({ class: "card-section wiki-backlinks" },
|
||||
h3(i18n.wikiLinkedPages),
|
||||
page.linkedPages && page.linkedPages.length
|
||||
? ul(...page.linkedPages.map(b => li(a({ href: pageHref(b.tribeId ? b.id : b.slug, b.tribeId) }, b.title))))
|
||||
: p(i18n.wikiNoLinkedPages)
|
||||
)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
exports.renderTribeWikiSection = (tribe, pages, toolbar = null, wrap = (pg, card) => card) => {
|
||||
const items = Array.isArray(pages) ? pages : [];
|
||||
const createBtn = form({ method: "GET", action: "/wiki" },
|
||||
input({ type: "hidden", name: "filter", value: "create" }),
|
||||
input({ type: "hidden", name: "tribeId", value: tribe.id }),
|
||||
button({ type: "submit", class: "create-button" }, i18n.tribeWikiCreate));
|
||||
const header = toolbar || div({ class: "tribe-content-header" }, h2(i18n.tribeSectionWiki), createBtn);
|
||||
if (!items.length) return div({ class: "tribe-content-list" }, header, div({ class: "no-content-box" }, p(i18n.tribeWikiEmpty)));
|
||||
return div({ class: "tribe-content-list" },
|
||||
header,
|
||||
div({ class: "tribe-grid" }, ...paged(items).map(pg => wrap(pg, renderPageCard(pg, { tribeId: tribe.id, withBody: true }))))
|
||||
);
|
||||
};
|
||||
|
||||
exports.renderWikiBody = renderWikiBody;
|
||||
exports.diffLines = diffLines;
|
||||
Loading…
Add table
Add a link
Reference in a new issue