skins: elegir el aspecto en el movil con tarjetas de un toque

En Ajustes del movil, Tema y Flujos de trabajo dejan de ser desplegables:
cada skin es una tarjeta con su miniatura y cada workflow un boton grande,
y se aplican al tocarlos. Solo se ofrecen los skins que tienen maqueta de
movil (Acampada y Oasis Mobile); el servidor rechaza un tema de escritorio,
que dejaba la app sin maqueta. La bienvenida tambien ofrece elegir skin.

Aplicar un workflow ya no cambia el skin: se queda el que haya elegido cada
cual.

OasisMobile.css se carga con su propio <link> versionado antes de
Acampada.css, en vez de con @import: el @import sin ?v= se quedaba en la
cache del WebView y tras actualizar se veia la maqueta vieja.
This commit is contained in:
oasis-bot 2026-10-19 02:17:12 +02:00
parent ac921c9d56
commit 0baf85faa9
9 changed files with 103 additions and 17 deletions

View file

@ -438,9 +438,17 @@ exports.CONTENT_SPREADABLE = CONTENT_SPREADABLE;
exports.CONTENT_TORRENTABLE = CONTENT_TORRENTABLE;
exports.contentDeleteAction = contentDeleteAction;
exports.THEMES = [['Acampada', 'Acampada'], ['Dark-SNH', 'Dark-SNH'], ['Clear-SNH', 'Clear-SNH'], ['Purple-SNH', 'Purple-SNH'], ['Matrix-SNH', 'Matrix-SNH'], ['OasisMobile', 'Oasis-Mobile']];
// Acampada importa la maqueta de OasisMobile.css: los dos son solo de movil
exports.WIZARD_THEMES = exports.THEMES.filter(([value]) => value !== 'OasisMobile' && value !== 'Acampada');
// con el tema Acampada, hive_acampada.css va detras del tema: pinta lanzador, barras y tarjetas
// Acampada importa la maqueta de OasisMobile.css: los dos son solo de movil, y en el
// movil son los unicos que valen (los de escritorio no traen la maqueta)
exports.MOBILE_THEMES = [['Acampada', 'Acampada'], ['OasisMobile', 'Oasis Mobile']];
exports.WIZARD_THEMES = process.env.OASIS_MOBILE === '1'
? exports.MOBILE_THEMES
: exports.THEMES.filter(([value]) => value !== 'OasisMobile' && value !== 'Acampada');
// el tema Acampada es la maqueta de OasisMobile.css con otros colores. OasisMobile.css va
// en su propio <link> versionado y no como @import dentro de Acampada.css: un @import sin
// ?v= se queda en la cache del WebView y tras actualizar se veia la maqueta vieja.
// hive_acampada.css va detras del tema: pinta lanzador, barras y tarjetas.
const acampadaBase = (theme) => theme === 'Acampada' ? link({ rel: "stylesheet", href: `/assets/themes/OasisMobile.css?v=${assetVersion()}` }) : null;
const acampadaLink = (theme) => theme === 'Acampada' ? link({ rel: "stylesheet", href: `/assets/styles/hive_acampada.css?v=${assetVersion()}` }) : null;
const renderDocumentActions = (kind, id, leadingNodes = []) => {
@ -2006,7 +2014,7 @@ const template = (titlePrefix, ...elements) => {
// movil: el tema va DESPUES de mobile.css para ganar por cascada sin !important;
// escritorio: orden original de epsylon
...(process.env.OASIS_MOBILE === '1'
? [ link({ rel: "stylesheet", href: `/assets/styles/mobile.css?v=${assetVersion()}` }), themeLink, acampadaLink(theme) ]
? [ 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)" }) ]),
// el modulo Karvan trae su propia hoja: asi se ve igual en cualquier tema
// y tambien en escritorio, donde no se carga OasisMobile.css
@ -2395,7 +2403,7 @@ exports.ainavHomeView = ({ recentTags = [] } = {}) => {
// movil: el tema va DESPUES de mobile.css para ganar por cascada sin !important;
// escritorio: orden original de epsylon
...(process.env.OASIS_MOBILE === '1'
? [ link({ rel: "stylesheet", href: `/assets/styles/mobile.css?v=${assetVersion()}` }), link({ rel: "stylesheet", href: `/assets/themes/${theme}.css?v=${assetVersion()}` }), acampadaLink(theme) ]
? [ 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)" }) ]),
// el modulo Karvan trae su propia hoja: asi se ve igual en cualquier tema
// y tambien en escritorio, donde no se carga OasisMobile.css

View file

@ -2,7 +2,7 @@ const { form, button, div, h2, h3, p, section, select, option, input, br, a, lab
const fs = require('fs');
const path = require('path');
const { getConfig } = require('../configs/config-manager.js');
const { template, selectedLanguage, i18n, setLanguage, INBOX_BOT_ORDER, inboxBotLabel, THEMES, userLink } = require('./main_views');
const { template, selectedLanguage, i18n, setLanguage, INBOX_BOT_ORDER, inboxBotLabel, THEMES, MOBILE_THEMES, userLink } = require('./main_views');
const i18nBase = require("../client/assets/translations/i18n");
const moment = require("../server/node_modules/moment");
const { WORKFLOWS, currentWorkflow } = require('../models/workflows_model');
@ -58,7 +58,26 @@ const settingsView = (params) => {
const modOn = (name) => (currentConfig.modules || {})[`${name}Mod`] === 'on';
const isMobile = process.env.OASIS_MOBILE === '1';
const workflowSection = section({ id: "workflows" },
// movil: skins y workflows como tarjetas que se aplican al tocarlas (un boton
// de envio por opcion); el desplegable de escritorio quedaba enterrado y dejaba
// poner temas de escritorio, que en el movil se quedan sin maqueta
const skinPicker = () => section({ id: "theme" },
div({ class: "tags-header" }, h2(i18n.theme), p(i18n.skinIntroMobile)),
form({ action: "/settings/theme", method: "post", class: "skin-pick" },
...MOBILE_THEMES.map(([value, name]) => button({ type: "submit", name: "theme", value, class: theme === value ? "skin-card is-current" : "skin-card" },
img({ src: `/assets/images/theme-${value.toLowerCase()}.png`, alt: "", class: "skin-shot" }),
span({ class: "skin-name" }, name)))
)
);
const workflowPicker = () => section({ id: "workflows" },
div({ class: "tags-header" }, h2(i18n.workflowsTitle), p(i18n.workflowsDescriptionMobile)),
form({ action: "/settings/workflow", method: "POST", class: "workflow-pick" },
...WORKFLOWS.map(w => button({ type: "submit", name: "workflow", value: w.key, class: activeWorkflow === w.key ? "workflow-card is-current" : "workflow-card" },
i18n[`workflow_${w.key}`] || w.key))
)
);
const workflowSection = isMobile ? workflowPicker() : section({ id: "workflows" },
div({ class: "tags-header" },
h2(i18n.workflowsTitle),
p(i18n.workflowsDescription),
@ -181,7 +200,7 @@ const settingsView = (params) => {
)
)
),
section({ id: "theme" },
isMobile ? skinPicker() : section({ id: "theme" },
div({ class: "tags-header" },
h2(i18n.theme),
p(i18n.themeIntro),