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
|
|
@ -1,6 +1,7 @@
|
|||
const { form, button, div, h2, p, section, textarea, label, input, br, img, a, select, option, span, table, tr, td } = require("../server/node_modules/hyperaxe");
|
||||
const moment = require("../server/node_modules/moment");
|
||||
const { template, i18n, userLink, renderVisibilityChip, renderDocumentActions } = require('./main_views');
|
||||
const { renderUrl } = require('../backend/renderUrl');
|
||||
const { renderStyledText } = require('../backend/renderStyledText');
|
||||
|
||||
const generateCVBox = (label, content, className) => {
|
||||
return div({ class: `cv-box ${className}` },
|
||||
|
|
@ -15,7 +16,7 @@ exports.createCVView = async (cv = {}, editMode = false) => {
|
|||
return template(
|
||||
title,
|
||||
section(
|
||||
div({ class: "tags-header" },
|
||||
div({ class: "tags-header module-header-line" },
|
||||
h2(title),
|
||||
p(i18n.cvDescription)
|
||||
),
|
||||
|
|
@ -44,34 +45,36 @@ exports.createCVView = async (cv = {}, editMode = false) => {
|
|||
label(i18n.cvNameLabel), br(),
|
||||
input({ type: "text", name: "name", required: true, value: cv.name || "" }), br(),
|
||||
label(i18n.cvDescriptionLabel), br(),
|
||||
textarea({ name: "description", required: true, rows: 4 }, cv.description || ""), br(),
|
||||
textarea({ maxlength: "5000", name: "description", required: true, rows: 4 }, cv.description || ""), br(),
|
||||
label(i18n.cvLanguagesLabel), br(),
|
||||
input({ type: "text", name: "languages", value: cv.languages || "" }), br(),
|
||||
label(i18n.cvPhotoLabel), br(),
|
||||
input({ type: "file", name: "image" }), br(), br(),
|
||||
label(i18n.cvPdfLabel), br(),
|
||||
input({ type: "file", name: "cvPdf", accept: "application/pdf" }), br(), br(),
|
||||
label(i18n.cvPersonalExperiencesLabel), br(),
|
||||
textarea({ name: "personalExperiences", rows: 4 }, cv.personalExperiences || ""), br(),
|
||||
textarea({ maxlength: "5000", name: "personalExperiences", rows: 4 }, cv.personalExperiences || ""), br(),
|
||||
label(i18n.cvPersonalSkillsLabel), br(),
|
||||
input({ type: "text", name: "personalSkills", required: true, value: (cv.personalSkills || []).join(", ") }), br()
|
||||
], "personal"),
|
||||
|
||||
generateCVBox(i18n.cvOasis, [
|
||||
label(i18n.cvOasisExperiencesLabel), br(),
|
||||
textarea({ name: "oasisExperiences", rows: 4 }, cv.oasisExperiences || ""), br(),
|
||||
textarea({ maxlength: "5000", name: "oasisExperiences", rows: 4 }, cv.oasisExperiences || ""), br(),
|
||||
label(i18n.cvOasisSkillsLabel), br(),
|
||||
input({ type: "text", name: "oasisSkills", value: (cv.oasisSkills || []).join(", ") }), br()
|
||||
], "oasis"),
|
||||
|
||||
generateCVBox(i18n.cvEducational, [
|
||||
label(i18n.cvEducationExperiencesLabel), br(),
|
||||
textarea({ name: "educationExperiences", rows: 4 }, cv.educationExperiences || ""), br(),
|
||||
textarea({ maxlength: "5000", name: "educationExperiences", rows: 4 }, cv.educationExperiences || ""), br(),
|
||||
label(i18n.cvEducationalSkillsLabel), br(),
|
||||
input({ type: "text", name: "educationalSkills", value: (cv.educationalSkills || []).join(", ") }), br()
|
||||
], "education"),
|
||||
|
||||
generateCVBox(i18n.cvProfessional, [
|
||||
label(i18n.cvProfessionalExperiencesLabel), br(),
|
||||
textarea({ name: "professionalExperiences", rows: 4 }, cv.professionalExperiences || ""), br(),
|
||||
textarea({ maxlength: "5000", name: "professionalExperiences", rows: 4 }, cv.professionalExperiences || ""), br(),
|
||||
label(i18n.cvProfessionalSkillsLabel), br(),
|
||||
input({ type: "text", name: "professionalSkills", value: (cv.professionalSkills || []).join(", ") }), br()
|
||||
], "professional"),
|
||||
|
|
@ -104,23 +107,38 @@ exports.createCVView = async (cv = {}, editMode = false) => {
|
|||
)
|
||||
};
|
||||
|
||||
exports.cvView = async (cv) => {
|
||||
exports.cvView = async (cv, certificates = []) => {
|
||||
const title = i18n.cvTitle;
|
||||
|
||||
if (!cv) {
|
||||
return template(
|
||||
title,
|
||||
section(
|
||||
div({ class: "tags-header" },
|
||||
div({ class: "tags-header module-header-line" },
|
||||
h2(title),
|
||||
p(i18n.cvDescription)
|
||||
),
|
||||
div({ class: "no-cv" },
|
||||
p(i18n.cvNoCV),
|
||||
div({ class: "filters" },
|
||||
form({ method: "GET", action: "/cv/create" },
|
||||
button({ type: "submit" }, i18n.cvCreateButton)
|
||||
button({ type: "submit", class: "create-button" }, i18n.cvCreateButton)
|
||||
)
|
||||
)
|
||||
),
|
||||
div({ class: "no-content-box" }, p(i18n.cvNoCV)),
|
||||
Array.isArray(certificates) && certificates.length
|
||||
? div({ class: "cv-section-block school-certificates" },
|
||||
h2(i18n.schoolCertificates),
|
||||
certificates.map(cert =>
|
||||
div({ class: "school-certificate" },
|
||||
span("🎓 "),
|
||||
a({ href: `/school/course/${encodeURIComponent(cert.courseId)}` }, cert.courseTitle || cert.courseId),
|
||||
span(" — "),
|
||||
userLink(cert.author),
|
||||
span({ class: "school-certificate-date" }, ` (${moment(cert.createdAt).format("YYYY/MM/DD")})`),
|
||||
a({ href: `/school/certificate/pdf/${encodeURIComponent(cert.courseId)}/${encodeURIComponent(cert.id)}`, class: "filter-btn school-cert-pdf" }, `⬇ ${i18n.schoolCertificatePdf || "PDF"}`)
|
||||
)
|
||||
)
|
||||
)
|
||||
: null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -144,7 +162,7 @@ exports.cvView = async (cv) => {
|
|||
const renderBlock = (heading, body, list) => (body || skills(list).length)
|
||||
? div({ class: "cv-box" },
|
||||
h2({ class: "job-section-title" }, heading),
|
||||
body ? p({ class: "tribe-side-description" }, ...renderUrl(String(body))) : null,
|
||||
body ? p({ class: "tribe-side-description" }, ...renderStyledText(String(body))) : null,
|
||||
renderSkillTags(list)
|
||||
)
|
||||
: null;
|
||||
|
|
@ -163,15 +181,15 @@ exports.cvView = async (cv) => {
|
|||
if (cv.preferences) pushRow(i18n.cvPreferencesLabel, cv.preferences);
|
||||
if (cv.languages) pushRow(i18n.cvLanguagesLabel, String(cv.languages).toUpperCase());
|
||||
pushRow(i18n.cvAiManaged, cv.aiManaged === false ? i18n.switchOff : `${i18n.switchOn} · ${cv.matchThreshold != null ? cv.matchThreshold : 80}%`);
|
||||
pushRow(i18n.cvCreatedAt, new Date(cv.createdAt).toLocaleString());
|
||||
if (cv.updatedAt) pushRow(i18n.cvUpdatedAt, new Date(cv.updatedAt).toLocaleString());
|
||||
pushRow(i18n.cvCreatedAt, moment(cv.createdAt).format("YYYY/MM/DD HH:mm"));
|
||||
if (cv.updatedAt) pushRow(i18n.cvUpdatedAt, moment(cv.updatedAt).format("YYYY/MM/DD HH:mm"));
|
||||
|
||||
const cvSide = div({ class: "tribe-side" },
|
||||
div({ class: "shop-title-row" },
|
||||
h2({ class: "tribe-card-title" }, cv.name || i18n.unnamed || 'Anonymous')
|
||||
),
|
||||
cv.photo
|
||||
? img({ src: `/blob/${encodeURIComponent(cv.photo)}`, class: "cv-photo tribe-detail-image" })
|
||||
? img({ loading: 'lazy', src: `/blob/${encodeURIComponent(cv.photo)}`, class: "cv-photo tribe-detail-image" })
|
||||
: null,
|
||||
(cv.contact || cv.author)
|
||||
? div({ class: 'profile-qr' },
|
||||
|
|
@ -179,6 +197,9 @@ exports.cvView = async (cv) => {
|
|||
img({ class: 'profile-qr-img', src: `/qr/${encodeURIComponent(cv.contact || cv.author)}?size=240`, alt: 'QR' })))
|
||||
: null,
|
||||
cv.contact ? p(userLink(cv.contact)) : null,
|
||||
cv.pdf
|
||||
? p(a({ href: `/blob/${encodeURIComponent(cv.pdf)}`, target: "_blank", rel: "noopener", class: "filter-btn" }, "📄 " + i18n.cvPdfLabel))
|
||||
: null,
|
||||
table({ class: "tribe-info-table jobs-info-table" }, ...infoRows),
|
||||
uniqueSkills.length
|
||||
? div({ class: "tribe-card-members" },
|
||||
|
|
@ -210,19 +231,34 @@ exports.cvView = async (cv) => {
|
|||
cv.description
|
||||
? div({ class: "job-section" },
|
||||
h2({ class: "job-section-title" }, i18n.cvDescriptionLabel),
|
||||
p({ class: "tribe-side-description" }, ...renderUrl(String(cv.description)))
|
||||
p({ class: "tribe-side-description" }, ...renderStyledText(String(cv.description)))
|
||||
)
|
||||
: null,
|
||||
renderBlock(i18n.cvPersonal, cv.personalExperiences, cv.personalSkills),
|
||||
renderBlock(i18n.cvEducationalView, cv.educationExperiences, cv.educationalSkills),
|
||||
renderBlock(i18n.cvProfessionalView, cv.professionalExperiences, cv.professionalSkills),
|
||||
renderBlock(i18n.cvOasisContributorView, cv.oasisExperiences, cv.oasisSkills)
|
||||
renderBlock(i18n.cvOasisContributorView, cv.oasisExperiences, cv.oasisSkills),
|
||||
Array.isArray(certificates) && certificates.length
|
||||
? div({ class: "cv-section-block school-certificates" },
|
||||
h2(i18n.schoolCertificates),
|
||||
certificates.map(cert =>
|
||||
div({ class: "school-certificate" },
|
||||
span("🎓 "),
|
||||
a({ href: `/school/course/${encodeURIComponent(cert.courseId)}` }, cert.courseTitle || cert.courseId),
|
||||
span(" — "),
|
||||
userLink(cert.author),
|
||||
span({ class: "school-certificate-date" }, ` (${moment(cert.createdAt).format("YYYY/MM/DD")})`),
|
||||
a({ href: `/school/certificate/pdf/${encodeURIComponent(cert.courseId)}/${encodeURIComponent(cert.id)}`, class: "filter-btn school-cert-pdf" }, `⬇ ${i18n.schoolCertificatePdf || "PDF"}`)
|
||||
)
|
||||
)
|
||||
)
|
||||
: null
|
||||
);
|
||||
|
||||
return template(
|
||||
title,
|
||||
section(
|
||||
div({ class: "tags-header" },
|
||||
div({ class: "tags-header module-header-line" },
|
||||
h2(title),
|
||||
p(i18n.cvDescription)
|
||||
),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue