From 98fe896176c3bdb7f36592a9811da1c87fa58d8a Mon Sep 17 00:00:00 2001 From: Chronister Date: Fri, 15 Sep 2017 00:35:54 -0700 Subject: [PATCH 01/11] Add support for selecting a theme --- .../settings/preferences_controller.rb | 1 + app/javascript/packs/common.js | 1 - app/lib/themes.rb | 16 +++++++++ app/lib/user_settings_decorator.rb | 5 +++ app/models/user.rb | 4 +++ app/views/layouts/application.html.haml | 1 + app/views/settings/preferences/show.html.haml | 1 + config/locales/simple_form.en.yml | 1 + config/settings.yml | 1 + config/themes.yml | 1 + config/webpack/configuration.js | 4 +++ config/webpack/shared.js | 35 ++++++++++--------- 12 files changed, 53 insertions(+), 18 deletions(-) create mode 100644 app/lib/themes.rb create mode 100644 config/themes.yml diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb index f107f2b165f84..207c7b3240b4c 100644 --- a/app/controllers/settings/preferences_controller.rb +++ b/app/controllers/settings/preferences_controller.rb @@ -41,6 +41,7 @@ def user_settings_params :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 ba7053f1fcbba..eb9f58025f5e4 100644 --- a/app/javascript/packs/common.js +++ b/app/javascript/packs/common.js @@ -2,7 +2,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); diff --git a/app/lib/themes.rb b/app/lib/themes.rb new file mode 100644 index 0000000000000..befa45debaf36 --- /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 62046ed7262a2..cb1b3c4a92740 100644 --- a/app/lib/user_settings_decorator.rb +++ b/app/lib/user_settings_decorator.rb @@ -25,6 +25,7 @@ def process_update 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 @@ def noindex_preference 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 5e548c1efb0d6..3bf069a315d78 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -110,6 +110,10 @@ def setting_noindex 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 88eff7d178d50..6a1aa0e65a9bd 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_account&.user&.setting_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/settings/preferences/show.html.haml b/app/views/settings/preferences/show.html.haml index f42f92508075e..2744ce792e0ac 100644 --- a/app/views/settings/preferences/show.html.haml +++ b/app/views/settings/preferences/show.html.haml @@ -51,6 +51,7 @@ .fields-group = f.input :setting_auto_play_gif, as: :boolean, wrapper: :with_label = f.input :setting_system_font_ui, as: :boolean, wrapper: :with_label + = f.input :setting_theme, collection: Themes.instance.names, wrapper: :with_label, include_blank: false .actions = f.button :button, t('generic.save_changes'), type: :submit diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index fb8524a24c0f0..57600f79ff296 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -44,6 +44,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: Theme severity: Severity type: Import type username: Username diff --git a/config/settings.yml b/config/settings.yml index ba63afa924ae4..c437b4ccbde02 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 0000000000000..a1049fae7de14 --- /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 6ef484c3a6291..8223294905f8e 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 b097a5fe44385..0f691701abfe0 100644 --- a/config/webpack/shared.js +++ b/config/webpack/shared.js @@ -7,7 +7,7 @@ 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 +15,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 +72,6 @@ module.exports = { ], resolve: { - alias: { - 'mastodon-application-style': existsSync(customApplicationStyle) ? - customApplicationStyle : originalApplicationStyle, - }, extensions: settings.extensions, modules: [ resolve(settings.source_path), From e1e394b94fae54f0558971682034cc7ae06a5e1d Mon Sep 17 00:00:00 2001 From: Chronister Date: Fri, 15 Sep 2017 01:36:35 -0700 Subject: [PATCH 02/11] Fix codeclimate issues --- app/lib/themes.rb | 2 +- config/webpack/shared.js | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/lib/themes.rb b/app/lib/themes.rb index befa45debaf36..243ffb9ab97cb 100644 --- a/app/lib/themes.rb +++ b/app/lib/themes.rb @@ -6,7 +6,7 @@ class Themes include Singleton - def initialize() + def initialize @conf = YAML.load_file(Rails.root.join('config', 'themes.yml')) end diff --git a/config/webpack/shared.js b/config/webpack/shared.js index 0f691701abfe0..ea2da6aa7e859 100644 --- a/config/webpack/shared.js +++ b/config/webpack/shared.js @@ -1,6 +1,5 @@ // 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'); @@ -16,7 +15,7 @@ const packPaths = sync(join(entryPath, extensionGlob)); const entryPacks = [...packPaths, ...localePackPaths].filter(path => path !== join(entryPath, 'custom.js')); const themePaths = Object.keys(themes).reduce( - (themePaths, name) => { + (themePaths, name) => { themePaths[name] = resolve(join(settings.source_path, themes[name])); return themePaths; }, {}); From 160ceacbd85a080eaf099c7ea779d7aab74bd47a Mon Sep 17 00:00:00 2001 From: Chronister Date: Fri, 15 Sep 2017 11:43:02 -0700 Subject: [PATCH 03/11] Look up site default style if current user is not available due to e.g. not being logged in --- app/views/layouts/application.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 6a1aa0e65a9bd..780fe32dd46d4 100755 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -19,7 +19,7 @@ = title = stylesheet_pack_tag 'common', media: 'all' - = stylesheet_pack_tag current_account&.user&.setting_theme, media: 'all' + = stylesheet_pack_tag (current_account&.user&.setting_theme || Setting.default_settings['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' }/ From f9ad20d1f04061d947cc96b43f3b4f2c5829d986 Mon Sep 17 00:00:00 2001 From: Chronister Date: Fri, 15 Sep 2017 13:13:00 -0700 Subject: [PATCH 04/11] Remove outdated comment in common.js --- app/javascript/packs/common.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/javascript/packs/common.js b/app/javascript/packs/common.js index eb9f58025f5e4..4880f0242814e 100644 --- a/app/javascript/packs/common.js +++ b/app/javascript/packs/common.js @@ -1,8 +1,6 @@ import { start } from 'rails-ujs'; -// import default stylesheet with variables require('font-awesome/css/font-awesome.css'); - require.context('../images/', true); start(); From 8780db68f7f93ca576984554066bdfe01c2ae778 Mon Sep 17 00:00:00 2001 From: Chronister Date: Sun, 17 Sep 2017 19:53:01 -0700 Subject: [PATCH 05/11] Address requested changes in themes PR --- app/controllers/application_controller.rb | 8 ++++++++ app/lib/themes.rb | 2 -- app/views/layouts/application.html.haml | 2 +- app/views/layouts/embedded.html.haml | 1 + app/views/settings/preferences/show.html.haml | 3 ++- config/locales/en.yml | 2 ++ config/locales/simple_form.en.yml | 3 ++- 7 files changed, 16 insertions(+), 5 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 0b40fb05b730f..ed8407dd3cd4c 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,13 @@ def current_session @current_session ||= SessionActivation.find_by(session_id: cookies.signed['_session_id']) end + def current_theme + if Themes.instance.names.include? current_account&.user&.setting_theme + return current_account&.user&.setting_theme + end + return Setting.default_settings['theme'] + end + def cache_collection(raw, klass) return raw unless klass.respond_to?(:with_includes) diff --git a/app/lib/themes.rb b/app/lib/themes.rb index 243ffb9ab97cb..a961ef21f7335 100644 --- a/app/lib/themes.rb +++ b/app/lib/themes.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - require 'singleton' require 'yaml' diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 780fe32dd46d4..15012de7ed587 100755 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -19,7 +19,7 @@ = title = stylesheet_pack_tag 'common', media: 'all' - = stylesheet_pack_tag (current_account&.user&.setting_theme || Setting.default_settings['theme']), 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 46dab2d0fa2d5..4439bbd682a23 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 current_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 2744ce792e0ac..5efd538e4e8f3 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, @@ -51,7 +53,6 @@ .fields-group = f.input :setting_auto_play_gif, as: :boolean, wrapper: :with_label = f.input :setting_system_font_ui, as: :boolean, wrapper: :with_label - = f.input :setting_theme, collection: Themes.instance.names, wrapper: :with_label, include_blank: false .actions = f.button :button, t('generic.save_changes'), type: :submit diff --git a/config/locales/en.yml b/config/locales/en.yml index 0f6bac9e1865e..4855d9ebf18eb 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -449,6 +449,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 57600f79ff296..c535a22fe4ed6 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -13,6 +13,7 @@ en: one: 1 character left other: %{count} 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,7 +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: Theme + setting_theme: Site theme severity: Severity type: Import type username: Username From 8b13827c9972c2487666052eea9219e3a2fe66a8 Mon Sep 17 00:00:00 2001 From: Chronister Date: Sun, 17 Sep 2017 19:55:43 -0700 Subject: [PATCH 06/11] Fix codeclimate issues --- app/controllers/application_controller.rb | 6 +++--- app/lib/themes.rb | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index ed8407dd3cd4c..e314f519309e8 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -79,10 +79,10 @@ def current_session end def current_theme - if Themes.instance.names.include? current_account&.user&.setting_theme - return current_account&.user&.setting_theme + if Themes.instance.names.include? current_account&.user&.setting_theme + return current_account&.user&.setting_theme end - return Setting.default_settings['theme'] + Setting.default_settings['theme'] end def cache_collection(raw, klass) diff --git a/app/lib/themes.rb b/app/lib/themes.rb index a961ef21f7335..243ffb9ab97cb 100644 --- a/app/lib/themes.rb +++ b/app/lib/themes.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'singleton' require 'yaml' From aa4d7ac3898b32bb1645cb60af13d892577d1c04 Mon Sep 17 00:00:00 2001 From: Chronister Date: Sun, 17 Sep 2017 22:23:35 -0700 Subject: [PATCH 07/11] Explicitly check current_account in application controller and only check theme availability if non-nil --- app/controllers/application_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index e314f519309e8..c790d038e0e78 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -79,7 +79,7 @@ def current_session end def current_theme - if Themes.instance.names.include? current_account&.user&.setting_theme + if current_account and Themes.instance.names.include? current_account.user.setting_theme return current_account&.user&.setting_theme end Setting.default_settings['theme'] From efdf67240f4f0c6608eed74b6ca73e44df11a7c3 Mon Sep 17 00:00:00 2001 From: Chronister Date: Mon, 18 Sep 2017 00:49:00 -0700 Subject: [PATCH 08/11] codeclimate --- app/controllers/application_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index c790d038e0e78..0c00485cd1239 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -79,7 +79,7 @@ def current_session end def current_theme - if current_account and Themes.instance.names.include? current_account.user.setting_theme + if current_account && Themes.instance.names.include? current_account.user.setting_theme return current_account&.user&.setting_theme end Setting.default_settings['theme'] From 3fc70b0391240b9a16e9628438c0572483d212e9 Mon Sep 17 00:00:00 2001 From: Chronister Date: Mon, 18 Sep 2017 02:33:27 -0700 Subject: [PATCH 09/11] explicit precedence with && --- app/controllers/application_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 0c00485cd1239..d0704562e9aed 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -79,7 +79,7 @@ def current_session end def current_theme - if current_account && Themes.instance.names.include? current_account.user.setting_theme + if (current_account && (Themes.instance.names.include? current_account.user.setting_theme)) return current_account&.user&.setting_theme end Setting.default_settings['theme'] From 142ac69c3c0df03553dda20b0f646817d5800cac Mon Sep 17 00:00:00 2001 From: Chronister Date: Mon, 18 Sep 2017 17:00:15 -0700 Subject: [PATCH 10/11] Fix code style in application_controller according to @nightpool's suggestion, use default style in embedded.html.haml --- app/controllers/application_controller.rb | 6 ++---- app/views/layouts/embedded.html.haml | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d0704562e9aed..874d9e802c988 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -79,10 +79,8 @@ def current_session end def current_theme - if (current_account && (Themes.instance.names.include? current_account.user.setting_theme)) - return current_account&.user&.setting_theme - end - Setting.default_settings['theme'] + return Setting.default_settings['theme'] unless Themes.instance.names.include? current_user&.setting_theme + return current_user.setting_theme end def cache_collection(raw, klass) diff --git a/app/views/layouts/embedded.html.haml b/app/views/layouts/embedded.html.haml index 4439bbd682a23..ac11cfbe72e4e 100644 --- a/app/views/layouts/embedded.html.haml +++ b/app/views/layouts/embedded.html.haml @@ -5,7 +5,7 @@ %meta{ name: 'robots', content: 'noindex' }/ = stylesheet_pack_tag 'common', media: 'all' - = stylesheet_pack_tag current_theme, 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' From b3d0e488e583a2d4ecc6296056dcf14100dd572a Mon Sep 17 00:00:00 2001 From: Chronister Date: Mon, 18 Sep 2017 17:02:47 -0700 Subject: [PATCH 11/11] codeclimate: indentation + return --- app/controllers/application_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 874d9e802c988..d5eca6ffb6012 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -79,8 +79,8 @@ def current_session end def current_theme - return Setting.default_settings['theme'] unless Themes.instance.names.include? current_user&.setting_theme - return current_user.setting_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)