Change how theme style packs are loaded
Load the `common` style pack, and then charge the style pack for the current skin, independent from any selected JS pack.pull/2697/head
parent
93cdc66e64
commit
c3e12a4dfa
|
@ -19,6 +19,7 @@ class ApplicationController < ActionController::Base
|
||||||
helper_method :current_session
|
helper_method :current_session
|
||||||
helper_method :current_flavour
|
helper_method :current_flavour
|
||||||
helper_method :current_skin
|
helper_method :current_skin
|
||||||
|
helper_method :current_theme
|
||||||
helper_method :single_user_mode?
|
helper_method :single_user_mode?
|
||||||
helper_method :use_seamless_external_login?
|
helper_method :use_seamless_external_login?
|
||||||
helper_method :omniauth_only?
|
helper_method :omniauth_only?
|
||||||
|
|
|
@ -4,7 +4,7 @@ module ThemingConcern
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
def use_pack(pack_name)
|
def use_pack(pack_name)
|
||||||
@theme = resolve_pack_with_common(Themes.instance.flavour(current_flavour), pack_name, current_skin)
|
@theme = resolve_pack(Themes.instance.flavour(current_flavour), pack_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -18,29 +18,22 @@ module ThemingConcern
|
||||||
[current_user&.setting_skin, Setting.skin, 'default'].find { |skin| skins.include?(skin) }
|
[current_user&.setting_skin, Setting.skin, 'default'].find { |skin| skins.include?(skin) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def valid_pack_data?(data, pack_name)
|
def current_theme
|
||||||
data['pack'].is_a?(Hash) && data['pack'][pack_name].present?
|
# NOTE: this is slightly different from upstream, as it's a derived value used
|
||||||
|
# for the sole purpose of pointing to the appropriate stylesheet pack
|
||||||
|
"skins/#{current_flavour}/#{current_skin}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def nil_pack(data)
|
def resolve_pack(data, pack_name)
|
||||||
{
|
pack_data = {
|
||||||
flavour: data['name'],
|
flavour: data['name'],
|
||||||
pack: nil,
|
pack: nil,
|
||||||
preload: nil,
|
preload: nil,
|
||||||
skin: nil,
|
|
||||||
supported_locales: data['locales'],
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def pack(data, pack_name, skin)
|
|
||||||
pack_data = {
|
|
||||||
flavour: data['name'],
|
|
||||||
pack: pack_name,
|
|
||||||
preload: nil,
|
|
||||||
skin: nil,
|
|
||||||
supported_locales: data['locales'],
|
supported_locales: data['locales'],
|
||||||
}
|
}
|
||||||
|
return pack_data unless data['pack'].is_a?(Hash) && data['pack'][pack_name].present?
|
||||||
|
|
||||||
|
pack_data[:pack] = pack_name
|
||||||
return pack_data unless data['pack'][pack_name].is_a?(Hash)
|
return pack_data unless data['pack'][pack_name].is_a?(Hash)
|
||||||
|
|
||||||
pack_data[:pack] = nil unless data['pack'][pack_name]['filename']
|
pack_data[:pack] = nil unless data['pack'][pack_name]['filename']
|
||||||
|
@ -49,22 +42,6 @@ module ThemingConcern
|
||||||
pack_data[:preload] = [preloads] if preloads.is_a?(String)
|
pack_data[:preload] = [preloads] if preloads.is_a?(String)
|
||||||
pack_data[:preload] = preloads if preloads.is_a?(Array)
|
pack_data[:preload] = preloads if preloads.is_a?(Array)
|
||||||
|
|
||||||
if skin != 'default' && data['skin'][skin]
|
|
||||||
pack_data[:skin] = skin if data['skin'][skin].include?(pack_name)
|
|
||||||
elsif data['pack'][pack_name]['stylesheet']
|
|
||||||
pack_data[:skin] = 'default'
|
|
||||||
end
|
|
||||||
|
|
||||||
pack_data
|
pack_data
|
||||||
end
|
end
|
||||||
|
|
||||||
def resolve_pack(data, pack_name, skin)
|
|
||||||
pack(data, pack_name, skin) if valid_pack_data?(data, pack_name)
|
|
||||||
end
|
|
||||||
|
|
||||||
def resolve_pack_with_common(data, pack_name, skin = 'default')
|
|
||||||
result = resolve_pack(data, pack_name, skin) || nil_pack(data)
|
|
||||||
result[:common] = resolve_pack(data, 'common', skin)
|
|
||||||
result
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import Rails from '@rails/ujs';
|
||||||
|
import 'font-awesome/css/font-awesome.css';
|
||||||
|
|
||||||
|
export function start() {
|
||||||
|
require.context('@/images/', true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Rails.start();
|
||||||
|
} catch (e) {
|
||||||
|
// If called twice
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,12 @@
|
||||||
import 'packs/public-path';
|
import 'packs/public-path';
|
||||||
|
|
||||||
|
import { start } from 'flavours/glitch/common';
|
||||||
import { loadLocale } from 'flavours/glitch/locales';
|
import { loadLocale } from 'flavours/glitch/locales';
|
||||||
import main from "flavours/glitch/main";
|
import main from "flavours/glitch/main";
|
||||||
import { loadPolyfills } from 'flavours/glitch/polyfills';
|
import { loadPolyfills } from 'flavours/glitch/polyfills';
|
||||||
|
|
||||||
|
start();
|
||||||
|
|
||||||
loadPolyfills()
|
loadPolyfills()
|
||||||
.then(loadLocale)
|
.then(loadLocale)
|
||||||
.then(main)
|
.then(main)
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
|
/* This file is a hack to have something more reliable than the upstream `common` tag
|
||||||
|
that is implicitly generated as the common chunk through webpack's `splitChunks` config */
|
||||||
|
|
||||||
import 'packs/public-path';
|
import 'packs/public-path';
|
||||||
import 'font-awesome/css/font-awesome.css';
|
import 'font-awesome/css/font-awesome.css';
|
||||||
import Rails from '@rails/ujs';
|
|
||||||
import 'flavours/glitch/styles/index.scss';
|
|
||||||
|
|
||||||
Rails.start();
|
// This is a hack to ensures that webpack compiles our images.
|
||||||
|
|
||||||
// This ensures that webpack compiles our images.
|
|
||||||
require.context('../images', true);
|
require.context('../images', true);
|
||||||
|
|
|
@ -10,6 +10,7 @@ import Rails from '@rails/ujs';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { throttle } from 'lodash';
|
import { throttle } from 'lodash';
|
||||||
|
|
||||||
|
import { start } from 'flavours/glitch/common';
|
||||||
import { timeAgoString } from 'flavours/glitch/components/relative_timestamp';
|
import { timeAgoString } from 'flavours/glitch/components/relative_timestamp';
|
||||||
import emojify from 'flavours/glitch/features/emoji/emoji';
|
import emojify from 'flavours/glitch/features/emoji/emoji';
|
||||||
import loadKeyboardExtensions from 'flavours/glitch/load_keyboard_extensions';
|
import loadKeyboardExtensions from 'flavours/glitch/load_keyboard_extensions';
|
||||||
|
@ -19,6 +20,8 @@ import ready from 'flavours/glitch/ready';
|
||||||
|
|
||||||
import 'cocoon-js-vanilla';
|
import 'cocoon-js-vanilla';
|
||||||
|
|
||||||
|
start();
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
usernameTaken: {
|
usernameTaken: {
|
||||||
id: 'username.taken',
|
id: 'username.taken',
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
import 'packs/public-path';
|
import 'packs/public-path';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
|
|
||||||
|
import { start } from 'flavours/glitch/common';
|
||||||
import ComposeContainer from 'flavours/glitch/containers/compose_container';
|
import ComposeContainer from 'flavours/glitch/containers/compose_container';
|
||||||
import { loadPolyfills } from 'flavours/glitch/polyfills';
|
import { loadPolyfills } from 'flavours/glitch/polyfills';
|
||||||
import ready from 'flavours/glitch/ready';
|
import ready from 'flavours/glitch/ready';
|
||||||
|
|
||||||
|
start();
|
||||||
|
|
||||||
function loaded() {
|
function loaded() {
|
||||||
const mountNode = document.getElementById('mastodon-compose');
|
const mountNode = document.getElementById('mastodon-compose');
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
/* This file is a hack to have something more reliable than the upstream `common` tag
|
||||||
|
that is implicitly generated as the common chunk through webpack's `splitChunks` config */
|
||||||
|
|
||||||
import './public-path';
|
import './public-path';
|
||||||
import 'font-awesome/css/font-awesome.css';
|
import 'font-awesome/css/font-awesome.css';
|
||||||
import 'styles/application.scss';
|
|
||||||
|
|
||||||
require.context('../images/', true);
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
@import 'flavours/glitch/styles/index';
|
|
@ -0,0 +1 @@
|
||||||
|
@import 'styles/application';
|
|
@ -7,7 +7,8 @@ class Themes
|
||||||
include Singleton
|
include Singleton
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
result = {}
|
@flavours = {}
|
||||||
|
|
||||||
Rails.root.glob('app/javascript/flavours/*/theme.yml') do |pathname|
|
Rails.root.glob('app/javascript/flavours/*/theme.yml') do |pathname|
|
||||||
data = YAML.load_file(pathname)
|
data = YAML.load_file(pathname)
|
||||||
next unless data['pack']
|
next unless data['pack']
|
||||||
|
@ -35,42 +36,34 @@ class Themes
|
||||||
data['name'] = name
|
data['name'] = name
|
||||||
data['locales'] = locales
|
data['locales'] = locales
|
||||||
data['screenshot'] = screenshots
|
data['screenshot'] = screenshots
|
||||||
data['skin'] = { 'default' => [] }
|
data['skins'] = []
|
||||||
result[name] = data
|
@flavours[name] = data
|
||||||
end
|
end
|
||||||
|
|
||||||
Rails.root.glob('app/javascript/skins/*/*') do |pathname|
|
Rails.root.glob('app/javascript/skins/*/*') do |pathname|
|
||||||
ext = pathname.extname.to_s
|
ext = pathname.extname.to_s
|
||||||
skin = pathname.basename.to_s
|
skin = pathname.basename.to_s
|
||||||
name = pathname.dirname.basename.to_s
|
name = pathname.dirname.basename.to_s
|
||||||
next unless result[name]
|
next unless @flavours[name]
|
||||||
|
|
||||||
if pathname.directory?
|
if pathname.directory?
|
||||||
pack = []
|
@flavours[name]['skins'] << skin if pathname.glob('{common,index,application}.{css,scss}').any?
|
||||||
pathname.glob('*.{css,scss}') do |sheet|
|
|
||||||
pack.push(sheet.basename(sheet.extname).to_s)
|
|
||||||
end
|
|
||||||
elsif /^\.s?css$/i.match?(ext)
|
elsif /^\.s?css$/i.match?(ext)
|
||||||
skin = pathname.basename(ext).to_s
|
@flavours[name]['skins'] << pathname.basename(ext).to_s
|
||||||
pack = ['common']
|
|
||||||
end
|
end
|
||||||
|
|
||||||
result[name]['skin'][skin] = pack if skin != 'default'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@conf = result
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def flavour(name)
|
def flavour(name)
|
||||||
@conf[name]
|
@flavours[name]
|
||||||
end
|
end
|
||||||
|
|
||||||
def flavours
|
def flavours
|
||||||
@conf.keys
|
@flavours.keys
|
||||||
end
|
end
|
||||||
|
|
||||||
def skins_for(name)
|
def skins_for(name)
|
||||||
@conf[name]['skin'].keys
|
@flavours[name]['skins']
|
||||||
end
|
end
|
||||||
|
|
||||||
def flavours_and_skins
|
def flavours_and_skins
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
- if theme
|
- if theme && theme[:pack]
|
||||||
= render partial: 'layouts/theme', object: theme[:common] if theme[:pack] != 'common' && theme[:common]
|
- pack_path = "flavours/#{theme[:flavour]}/#{theme[:pack]}"
|
||||||
- if theme[:pack]
|
= javascript_pack_tag pack_path, crossorigin: 'anonymous'
|
||||||
- pack_path = "flavours/#{theme[:flavour]}/#{theme[:pack]}"
|
- theme[:preload]&.each do |link|
|
||||||
= javascript_pack_tag pack_path, crossorigin: 'anonymous'
|
%link{ href: asset_pack_path("#{link}.js"), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/
|
||||||
- if theme[:skin]
|
|
||||||
- if !theme[:flavour] || theme[:skin] == 'default'
|
|
||||||
= stylesheet_pack_tag pack_path, media: 'all', crossorigin: 'anonymous'
|
|
||||||
- else
|
|
||||||
= stylesheet_pack_tag "skins/#{theme[:flavour]}/#{theme[:skin]}/#{theme[:pack]}", media: 'all', crossorigin: 'anonymous'
|
|
||||||
- theme[:preload]&.each do |link|
|
|
||||||
%link{ href: asset_pack_path("#{link}.js"), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/
|
|
||||||
|
|
|
@ -26,11 +26,13 @@
|
||||||
|
|
||||||
%title= html_title
|
%title= html_title
|
||||||
|
|
||||||
= javascript_pack_tag 'common', crossorigin: 'anonymous'
|
= stylesheet_pack_tag 'flavours/vanilla/common', media: 'all', crossorigin: 'anonymous' # upstream uses `common` but that's implicitly defined
|
||||||
|
= stylesheet_pack_tag current_theme, media: 'all', crossorigin: 'anonymous'
|
||||||
|
|
||||||
-# Needed for the wicg-inert polyfill. It needs to be on it's own <style> tag, with this `id`
|
-# Needed for the wicg-inert polyfill. It needs to be on it's own <style> tag, with this `id`
|
||||||
= stylesheet_pack_tag 'flavours/vanilla/inert', media: 'all', id: 'inert-style'
|
= stylesheet_pack_tag 'flavours/vanilla/inert', media: 'all', id: 'inert-style'
|
||||||
|
|
||||||
|
= javascript_pack_tag 'common', crossorigin: 'anonymous'
|
||||||
- if @theme
|
- if @theme
|
||||||
- if @theme[:supported_locales].include? I18n.locale.to_s
|
- if @theme[:supported_locales].include? I18n.locale.to_s
|
||||||
= preload_pack_asset "locales/#{@theme[:flavour]}/#{I18n.locale}-json.js"
|
= preload_pack_asset "locales/#{@theme[:flavour]}/#{I18n.locale}-json.js"
|
||||||
|
@ -39,13 +41,13 @@
|
||||||
= csrf_meta_tags unless skip_csrf_meta_tags?
|
= csrf_meta_tags unless skip_csrf_meta_tags?
|
||||||
%meta{ name: 'style-nonce', content: request.content_security_policy_nonce }
|
%meta{ name: 'style-nonce', content: request.content_security_policy_nonce }
|
||||||
|
|
||||||
|
= stylesheet_link_tag custom_css_path, skip_pipeline: true, host: root_url, media: 'all'
|
||||||
|
|
||||||
= yield :header_tags
|
= yield :header_tags
|
||||||
|
|
||||||
-# These must come after :header_tags to ensure our initial state has been defined.
|
-# These must come after :header_tags to ensure our initial state has been defined.
|
||||||
= render partial: 'layouts/theme', object: @theme
|
= render partial: 'layouts/theme', object: @theme
|
||||||
|
|
||||||
= stylesheet_link_tag custom_css_path, skip_pipeline: true, host: root_url, media: 'all'
|
|
||||||
|
|
||||||
%body{ class: body_classes }
|
%body{ class: body_classes }
|
||||||
= content_for?(:content) ? yield(:content) : yield
|
= content_for?(:content) ? yield(:content) : yield
|
||||||
|
|
||||||
|
|
|
@ -11,13 +11,16 @@
|
||||||
- if storage_host?
|
- if storage_host?
|
||||||
%link{ rel: 'dns-prefetch', href: storage_host }/
|
%link{ rel: 'dns-prefetch', href: storage_host }/
|
||||||
|
|
||||||
= render_initial_state
|
= stylesheet_pack_tag 'flavours/vanilla/common', media: 'all', crossorigin: 'anonymous' # upstream uses `common` but that's implicitly defined
|
||||||
= javascript_pack_tag 'common', crossorigin: 'anonymous'
|
= stylesheet_pack_tag current_theme, media: 'all', crossorigin: 'anonymous'
|
||||||
|
= javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous'
|
||||||
- if @theme
|
- if @theme
|
||||||
- if @theme[:supported_locales].include? I18n.locale.to_s
|
- if @theme[:supported_locales].include? I18n.locale.to_s
|
||||||
= preload_pack_asset "locales/#{@theme[:flavour]}/#{I18n.locale}-json.js"
|
= preload_pack_asset "locales/#{@theme[:flavour]}/#{I18n.locale}-json.js"
|
||||||
- elsif @theme[:supported_locales].include? 'en'
|
- elsif @theme[:supported_locales].include? 'en'
|
||||||
= preload_pack_asset "locales/#{@theme[:flavour]}/en-json.js"
|
= preload_pack_asset "locales/#{@theme[:flavour]}/en-json.js"
|
||||||
|
= preload_pack_asset "locale/#{I18n.locale}-json.js"
|
||||||
|
= render_initial_state
|
||||||
= render partial: 'layouts/theme', object: @theme
|
= render partial: 'layouts/theme', object: @theme
|
||||||
|
|
||||||
%body.embed
|
%body.embed
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
%meta{ charset: 'utf-8' }/
|
%meta{ charset: 'utf-8' }/
|
||||||
%title= safe_join([yield(:page_title), Setting.default_settings['site_title']], ' - ')
|
%title= safe_join([yield(:page_title), Setting.default_settings['site_title']], ' - ')
|
||||||
%meta{ content: 'width=device-width,initial-scale=1', name: 'viewport' }/
|
%meta{ content: 'width=device-width,initial-scale=1', name: 'viewport' }/
|
||||||
|
= stylesheet_pack_tag 'flavours/vanilla/common', media: 'all', crossorigin: 'anonymous' # upstream uses `common` but that's implicitly defined
|
||||||
|
= stylesheet_pack_tag current_theme, media: 'all', crossorigin: 'anonymous'
|
||||||
= javascript_pack_tag 'common', crossorigin: 'anonymous'
|
= javascript_pack_tag 'common', crossorigin: 'anonymous'
|
||||||
= render partial: 'layouts/theme', object: @theme || { pack: 'error', flavour: 'glitch', common: { pack: 'common', flavour: 'glitch', skin: 'default' } }
|
= render partial: 'layouts/theme', object: @theme || { pack: 'error', flavour: 'glitch', common: { pack: 'common', flavour: 'glitch', skin: 'default' } }
|
||||||
%body.error
|
%body.error
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Common configuration for webpacker loaded from config/webpacker.yml
|
// Common configuration for webpacker loaded from config/webpacker.yml
|
||||||
|
|
||||||
const { lstatSync, readFileSync } = require('fs');
|
const { lstatSync, readFileSync } = require('fs');
|
||||||
const { basename, dirname, extname, join, resolve } = require('path');
|
const { basename, dirname, join, resolve } = require('path');
|
||||||
const { env } = require('process');
|
const { env } = require('process');
|
||||||
|
|
||||||
const glob = require('glob');
|
const glob = require('glob');
|
||||||
|
@ -37,12 +37,13 @@ skinFiles.forEach((skinFile) => {
|
||||||
const data = flavours[name].skin;
|
const data = flavours[name].skin;
|
||||||
if (lstatSync(skinFile).isDirectory()) {
|
if (lstatSync(skinFile).isDirectory()) {
|
||||||
data[skin] = {};
|
data[skin] = {};
|
||||||
const skinPacks = glob.sync(join(skinFile, '*.{css,scss}'));
|
// TODO: more cleanly take the first match
|
||||||
|
const skinPacks = glob.sync(join(skinFile, '{common,index,application}.{css,scss}'));
|
||||||
skinPacks.forEach((pack) => {
|
skinPacks.forEach((pack) => {
|
||||||
data[skin][basename(pack, extname(pack))] = pack;
|
data[skin] = pack;
|
||||||
});
|
});
|
||||||
} else if ((skin = skin.match(/^(.*)\.s?css$/i))) {
|
} else if ((skin = skin.match(/^(.*)\.s?css$/i))) {
|
||||||
data[skin[1]] = { common: skinFile };
|
data[skin[1]] = skinFile;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -36,12 +36,7 @@ function reducePacks (data, into = {}) {
|
||||||
const skin = data.skin[skinName];
|
const skin = data.skin[skinName];
|
||||||
if (!skin) continue;
|
if (!skin) continue;
|
||||||
|
|
||||||
for (const entry in skin) {
|
into[`skins/${data.name}/${skinName}`] = resolve(skin);
|
||||||
const packFile = skin[entry];
|
|
||||||
if (!packFile) continue;
|
|
||||||
|
|
||||||
into[`skins/${data.name}/${skinName}/${entry}`] = resolve(packFile);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return into;
|
return into;
|
||||||
|
|
|
@ -8,6 +8,14 @@ namespace :assets do
|
||||||
def current_user
|
def current_user
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def current_flavour
|
||||||
|
Setting.default_settings['flavour']
|
||||||
|
end
|
||||||
|
|
||||||
|
def current_skin
|
||||||
|
Setting.default_settings['skin']
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
html = renderer.render(action, opts)
|
html = renderer.render(action, opts)
|
||||||
|
|
Loading…
Reference in New Issue