FEATURE: Allow the base font size to be changed on a per-user basis (#6859)

This commit is contained in:
David Taylor 2019-01-14 13:21:46 +00:00 committed by GitHub
parent 59e3eecfa6
commit 1ebd3dbbd0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 116 additions and 11 deletions

View File

@ -20,6 +20,8 @@ const USER_HOMES = {
5: "top" 5: "top"
}; };
const TEXT_SIZES = ["normal", "larger", "largest"];
export default Ember.Controller.extend(PreferencesTabController, { export default Ember.Controller.extend(PreferencesTabController, {
@computed("makeThemeDefault") @computed("makeThemeDefault")
saveAttrNames(makeDefault) { saveAttrNames(makeDefault) {
@ -32,7 +34,8 @@ export default Ember.Controller.extend(PreferencesTabController, {
"automatically_unpin_topics", "automatically_unpin_topics",
"allow_private_messages", "allow_private_messages",
"homepage_id", "homepage_id",
"hide_profile_and_presence" "hide_profile_and_presence",
"text_size"
]; ];
if (makeDefault) { if (makeDefault) {
@ -55,6 +58,13 @@ export default Ember.Controller.extend(PreferencesTabController, {
return currentThemeId(); return currentThemeId();
}, },
@computed
textSizes() {
return TEXT_SIZES.map(value => {
return { name: I18n.t(`user.text_size.${value}`), value };
});
},
userSelectableThemes: function() { userSelectableThemes: function() {
return listThemes(this.site); return listThemes(this.site);
}.property(), }.property(),
@ -114,6 +124,22 @@ export default Ember.Controller.extend(PreferencesTabController, {
this.homeChanged(); this.homeChanged();
}) })
.catch(popupAjaxError); .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");
} }
} }
}); });

View File

@ -285,7 +285,8 @@ const User = RestModel.extend({
"theme_ids", "theme_ids",
"allow_private_messages", "allow_private_messages",
"homepage_id", "homepage_id",
"hide_profile_and_presence" "hide_profile_and_presence",
"text_size"
]; ];
if (fields) { if (fields) {

View File

@ -10,6 +10,13 @@
</div> </div>
{{/if}} {{/if}}
<div class="control-group text-size">
<label class="control-label">{{i18n 'user.text_size.title'}}</label>
<div class="controls">
{{combo-box valueAttribute="value" content=textSizes value=model.user_option.text_size onSelect=(action "selectTextSize")}}
</div>
</div>
{{#if siteSettings.allow_user_locale}} {{#if siteSettings.allow_user_locale}}
<div class="control-group pref-locale"> <div class="control-group pref-locale">
<label class="control-label">{{i18n 'user.locale.title'}}</label> <label class="control-label">{{i18n 'user.locale.title'}}</label>

View File

@ -13,6 +13,14 @@ html {
background-color: $secondary; background-color: $secondary;
overflow-y: scroll; overflow-y: scroll;
direction: ltr; direction: ltr;
&.text-size-larger {
font-size: $base-font-size-larger;
}
&.text-size-largest {
font-size: $base-font-size-largest;
}
} }
// Links // Links

View File

@ -33,6 +33,8 @@ $bronze: #cd7f32 !default;
// -------------------------------------------------- // --------------------------------------------------
$base-font-size: 14px !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; $base-font-family: Helvetica, Arial, sans-serif !default;
// Font-size defintions, multiplier ^ (step / interval) // Font-size defintions, multiplier ^ (step / interval)

View File

@ -109,7 +109,12 @@ module ApplicationHelper
end end
def html_classes 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 end
def body_classes def body_classes
@ -126,8 +131,9 @@ module ApplicationHelper
result.join(' ') result.join(' ')
end end
def rtl_class def text_size_class
rtl? ? 'rtl' : '' size = current_user&.user_option&.text_size || SiteSetting.default_text_size
"text-size-#{size}"
end end
def escape_unicode(javascript) def escape_unicode(javascript)

View File

@ -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) @like_notification_frequency_type ||= Enum.new(always: 0, first_time_and_daily: 1, first_time: 2, never: 3)
end 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 def set_defaults
self.email_always = SiteSetting.default_email_always self.email_always = SiteSetting.default_email_always
self.mailing_list_mode = SiteSetting.default_email_mailing_list_mode 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.include_tl0_in_digests = SiteSetting.default_include_tl0_in_digests
self.text_size = SiteSetting.default_text_size
true true
end end
@ -146,6 +154,14 @@ class UserOption < ActiveRecord::Base
end end
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 private
def update_tracked_topics def update_tracked_topics
@ -185,6 +201,7 @@ end
# homepage_id :integer # homepage_id :integer
# theme_ids :integer default([]), not null, is an Array # theme_ids :integer default([]), not null, is an Array
# hide_profile_and_presence :boolean default(FALSE), not null # hide_profile_and_presence :boolean default(FALSE), not null
# text_size_key :integer default(0), not null
# #
# Indexes # Indexes
# #

View File

@ -23,7 +23,8 @@ class UserOptionSerializer < ApplicationSerializer
:theme_key_seq, :theme_key_seq,
:allow_private_messages, :allow_private_messages,
:homepage_id, :homepage_id,
:hide_profile_and_presence :hide_profile_and_presence,
:text_size
def auto_track_topics_after_msecs def auto_track_topics_after_msecs
object.auto_track_topics_after_msecs || SiteSetting.default_other_auto_track_topics_after_msecs object.auto_track_topics_after_msecs || SiteSetting.default_other_auto_track_topics_after_msecs

View File

@ -37,7 +37,8 @@ class UserUpdater
:theme_ids, :theme_ids,
:allow_private_messages, :allow_private_messages,
:homepage_id, :homepage_id,
:hide_profile_and_presence :hide_profile_and_presence,
:text_size
] ]
def initialize(actor, user) def initialize(actor, user)

View File

@ -909,6 +909,13 @@ en:
website: "Web Site" website: "Web Site"
email_settings: "Email" email_settings: "Email"
hide_profile_and_presence: "Hide my public profile and presence features" 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: like_notification_frequency:
title: "Notify when liked" title: "Notify when liked"
always: "Always" always: "Always"

View File

@ -1877,6 +1877,8 @@ en:
default_categories_muted: "List of categories that are muted by default." 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_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." 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." 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."

View File

@ -1812,6 +1812,14 @@ user_preferences:
type: category_list type: category_list
default: '' default: ''
default_text_size:
type: enum
default: normal
choices:
- normal
- larger
- largest
api: api:
retain_web_hook_events_period_days: retain_web_hook_events_period_days:
default: 30 default: 30

View File

@ -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

View File

@ -147,15 +147,29 @@ describe ApplicationHelper do
end end
end end
describe '#rtl_class' do describe '#html_classes' do
it "returns 'rtl' when the I18n.locale is rtl" do it "includes 'rtl' when the I18n.locale is rtl" do
I18n.stubs(:locale).returns(:he) I18n.stubs(:locale).returns(:he)
expect(helper.rtl_class).to eq('rtl') expect(helper.html_classes.split(" ")).to include('rtl')
end end
it 'returns an empty string when the I18n.locale is not rtl' do it 'returns an empty string when the I18n.locale is not rtl' do
I18n.stubs(:locale).returns(:zh_TW) 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
end end