ajustes: los seis accesos arriba y colores configurables
Arriba de Ajustes, una rejilla con lo que se elige en la bienvenida (identidad, perfil de uso, portada, comunidad y saludo) y Colores. Cada acceso baja a su bloque y cada bloque guarda al tocar. Colores: ocho paletas de un toque y fondo, texto y resalte a mano. Se guardan en la config (mobileColors) y van en /mobile-colors.css, la ultima hoja (la CSP no deja estilos en linea). Solo con el aspecto Acampada; elegir colores pasa a el. Si el texto no se lee sobre el fondo se cambia por negro o blanco y se avisa. En la bienvenida, Colores es un paso mas, despues del tipo de portada. Identidad y saludo envian a las rutas del dev de la bienvenida; fork_routes.js cambia su vuelta a /welcome por la vuelta a Ajustes. Todo en mobile_settings.js; en las vistas del dev solo hay llamadas a mobile_ui. Miniaturas de los skins rehechas (salia Karvan).
This commit is contained in:
parent
18098dd38e
commit
e861b05946
11 changed files with 709 additions and 18 deletions
|
|
@ -2010,6 +2010,7 @@ const template = (titlePrefix, ...elements) => {
|
|||
? [ link({ rel: "stylesheet", href: `/assets/styles/mobile.css?v=${assetVersion()}` }), acampadaBase(theme), themeLink, acampadaLink(theme) ]
|
||||
: [ themeLink, link({ rel: "stylesheet", href: `/assets/styles/mobile.css?v=${assetVersion()}`, media: "(max-width: 768px)" }) ]),
|
||||
process.env.OASIS_MOBILE === '1' ? link({ rel: "stylesheet", href: `/assets/styles/mobile_ux.css?v=${assetVersion()}` }) : null,
|
||||
process.env.OASIS_MOBILE === '1' ? require('./mobile_ui').colorsLink() : null,
|
||||
link({ rel: "icon", href: "/assets/images/favicon.svg" }),
|
||||
meta({ charset: "utf-8" }),
|
||||
meta({ name: "description", content: i18n.oasisDescription }),
|
||||
|
|
@ -2398,6 +2399,7 @@ exports.ainavHomeView = ({ recentTags = [] } = {}) => {
|
|||
? [ link({ rel: "stylesheet", href: `/assets/styles/mobile.css?v=${assetVersion()}` }), acampadaBase(theme), link({ rel: "stylesheet", href: `/assets/themes/${theme}.css?v=${assetVersion()}` }), acampadaLink(theme) ]
|
||||
: [ link({ rel: "stylesheet", href: `/assets/themes/${theme}.css?v=${assetVersion()}` }), link({ rel: "stylesheet", href: `/assets/styles/mobile.css?v=${assetVersion()}`, media: "(max-width: 768px)" }) ]),
|
||||
process.env.OASIS_MOBILE === '1' ? link({ rel: "stylesheet", href: `/assets/styles/mobile_ux.css?v=${assetVersion()}` }) : null,
|
||||
process.env.OASIS_MOBILE === '1' ? require('./mobile_ui').colorsLink() : null,
|
||||
link({ rel: "icon", href: "/assets/images/favicon.svg" }),
|
||||
meta({ charset: "utf-8" }),
|
||||
meta({ name: "description", content: i18n.oasisDescription }),
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ const ICONS = {
|
|||
data: '<ellipse cx="12" cy="5" rx="8" ry="3"/><path d="M4 5v14c0 1.7 3.6 3 8 3s8-1.3 8-3V5M4 12c0 1.7 3.6 3 8 3s8-1.3 8-3"/>',
|
||||
mic: '<rect x="9" y="2" width="6" height="12" rx="3"/><path d="M5 11a7 7 0 0 0 14 0M12 18v4"/>',
|
||||
camera: '<rect x="2" y="6" width="14" height="12" rx="2"/><path d="M16 10l6-3v10l-6-3z"/>',
|
||||
palette: '<path d="M12 3a9 9 0 1 0 0 18c1.1 0 1.8-.8 1.8-1.8 0-.5-.2-.9-.5-1.2-.3-.3-.5-.7-.5-1.2 0-1 .8-1.8 1.8-1.8H17a4 4 0 0 0 4-4c0-4.4-4-8-9-8z"/><circle cx="7.5" cy="11" r="1"/><circle cx="10.5" cy="7" r="1"/><circle cx="15.5" cy="7.5" r="1"/>',
|
||||
hangup: '<path d="M3 15.5c5-4.7 13-4.7 18 0l-2.5 2.5-3-1.5v-2.5a10 10 0 0 0-7 0v2.5l-3 1.5z"/>'
|
||||
};
|
||||
|
||||
|
|
|
|||
267
src/views/mobile_settings.js
Normal file
267
src/views/mobile_settings.js
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
"use strict";
|
||||
// Ajustes del movil: los seis accesos de arriba y lo que hay detras de cada uno.
|
||||
//
|
||||
// Lo que se elige en la bienvenida (identidad, perfil de uso, portada, comunidad y
|
||||
// saludo) tiene que poder cambiarse despues sin buscarlo. Arriba de Ajustes va una
|
||||
// rejilla con esos cinco y un sexto, Colores; cada acceso baja a su bloque, y cada
|
||||
// bloque guarda al tocar, igual que en la bienvenida.
|
||||
//
|
||||
// Los formularios de identidad y saludo envian a las rutas del dev (/welcome/profile,
|
||||
// /welcome/greeting) con un returnTo; fork_routes.js cambia su redireccion a
|
||||
// /welcome por la vuelta a Ajustes. Colores es propio: /settings/colors y la hoja
|
||||
// /mobile-colors.css, que va la ultima.
|
||||
//
|
||||
// Los colores son las variables --a26-* del tema Acampada; por eso al elegir colores
|
||||
// el aspecto pasa a Acampada (OasisMobile.css es del dev y lleva los suyos fijos).
|
||||
|
||||
const crypto = require("crypto");
|
||||
const { a, button, details, div, form, img, input, label, p, section, span, summary, textarea, h2 } = require("../server/node_modules/hyperaxe");
|
||||
const { iconNode } = require("./mobile_icons");
|
||||
|
||||
const t = () => require("./main_views").i18n;
|
||||
const cfgNow = () => { try { return require("../configs/config-manager.js").getConfig() || {}; } catch (_) { return {}; } };
|
||||
|
||||
// ---------- colores ----------
|
||||
|
||||
// [clave, fondo, texto, acento]; la primera es la de Acampada tal cual
|
||||
const PALETTES = [
|
||||
["acampada", "#ffffff", "#000000", "#00e5ff"],
|
||||
["noche", "#101114", "#f2f2f2", "#00e5ff"],
|
||||
["oro", "#121212", "#f2f2f2", "#ffd700"],
|
||||
["bosque", "#f3f7ef", "#13231a", "#3ccf7e"],
|
||||
["lila", "#faf7ff", "#1d1430", "#b48cff"],
|
||||
["brasa", "#fff8f2", "#1a0f0a", "#ff6a2b"],
|
||||
["papel", "#f5efe3", "#2a2420", "#ff4f8b"],
|
||||
["contraste", "#000000", "#ffffff", "#ffff00"]
|
||||
];
|
||||
const DEFAULT = PALETTES[0];
|
||||
|
||||
const HEX = /^#[0-9a-f]{6}$/i;
|
||||
const rgb = (hex) => [1, 3, 5].map(i => parseInt(hex.slice(i, i + 2), 16));
|
||||
const toHex = (c) => "#" + c.map(v => Math.round(Math.max(0, Math.min(255, v))).toString(16).padStart(2, "0")).join("");
|
||||
const mix = (x, y, k) => { const p = rgb(x), q = rgb(y); return toHex(p.map((v, i) => v + (q[i] - v) * k)); };
|
||||
const lum = (hex) => {
|
||||
const [r, g, b] = rgb(hex).map(v => { v /= 255; return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4); });
|
||||
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
||||
};
|
||||
const contrast = (x, y) => { const p = lum(x), q = lum(y); return (Math.max(p, q) + 0.05) / (Math.min(p, q) + 0.05); };
|
||||
// negro o blanco, el que mas se lea encima
|
||||
const readableOn = (hex) => contrast(hex, "#000000") >= contrast(hex, "#ffffff") ? "#000000" : "#ffffff";
|
||||
|
||||
// lo guardado, ya comprobado; sin nada guardado, Acampada
|
||||
const colorState = (cfg) => {
|
||||
const c = (cfg || cfgNow()).mobileColors;
|
||||
if (!c || typeof c !== "object" || ![c.bg, c.fg, c.accent].every(v => HEX.test(String(v || "")))) {
|
||||
return { preset: DEFAULT[0], bg: DEFAULT[1], fg: DEFAULT[2], accent: DEFAULT[3], chosen: false, fixed: false };
|
||||
}
|
||||
const preset = c.preset === "custom" || PALETTES.some(x => x[0] === c.preset) ? c.preset : "custom";
|
||||
return { preset, bg: c.bg.toLowerCase(), fg: c.fg.toLowerCase(), accent: c.accent.toLowerCase(), chosen: true, fixed: c.fixed === true };
|
||||
};
|
||||
|
||||
// lo que llega del formulario -> lo que se guarda. Un texto que no se lee sobre el
|
||||
// fondo se cambia por negro o blanco y se avisa (fixed)
|
||||
const colorsFromBody = (body) => {
|
||||
const pick = String(body.preset || "");
|
||||
const pal = PALETTES.find(x => x[0] === pick);
|
||||
if (pal) return { preset: pal[0], bg: pal[1], fg: pal[2], accent: pal[3] };
|
||||
const now = colorState();
|
||||
const bg = HEX.test(String(body.bg || "")) ? body.bg.toLowerCase() : now.bg;
|
||||
let fg = HEX.test(String(body.fg || "")) ? body.fg.toLowerCase() : now.fg;
|
||||
const accent = HEX.test(String(body.accent || "")) ? body.accent.toLowerCase() : now.accent;
|
||||
let fixed = false;
|
||||
if (contrast(bg, fg) < 4.5) { fg = readableOn(bg); fixed = true; }
|
||||
return { preset: "custom", bg, fg, accent, fixed };
|
||||
};
|
||||
|
||||
const PREVIEW_CSS = PALETTES.map(([k, bg, fg, ac]) =>
|
||||
`.pal-${k} .pal-bg{background:${bg}!important}.pal-${k} .pal-fg{background:${fg}!important}.pal-${k} .pal-ac{background:${ac}!important}`).join("\n");
|
||||
|
||||
// la hoja que va la ultima: las variables del tema con los colores elegidos y las
|
||||
// muestras de las paletas (la CSP no deja estilos en linea)
|
||||
const colorsCss = (cfg) => {
|
||||
const c = colorState(cfg);
|
||||
// solo con Acampada: OasisMobile.css es del dev y lleva su paleta fija
|
||||
const onAcampada = ((cfg || cfgNow()).themes || {}).current === "Acampada";
|
||||
const vars = c.chosen && onAcampada ? [
|
||||
`--a26-bg:${c.bg}`, `--a26-fg:${c.fg}`, `--a26-neon:${c.accent}`,
|
||||
`--a26-on-neon:${readableOn(c.accent)}`,
|
||||
`--a26-soft:${mix(c.bg, c.fg, 0.06)}`,
|
||||
`--a26-muted:${mix(c.fg, c.bg, 0.35)}`,
|
||||
`--a26-tint:${mix(c.bg, c.accent, 0.2)}`,
|
||||
`--a26-line:${mix(c.bg, c.fg, 0.25)}`,
|
||||
`color-scheme:${lum(c.bg) < 0.2 ? "dark" : "light"}`
|
||||
] : [];
|
||||
const custom = `.pal-custom .pal-bg{background:${c.bg}!important}.pal-custom .pal-fg{background:${c.fg}!important}.pal-custom .pal-ac{background:${c.accent}!important}`;
|
||||
return (vars.length ? `:root{${vars.join(";")}}\n` : "") + PREVIEW_CSS + "\n" + custom + "\n";
|
||||
};
|
||||
const colorsVersion = (cfg) => crypto.createHash("sha1").update(colorsCss(cfg)).digest("hex").slice(0, 10);
|
||||
const colorsLink = () => require("../server/node_modules/hyperaxe").link({ rel: "stylesheet", href: `/mobile-colors.css?v=${colorsVersion()}` });
|
||||
|
||||
const swatch = (key) => span({ class: `pal-swatch pal-${key}`, "aria-hidden": "true" },
|
||||
span({ class: "pal-bg" }), span({ class: "pal-fg" }), span({ class: "pal-ac" }));
|
||||
|
||||
// el selector de colores, el mismo en Ajustes y en la bienvenida
|
||||
const colorsPick = (returnTo, withKeep) => {
|
||||
const i18n = t();
|
||||
const c = colorState();
|
||||
const cur = c.chosen ? c.preset : DEFAULT[0];
|
||||
const theme = (cfgNow().themes || {}).current;
|
||||
return div({ class: "color-pick" },
|
||||
form({ method: "POST", action: "/settings/colors", class: "ux-pick" },
|
||||
input({ type: "hidden", name: "returnTo", value: returnTo }),
|
||||
p({ class: "ux-pick-hint" }, i18n.colorsHint || "Tap a palette and it is applied."),
|
||||
theme && theme !== "Acampada" ? p({ class: "form-hint" }, i18n.colorsSkinNote || "Colours go with the Acampada look; choosing one switches to it.") : null,
|
||||
div({ class: "pal-grid" },
|
||||
...PALETTES.map(([k]) => button(Object.assign({ type: "submit", name: "preset", value: k, class: k === cur ? "pal-card is-current" : "pal-card" }, k === cur ? { "aria-current": "true" } : {}),
|
||||
swatch(k),
|
||||
span({ class: "pal-name" }, i18n[`palette_${k}`] || k),
|
||||
k === cur ? span({ class: "ux-pick-now" }, i18n.uxPickCurrent || "In use") : null
|
||||
))
|
||||
),
|
||||
withKeep && !c.chosen ? div({ class: "welcome-action" }, button({ type: "submit", name: "preset", value: DEFAULT[0], class: "welcome-skip-btn" }, i18n.colorsKeep || "Keep these")) : null
|
||||
),
|
||||
details(Object.assign({ class: "color-custom" }, cur === "custom" ? { open: true } : {}),
|
||||
summary(swatch("custom"), span(i18n.colorsCustom || "Your own")),
|
||||
form({ method: "POST", action: "/settings/colors", class: "color-custom-form" },
|
||||
input({ type: "hidden", name: "returnTo", value: returnTo }),
|
||||
input({ type: "hidden", name: "preset", value: "custom" }),
|
||||
label({ class: "color-field" }, span(i18n.colorsBg || "Background"), input({ type: "color", name: "bg", value: c.bg })),
|
||||
label({ class: "color-field" }, span(i18n.colorsFg || "Text"), input({ type: "color", name: "fg", value: c.fg })),
|
||||
label({ class: "color-field" }, span(i18n.colorsAccent || "Accent"), input({ type: "color", name: "accent", value: c.accent })),
|
||||
c.fixed ? p({ class: "form-hint" }, i18n.colorsFixed || "The text could not be read on that background, so it was set to black or white.") : null,
|
||||
button({ type: "submit", class: "create-button" }, i18n.colorsApply || "Apply")
|
||||
)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
// ---------- los seis accesos ----------
|
||||
|
||||
const QUICK = [
|
||||
{ id: "identity", icon: "profile", key: "quick_identity" },
|
||||
{ id: "workflows", icon: "modules", key: "quick_workflow" },
|
||||
{ id: "ux", icon: "activity", key: "quick_ux" },
|
||||
{ id: "colors", icon: "palette", key: "quick_colors" },
|
||||
{ id: "community", icon: "invites", key: "quick_community" },
|
||||
{ id: "greeting", icon: "publish", key: "quick_greeting" }
|
||||
];
|
||||
|
||||
// la linea de debajo de cada acceso: lo que hay puesto ahora
|
||||
const quickValue = (id, cfg) => {
|
||||
const i18n = t();
|
||||
if (id === "identity") return global.__OASIS_SELF_NAME__ || "";
|
||||
if (id === "workflows") {
|
||||
const wf = (() => { try { return require("../models/workflows_model").currentWorkflow(cfg); } catch (_) { return null; } })();
|
||||
return wf ? (i18n[`workflow_${wf}`] || wf) : "";
|
||||
}
|
||||
if (id === "ux") {
|
||||
const ux = (cfg.ux || {}).current || "blocks";
|
||||
return { blocks: i18n.uxModeMenus, ainav: i18n.uxModeAINav, chats: i18n.uxModeChats, feed: i18n.uxModeFeed, phone: i18n.phoneTitle, rooms: i18n.roomsTitle }[ux] || "";
|
||||
}
|
||||
if (id === "colors") { const c = colorState(cfg); return c.preset === "custom" ? (i18n.colorsCustom || "") : (i18n[`palette_${c.preset}`] || ""); }
|
||||
return "";
|
||||
};
|
||||
|
||||
const quickGrid = (cfg, present) => {
|
||||
const i18n = t();
|
||||
return section({ id: "quick", class: "quick-settings" },
|
||||
div({ class: "tags-header" }, h2(i18n.quickTitle || "The basics"), p(i18n.quickHint || "What you chose when you started, one tap away.")),
|
||||
div({ class: "quick-grid" },
|
||||
...QUICK.filter(q => present.includes(q.id)).map(q => a({ href: `#${q.id}`, class: "quick-tile" },
|
||||
iconNode("quick-ico", q.icon),
|
||||
span({ class: "quick-name" }, i18n[q.key] || q.id),
|
||||
span({ class: "quick-now" }, quickValue(q.id, cfg))
|
||||
))
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
// ---------- bloques nuevos de Ajustes ----------
|
||||
|
||||
const RETURN = (id) => `/settings#${id}`;
|
||||
|
||||
const identitySection = () => {
|
||||
const i18n = t();
|
||||
const avatar = global.__OASIS_SELF_AVATAR__;
|
||||
return section({ id: "identity" },
|
||||
div({ class: "tags-header" }, h2(i18n.quick_identity || i18n.welcomeStepIdentity), p(i18n.welcomeHintIdentity)),
|
||||
form({ method: "POST", action: "/welcome/profile", enctype: "multipart/form-data", class: "welcome-profile-form" },
|
||||
input({ type: "hidden", name: "returnTo", value: RETURN("identity") }),
|
||||
label({ for: "set-name" }, i18n.profileName),
|
||||
input({ type: "text", id: "set-name", name: "name", maxlength: "80", value: global.__OASIS_SELF_NAME__ || "" }),
|
||||
label({ for: "set-image" }, i18n.profileImage),
|
||||
avatar ? img({ class: "welcome-profile-avatar", src: avatar, alt: "" }) : null,
|
||||
input({ type: "file", id: "set-image", name: "image", accept: "image/*" }),
|
||||
button({ type: "submit" }, i18n.welcomeStepProfileAction)
|
||||
),
|
||||
a({ href: "/profile/edit", class: "filter-btn" }, i18n.quickProfileMore || "Description and more")
|
||||
);
|
||||
};
|
||||
|
||||
const colorsSection = () => {
|
||||
const i18n = t();
|
||||
return section({ id: "colors" },
|
||||
div({ class: "tags-header" }, h2(i18n.quick_colors || "Colours")),
|
||||
colorsPick(RETURN("colors"), false)
|
||||
);
|
||||
};
|
||||
|
||||
const communitySection = () => {
|
||||
const i18n = t();
|
||||
return section({ id: "community" },
|
||||
div({ class: "tags-header" }, h2(i18n.quick_community || i18n.welcomeStepCommunity), p(i18n.welcomeHintCommunity)),
|
||||
div({ class: "quick-links" },
|
||||
a({ href: "/invites", class: "create-button" }, i18n.quickJoinPub || i18n.invites),
|
||||
a({ href: "/peers", class: "filter-btn" }, i18n.quickSeePeers || i18n.peers)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const greetingSection = () => {
|
||||
const i18n = t();
|
||||
return section({ id: "greeting" },
|
||||
div({ class: "tags-header" }, h2(i18n.quick_greeting || i18n.welcomeStepIntro), p(i18n.welcomeHintIntro)),
|
||||
form({ method: "POST", action: "/welcome/greeting", class: "welcome-greeting-form" },
|
||||
input({ type: "hidden", name: "returnTo", value: RETURN("greeting") }),
|
||||
textarea({ name: "text", required: true, minlength: "1", maxlength: "280", rows: 3, placeholder: i18n.quickGreetingPlaceholder || "" }),
|
||||
button({ type: "submit" }, i18n.welcomeStepGreetingAction)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
// recibe las secciones de settings_view (pestaña Oasis) y devuelve la lista del movil:
|
||||
// la rejilla arriba, los bloques nuevos metidos en su sitio y el orden de los accesos
|
||||
const ORDER = ["quick", "update", "language", "identity", "workflows", "ux", "colors", "theme", "community", "greeting", "home-page"];
|
||||
const withQuick = (parts) => {
|
||||
const cfg = cfgNow();
|
||||
const mods = cfg.modules || {};
|
||||
const extra = [
|
||||
identitySection(),
|
||||
colorsSection(),
|
||||
mods.invitesMod !== "off" ? communitySection() : null,
|
||||
mods.feedMod !== "off" ? greetingSection() : null
|
||||
].filter(Boolean);
|
||||
const list = parts.filter(Boolean).concat(extra);
|
||||
const ids = list.map(el => el && el.id);
|
||||
list.push(quickGrid(cfg, ids));
|
||||
const rank = (el) => { const i = ORDER.indexOf(el && el.id); return i === -1 ? ORDER.length : i; };
|
||||
return list.map((el, i) => ({ el, i })).sort((x, y) => rank(x.el) - rank(y.el) || x.i - y.i).map(x => x.el);
|
||||
};
|
||||
|
||||
// ---------- bienvenida ----------
|
||||
|
||||
// Colores entra como paso, despues del tipo de portada. No es un paso del dev, asi
|
||||
// que el "hecho" sale de si ya hay colores guardados
|
||||
const welcomeWithColors = (usable) => {
|
||||
if (usable.includes("colors")) return usable;
|
||||
const out = usable.slice();
|
||||
const at = out.indexOf("ux");
|
||||
out.splice(at === -1 ? out.length : at + 1, 0, "colors");
|
||||
return out;
|
||||
};
|
||||
const welcomeDone = (steps) => Object.assign({}, steps, { colors: colorState().chosen });
|
||||
|
||||
module.exports = {
|
||||
PALETTES, colorState, colorsFromBody, colorsCss, colorsLink, colorsPick,
|
||||
withQuick, welcomeWithColors, welcomeDone, contrast
|
||||
};
|
||||
|
|
@ -107,14 +107,9 @@ const tribeTabs = (tribe, section, sections) => {
|
|||
|
||||
// ---------- ajustes ----------
|
||||
|
||||
// primero lo que se toca (idioma, aspecto, tipo de uso, portada) y despues lo tecnico;
|
||||
// se reordena por id de seccion, sin tocar las secciones
|
||||
const SETTINGS_FIRST = ['update', 'language', 'ux', 'theme', 'workflows', 'home-page'];
|
||||
const settingsOrder = (parts) => {
|
||||
const list = parts.filter(Boolean);
|
||||
const rank = (el) => { const i = SETTINGS_FIRST.indexOf(el && el.id); return i === -1 ? SETTINGS_FIRST.length : i; };
|
||||
return list.map((el, i) => ({ el, i })).sort((x, y) => rank(x.el) - rank(y.el) || x.i - y.i).map(x => x.el);
|
||||
};
|
||||
// primero los seis accesos y lo que se toca (idioma, identidad, uso, portada,
|
||||
// colores, aspecto) y despues lo tecnico; ver mobile_settings.js
|
||||
const settingsOrder = (parts) => require('./mobile_settings').withQuick(parts);
|
||||
|
||||
// cada idioma un boton que se aplica al tocarlo (antes desplegable y otro boton)
|
||||
const LANGS = [['Español', 'es'], ['Català', 'ca'], ['Euskara', 'eu'], ['Galego', 'gl'], ['English', 'en'], ['Français', 'fr'],
|
||||
|
|
@ -179,12 +174,14 @@ const workflowPick = (action, current, keys, withHint) => {
|
|||
|
||||
// pasos de la bienvenida en el movil: el backend del dev quita el de uso en el movil;
|
||||
// aqui vuelve, el segundo, justo despues de la identidad (/welcome/workflow es suyo)
|
||||
// y Colores despues del tipo de portada (mobile_settings.js)
|
||||
const welcomeSteps = (usable) => {
|
||||
if (usable.includes('workflow')) return usable;
|
||||
const out = usable.slice();
|
||||
const at = out.indexOf('profile');
|
||||
out.splice(at === -1 ? 0 : at + 1, 0, 'workflow');
|
||||
return out;
|
||||
if (!out.includes('workflow')) {
|
||||
const at = out.indexOf('profile');
|
||||
out.splice(at === -1 ? 0 : at + 1, 0, 'workflow');
|
||||
}
|
||||
return require('./mobile_settings').welcomeWithColors(out);
|
||||
};
|
||||
|
||||
// ---------- acciones de tarjeta ----------
|
||||
|
|
@ -228,7 +225,14 @@ const welcomeBannerHere = (here) => ['', '/', '/activity'].includes(here);
|
|||
const welcomeBannerText = () => t().welcomeBannerTextMobile || t().welcomeBannerText;
|
||||
const welcomeBannerAction = () => t().welcomeBannerActionMobile || t().welcomeBannerAction;
|
||||
|
||||
// colores y Ajustes: viven en mobile_settings.js; aqui se reexportan para que los
|
||||
// enganches de las vistas del dev pasen todos por mobile_ui
|
||||
const colorsLink = () => require('./mobile_settings').colorsLink();
|
||||
const colorsPick = (returnTo, withKeep) => require('./mobile_settings').colorsPick(returnTo, withKeep);
|
||||
const welcomeDone = (steps) => require('./mobile_settings').welcomeDone(steps);
|
||||
|
||||
module.exports = {
|
||||
colorsLink, colorsPick, welcomeDone,
|
||||
isMobile, VOTE_KINDS, voteKindPicker, voteBars, voteOwnNote, tribeRow, tribeTabs,
|
||||
settingsOrder, languagePick, uxPick, workflowPick, welcomeSteps, deleteConfirm, footerLine,
|
||||
welcomeBannerHere, welcomeBannerText, welcomeBannerAction
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ const SECTIONS = {
|
|||
greeting: ["welcomeStepIntro", "welcomeHintIntro"]
|
||||
}
|
||||
if (mobileUi.isMobile()) SECTIONS.workflow = ["welcomeStepUseMobile", "welcomeHintUseMobile"]
|
||||
if (mobileUi.isMobile()) SECTIONS.colors = ["quick_colors", "welcomeHintColors"]
|
||||
|
||||
const submitRow = (text) => div({ class: "welcome-action" }, button({ type: "submit" }, text))
|
||||
|
||||
|
|
@ -63,6 +64,7 @@ const stepAction = (key, profile, status) => {
|
|||
submitRow(i18n.welcomeStepWorkflowAction)
|
||||
)
|
||||
}
|
||||
if (key === "colors") return mobileUi.colorsPick("/welcome#step-colors", true)
|
||||
if (key === "theme") return form({ method: "POST", action: "/welcome/theme", class: "welcome-ux-form" },
|
||||
div({ class: "welcome-ux-grid" },
|
||||
WIZARD_THEMES.map(([value, name]) => pickCard("theme", value, status.theme, name, `/assets/images/theme-${value.toLowerCase()}.png`))
|
||||
|
|
@ -105,7 +107,7 @@ const stepAction = (key, profile, status) => {
|
|||
}
|
||||
|
||||
exports.welcomeView = async (status, profile = {}) => {
|
||||
const steps = status.steps || {}
|
||||
const steps = mobileUi.isMobile() ? mobileUi.welcomeDone(status.steps || {}) : (status.steps || {})
|
||||
const usable = mobileUi.isMobile() ? mobileUi.welcomeSteps(status.usable || []) : (status.usable || [])
|
||||
const opened = usable.find(key => !steps[key])
|
||||
const html = template(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue