diff options
-rw-r--r-- | app/controllers/application_controller.rb | 6 | ||||
-rw-r--r-- | app/controllers/settings/preferences_controller.rb | 1 | ||||
-rw-r--r-- | app/javascript/packs/common.js | 3 | ||||
-rw-r--r-- | app/lib/themes.rb | 16 | ||||
-rw-r--r-- | app/lib/user_settings_decorator.rb | 5 | ||||
-rw-r--r-- | app/models/user.rb | 4 | ||||
-rwxr-xr-x | app/views/layouts/application.html.haml | 1 | ||||
-rw-r--r-- | app/views/layouts/embedded.html.haml | 1 | ||||
-rw-r--r-- | app/views/settings/preferences/show.html.haml | 2 | ||||
-rw-r--r-- | config/locales/en.yml | 2 | ||||
-rw-r--r-- | config/locales/simple_form.en.yml | 2 | ||||
-rw-r--r-- | config/settings.yml | 1 | ||||
-rw-r--r-- | config/themes.yml | 1 | ||||
-rw-r--r-- | config/webpack/configuration.js | 4 | ||||
-rw-r--r-- | config/webpack/shared.js | 36 |
15 files changed, 64 insertions, 21 deletions
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 0b40fb05b..d5eca6ffb 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -12,6 +12,7 @@ class ApplicationController < ActionController::Base helper_method :current_account helper_method :current_session + helper_method :current_theme helper_method :single_user_mode? rescue_from ActionController::RoutingError, with: :not_found @@ -77,6 +78,11 @@ class ApplicationController < ActionController::Base @current_session ||= SessionActivation.find_by(session_id: cookies.signed['_session_id']) end + def current_theme + return Setting.default_settings['theme'] unless Themes.instance.names.include? current_user&.setting_theme + current_user.setting_theme + end + def cache_collection(raw, klass) return raw unless klass.respond_to?(:with_includes) diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb index f107f2b16..207c7b324 100644 --- a/app/controllers/settings/preferences_controller.rb +++ b/app/controllers/settings/preferences_controller.rb @@ -41,6 +41,7 @@ class Settings::PreferencesController < ApplicationController :setting_auto_play_gif, :setting_system_font_ui, :setting_noindex, + :setting_theme, notification_emails: %i(follow follow_request reblog favourite mention digest), interactions: %i(must_be_follower must_be_following) ) diff --git a/app/javascript/packs/common.js b/app/javascript/packs/common.js index ba7053f1f..4880f0242 100644 --- a/app/javascript/packs/common.js +++ b/app/javascript/packs/common.js @@ -1,9 +1,6 @@ import { start } from 'rails-ujs'; -// import default stylesheet with variables require('font-awesome/css/font-awesome.css'); -require('mastodon-application-style'); - require.context('../images/', true); start(); diff --git a/app/lib/themes.rb b/app/lib/themes.rb new file mode 100644 index 000000000..243ffb9ab --- /dev/null +++ b/app/lib/themes.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require 'singleton' +require 'yaml' + +class Themes + include Singleton + + def initialize + @conf = YAML.load_file(Rails.root.join('config', 'themes.yml')) + end + + def names + @conf.keys + end +end diff --git a/app/lib/user_settings_decorator.rb b/app/lib/user_settings_decorator.rb index 62046ed72..cb1b3c4a9 100644 --- a/app/lib/user_settings_decorator.rb +++ b/app/lib/user_settings_decorator.rb @@ -25,6 +25,7 @@ class UserSettingsDecorator user.settings['auto_play_gif'] = auto_play_gif_preference user.settings['system_font_ui'] = system_font_ui_preference user.settings['noindex'] = noindex_preference + user.settings['theme'] = theme_preference end def merged_notification_emails @@ -67,6 +68,10 @@ class UserSettingsDecorator boolean_cast_setting 'setting_noindex' end + def theme_preference + settings['setting_theme'] + end + def boolean_cast_setting(key) settings[key] == '1' end diff --git a/app/models/user.rb b/app/models/user.rb index 5e548c1ef..3bf069a31 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -110,6 +110,10 @@ class User < ApplicationRecord settings.noindex end + def setting_theme + settings.theme + end + def token_for_app(a) return nil if a.nil? || a.owner != self Doorkeeper::AccessToken diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 88eff7d17..15012de7e 100755 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -19,6 +19,7 @@ = title = stylesheet_pack_tag 'common', media: 'all' + = stylesheet_pack_tag current_theme, media: 'all' = javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous' %link{ href: asset_pack_path('features/getting_started.js'), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/ diff --git a/app/views/layouts/embedded.html.haml b/app/views/layouts/embedded.html.haml index 46dab2d0f..ac11cfbe7 100644 --- a/app/views/layouts/embedded.html.haml +++ b/app/views/layouts/embedded.html.haml @@ -5,6 +5,7 @@ %meta{ name: 'robots', content: 'noindex' }/ = stylesheet_pack_tag 'common', media: 'all' + = stylesheet_pack_tag Setting.default_settings['theme'], media: 'all' = javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous' = javascript_pack_tag "locale_#{I18n.locale}", integrity: true, crossorigin: 'anonymous' = javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous' diff --git a/app/views/settings/preferences/show.html.haml b/app/views/settings/preferences/show.html.haml index f42f92508..5efd538e4 100644 --- a/app/views/settings/preferences/show.html.haml +++ b/app/views/settings/preferences/show.html.haml @@ -5,6 +5,8 @@ = render 'shared/error_messages', object: current_user .fields-group + = f.input :setting_theme, collection: Themes.instance.names, label_method: lambda { |theme| safe_join([I18n.t("themes.#{theme}", default: theme)])}, wrapper: :with_label, include_blank: false + = f.input :locale, collection: I18n.available_locales, wrapper: :with_label, diff --git a/config/locales/en.yml b/config/locales/en.yml index 9013f0ac9..0f3812aff 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -461,6 +461,8 @@ en: settings: Settings two_factor_authentication: Two-factor Authentication your_apps: Your applications + themes: + default: Mastodon statuses: open_in_web: Open in web over_character_limit: character limit of %{max} exceeded diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index fb8524a24..c535a22fe 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -13,6 +13,7 @@ en: one: <span class="note-counter">1</span> character left other: <span class="note-counter">%{count}</span> characters left setting_noindex: Affects your public profile and status pages + setting_theme: Affects how Mastodon looks when you're logged in from any device. imports: data: CSV file exported from another Mastodon instance sessions: @@ -44,6 +45,7 @@ en: setting_noindex: Opt-out of search engine indexing setting_system_font_ui: Use system's default font setting_unfollow_modal: Show confirmation dialog before unfollowing someone + setting_theme: Site theme severity: Severity type: Import type username: Username diff --git a/config/settings.yml b/config/settings.yml index ba63afa92..c437b4ccb 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -24,6 +24,7 @@ defaults: &defaults auto_play_gif: false system_font_ui: false noindex: false + theme: 'default' notification_emails: follow: false reblog: false diff --git a/config/themes.yml b/config/themes.yml new file mode 100644 index 000000000..a1049fae7 --- /dev/null +++ b/config/themes.yml @@ -0,0 +1 @@ +default: styles/application.scss diff --git a/config/webpack/configuration.js b/config/webpack/configuration.js index 6ef484c3a..822329490 100644 --- a/config/webpack/configuration.js +++ b/config/webpack/configuration.js @@ -9,6 +9,9 @@ const configPath = resolve('config', 'webpacker.yml'); const loadersDir = join(__dirname, 'loaders'); const settings = safeLoad(readFileSync(configPath), 'utf8')[env.NODE_ENV]; +const themePath = resolve('config', 'themes.yml'); +const themes = safeLoad(readFileSync(themePath), 'utf8'); + function removeOuterSlashes(string) { return string.replace(/^\/*/, '').replace(/\/*$/, ''); } @@ -29,6 +32,7 @@ const output = { module.exports = { settings, + themes, env, loadersDir, output, diff --git a/config/webpack/shared.js b/config/webpack/shared.js index b097a5fe4..ea2da6aa7 100644 --- a/config/webpack/shared.js +++ b/config/webpack/shared.js @@ -1,13 +1,12 @@ // Note: You must restart bin/webpack-dev-server for changes to take effect -const { existsSync } = require('fs'); const webpack = require('webpack'); const { basename, dirname, join, relative, resolve, sep } = require('path'); const { sync } = require('glob'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); const ManifestPlugin = require('webpack-manifest-plugin'); const extname = require('path-complete-extname'); -const { env, settings, output, loadersDir } = require('./configuration.js'); +const { env, settings, themes, output, loadersDir } = require('./configuration.js'); const localePackPaths = require('./generateLocalePacks'); const extensionGlob = `**/*{${settings.extensions.join(',')}}*`; @@ -15,20 +14,25 @@ const entryPath = join(settings.source_path, settings.source_entry_path); const packPaths = sync(join(entryPath, extensionGlob)); const entryPacks = [...packPaths, ...localePackPaths].filter(path => path !== join(entryPath, 'custom.js')); -const customApplicationStyle = resolve(join(settings.source_path, 'styles/custom.scss')); -const originalApplicationStyle = resolve(join(settings.source_path, 'styles/application.scss')); +const themePaths = Object.keys(themes).reduce( + (themePaths, name) => { + themePaths[name] = resolve(join(settings.source_path, themes[name])); + return themePaths; + }, {}); module.exports = { - entry: entryPacks.reduce( - (map, entry) => { - const localMap = map; - let namespace = relative(join(entryPath), dirname(entry)); - if (namespace === join('..', '..', '..', 'tmp', 'packs')) { - namespace = ''; // generated by generateLocalePacks.js - } - localMap[join(namespace, basename(entry, extname(entry)))] = resolve(entry); - return localMap; - }, {} + entry: Object.assign( + entryPacks.reduce( + (map, entry) => { + const localMap = map; + let namespace = relative(join(entryPath), dirname(entry)); + if (namespace === join('..', '..', '..', 'tmp', 'packs')) { + namespace = ''; // generated by generateLocalePacks.js + } + localMap[join(namespace, basename(entry, extname(entry)))] = resolve(entry); + return localMap; + }, {} + ), themePaths ), output: { @@ -67,10 +71,6 @@ module.exports = { ], resolve: { - alias: { - 'mastodon-application-style': existsSync(customApplicationStyle) ? - customApplicationStyle : originalApplicationStyle, - }, extensions: settings.extensions, modules: [ resolve(settings.source_path), |