From 1ebd3dbbd02a684b139e74b53d0545f15d78a1af Mon Sep 17 00:00:00 2001 From: David Taylor Date: Mon, 14 Jan 2019 13:21:46 +0000 Subject: [PATCH] FEATURE: Allow the base font size to be changed on a per-user basis (#6859) --- .../controllers/preferences/interface.js.es6 | 28 ++++++++++++++++++- .../javascripts/discourse/models/user.js.es6 | 3 +- .../templates/preferences/interface.hbs | 7 +++++ .../stylesheets/common/foundation/base.scss | 8 ++++++ .../common/foundation/variables.scss | 2 ++ app/helpers/application_helper.rb | 12 ++++++-- app/models/user_option.rb | 17 +++++++++++ app/serializers/user_option_serializer.rb | 3 +- app/services/user_updater.rb | 3 +- config/locales/client.en.yml | 7 +++++ config/locales/server.en.yml | 2 ++ config/site_settings.yml | 8 ++++++ ...10630_add_text_size_key_to_user_options.rb | 5 ++++ spec/helpers/application_helper_spec.rb | 22 ++++++++++++--- 14 files changed, 116 insertions(+), 11 deletions(-) create mode 100644 db/migrate/20190108110630_add_text_size_key_to_user_options.rb diff --git a/app/assets/javascripts/discourse/controllers/preferences/interface.js.es6 b/app/assets/javascripts/discourse/controllers/preferences/interface.js.es6 index 97cab16b0db..c9b8d796e71 100644 --- a/app/assets/javascripts/discourse/controllers/preferences/interface.js.es6 +++ b/app/assets/javascripts/discourse/controllers/preferences/interface.js.es6 @@ -20,6 +20,8 @@ const USER_HOMES = { 5: "top" }; +const TEXT_SIZES = ["normal", "larger", "largest"]; + export default Ember.Controller.extend(PreferencesTabController, { @computed("makeThemeDefault") saveAttrNames(makeDefault) { @@ -32,7 +34,8 @@ export default Ember.Controller.extend(PreferencesTabController, { "automatically_unpin_topics", "allow_private_messages", "homepage_id", - "hide_profile_and_presence" + "hide_profile_and_presence", + "text_size" ]; if (makeDefault) { @@ -55,6 +58,13 @@ export default Ember.Controller.extend(PreferencesTabController, { return currentThemeId(); }, + @computed + textSizes() { + return TEXT_SIZES.map(value => { + return { name: I18n.t(`user.text_size.${value}`), value }; + }); + }, + userSelectableThemes: function() { return listThemes(this.site); }.property(), @@ -114,6 +124,22 @@ export default Ember.Controller.extend(PreferencesTabController, { this.homeChanged(); }) .catch(popupAjaxError); + }, + + selectTextSize(newSize) { + const classList = document.documentElement.classList; + + TEXT_SIZES.forEach(name => { + const className = `text-size-${name}`; + if (newSize === name) { + classList.add(className); + } else { + classList.remove(className); + } + }); + + // Force refresh when leaving this screen + Discourse.set("assetVersion", "forceRefresh"); } } }); diff --git a/app/assets/javascripts/discourse/models/user.js.es6 b/app/assets/javascripts/discourse/models/user.js.es6 index 9b7d28e1ac4..70b6dbc7acd 100644 --- a/app/assets/javascripts/discourse/models/user.js.es6 +++ b/app/assets/javascripts/discourse/models/user.js.es6 @@ -285,7 +285,8 @@ const User = RestModel.extend({ "theme_ids", "allow_private_messages", "homepage_id", - "hide_profile_and_presence" + "hide_profile_and_presence", + "text_size" ]; if (fields) { diff --git a/app/assets/javascripts/discourse/templates/preferences/interface.hbs b/app/assets/javascripts/discourse/templates/preferences/interface.hbs index da84c8c605a..1be49ff9b54 100644 --- a/app/assets/javascripts/discourse/templates/preferences/interface.hbs +++ b/app/assets/javascripts/discourse/templates/preferences/interface.hbs @@ -10,6 +10,13 @@ {{/if}} +
+ +
+ {{combo-box valueAttribute="value" content=textSizes value=model.user_option.text_size onSelect=(action "selectTextSize")}} +
+
+ {{#if siteSettings.allow_user_locale}}
diff --git a/app/assets/stylesheets/common/foundation/base.scss b/app/assets/stylesheets/common/foundation/base.scss index 03fa374c22e..adf847650bc 100644 --- a/app/assets/stylesheets/common/foundation/base.scss +++ b/app/assets/stylesheets/common/foundation/base.scss @@ -13,6 +13,14 @@ html { background-color: $secondary; overflow-y: scroll; direction: ltr; + + &.text-size-larger { + font-size: $base-font-size-larger; + } + + &.text-size-largest { + font-size: $base-font-size-largest; + } } // Links diff --git a/app/assets/stylesheets/common/foundation/variables.scss b/app/assets/stylesheets/common/foundation/variables.scss index e0da4fcdaf3..b2090ddd3f8 100644 --- a/app/assets/stylesheets/common/foundation/variables.scss +++ b/app/assets/stylesheets/common/foundation/variables.scss @@ -33,6 +33,8 @@ $bronze: #cd7f32 !default; // -------------------------------------------------- $base-font-size: 14px !default; +$base-font-size-larger: 16px !default; +$base-font-size-largest: 18px !default; $base-font-family: Helvetica, Arial, sans-serif !default; // Font-size defintions, multiplier ^ (step / interval) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 82a5d5fbae6..d4a60fe613e 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -109,7 +109,12 @@ module ApplicationHelper end def html_classes - "#{mobile_view? ? 'mobile-view' : 'desktop-view'} #{mobile_device? ? 'mobile-device' : 'not-mobile-device'} #{rtl_class} #{current_user ? '' : 'anon'}" + list = [] + list << (mobile_view? ? 'mobile-view' : 'desktop-view') + list << (mobile_device? ? 'mobile-device' : 'not-mobile-device') + list << 'rtl' if rtl? + list << text_size_class + list.join(' ') end def body_classes @@ -126,8 +131,9 @@ module ApplicationHelper result.join(' ') end - def rtl_class - rtl? ? 'rtl' : '' + def text_size_class + size = current_user&.user_option&.text_size || SiteSetting.default_text_size + "text-size-#{size}" end def escape_unicode(javascript) diff --git a/app/models/user_option.rb b/app/models/user_option.rb index 4f2ba155c89..95b0d75795e 100644 --- a/app/models/user_option.rb +++ b/app/models/user_option.rb @@ -28,6 +28,12 @@ class UserOption < ActiveRecord::Base @like_notification_frequency_type ||= Enum.new(always: 0, first_time_and_daily: 1, first_time: 2, never: 3) end + def self.text_sizes + @text_sizes ||= Enum.new(normal: 0, larger: 1, largest: 2) + end + + validates :text_size_key, inclusion: { in: UserOption.text_sizes.values } + def set_defaults self.email_always = SiteSetting.default_email_always self.mailing_list_mode = SiteSetting.default_email_mailing_list_mode @@ -58,6 +64,8 @@ class UserOption < ActiveRecord::Base self.include_tl0_in_digests = SiteSetting.default_include_tl0_in_digests + self.text_size = SiteSetting.default_text_size + true end @@ -146,6 +154,14 @@ class UserOption < ActiveRecord::Base end end + def text_size + UserOption.text_sizes[text_size_key] + end + + def text_size=(value) + self.text_size_key = UserOption.text_sizes[value.to_sym] + end + private def update_tracked_topics @@ -185,6 +201,7 @@ end # homepage_id :integer # theme_ids :integer default([]), not null, is an Array # hide_profile_and_presence :boolean default(FALSE), not null +# text_size_key :integer default(0), not null # # Indexes # diff --git a/app/serializers/user_option_serializer.rb b/app/serializers/user_option_serializer.rb index 9df725e84cb..0a0e7048fab 100644 --- a/app/serializers/user_option_serializer.rb +++ b/app/serializers/user_option_serializer.rb @@ -23,7 +23,8 @@ class UserOptionSerializer < ApplicationSerializer :theme_key_seq, :allow_private_messages, :homepage_id, - :hide_profile_and_presence + :hide_profile_and_presence, + :text_size def auto_track_topics_after_msecs object.auto_track_topics_after_msecs || SiteSetting.default_other_auto_track_topics_after_msecs diff --git a/app/services/user_updater.rb b/app/services/user_updater.rb index 558f08953c4..3828b1da433 100644 --- a/app/services/user_updater.rb +++ b/app/services/user_updater.rb @@ -37,7 +37,8 @@ class UserUpdater :theme_ids, :allow_private_messages, :homepage_id, - :hide_profile_and_presence + :hide_profile_and_presence, + :text_size ] def initialize(actor, user) diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index b6d65d54138..a0ab970a488 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -909,6 +909,13 @@ en: website: "Web Site" email_settings: "Email" hide_profile_and_presence: "Hide my public profile and presence features" + + text_size: + title: "Text Size" + normal: "Normal" + larger: "Larger" + largest: "Largest" + like_notification_frequency: title: "Notify when liked" always: "Always" diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 4e3f1224165..8b1742fb678 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1877,6 +1877,8 @@ en: default_categories_muted: "List of categories that are muted by default." default_categories_watching_first_post: "List of categories in which first post in each new topic will be watched by default." + default_text_size: "Text size which is selected by default" + retain_web_hook_events_period_days: "Number of days to retain web hook event records." retry_web_hook_events: "Automatically retry failed web hook events for 4 times. Time gaps between the retries are 1, 5, 25 and 125 minutes." diff --git a/config/site_settings.yml b/config/site_settings.yml index 46f838f1cc1..a22ce2a8008 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -1811,6 +1811,14 @@ user_preferences: default_categories_watching_first_post: type: category_list default: '' + + default_text_size: + type: enum + default: normal + choices: + - normal + - larger + - largest api: retain_web_hook_events_period_days: diff --git a/db/migrate/20190108110630_add_text_size_key_to_user_options.rb b/db/migrate/20190108110630_add_text_size_key_to_user_options.rb new file mode 100644 index 00000000000..61edee561e6 --- /dev/null +++ b/db/migrate/20190108110630_add_text_size_key_to_user_options.rb @@ -0,0 +1,5 @@ +class AddTextSizeKeyToUserOptions < ActiveRecord::Migration[5.2] + def change + add_column :user_options, :text_size_key, :integer, null: false, default: 0 + end +end diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index f838326f972..0b1aa0ee6c3 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -147,15 +147,29 @@ describe ApplicationHelper do end end - describe '#rtl_class' do - it "returns 'rtl' when the I18n.locale is rtl" do + describe '#html_classes' do + it "includes 'rtl' when the I18n.locale is rtl" do I18n.stubs(:locale).returns(:he) - expect(helper.rtl_class).to eq('rtl') + expect(helper.html_classes.split(" ")).to include('rtl') end it 'returns an empty string when the I18n.locale is not rtl' do I18n.stubs(:locale).returns(:zh_TW) - expect(helper.rtl_class).to eq('') + expect(helper.html_classes.split(" ")).not_to include('rtl') + end + + it 'includes the user specified text size' do + user = Fabricate(:user) + user.user_option.text_size = "larger" + user.user_option.save! + helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user + expect(helper.html_classes.split(" ")).to include('text-size-larger') + end + + it 'falls back to the default text size for anon' do + expect(helper.html_classes.split(" ")).to include('text-size-normal') + SiteSetting.default_text_size = "largest" + expect(helper.html_classes.split(" ")).to include('text-size-largest') end end