FEATURE: allow users to select theme on single device
This commit is contained in:
parent
a0c936dadb
commit
e1dd543a93
|
@ -1,21 +1,30 @@
|
|||
import PreferencesTabController from "discourse/mixins/preferences-tab-controller";
|
||||
import { default as computed, observes } from "ember-addons/ember-computed-decorators";
|
||||
import { listThemes, previewTheme } from 'discourse/lib/theme-selector';
|
||||
import { listThemes, previewTheme, setLocalTheme } from 'discourse/lib/theme-selector';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
|
||||
export default Ember.Controller.extend(PreferencesTabController, {
|
||||
|
||||
saveAttrNames: [
|
||||
'locale',
|
||||
'external_links_in_new_tab',
|
||||
'dynamic_favicon',
|
||||
'enable_quoting',
|
||||
'disable_jump_reply',
|
||||
'automatically_unpin_topics',
|
||||
'theme_key'
|
||||
],
|
||||
@computed("makeThemeDefault")
|
||||
saveAttrNames(makeDefault) {
|
||||
let attrs = [
|
||||
'locale',
|
||||
'external_links_in_new_tab',
|
||||
'dynamic_favicon',
|
||||
'enable_quoting',
|
||||
'disable_jump_reply',
|
||||
'automatically_unpin_topics'
|
||||
];
|
||||
|
||||
if (makeDefault) {
|
||||
attrs.push('theme_key');
|
||||
}
|
||||
|
||||
return attrs;
|
||||
},
|
||||
|
||||
preferencesController: Ember.inject.controller('preferences'),
|
||||
makeThemeDefault: true,
|
||||
|
||||
@computed()
|
||||
availableLocales() {
|
||||
|
@ -40,8 +49,15 @@ export default Ember.Controller.extend(PreferencesTabController, {
|
|||
actions: {
|
||||
save() {
|
||||
this.set('saved', false);
|
||||
const makeThemeDefault = this.get("makeThemeDefault");
|
||||
|
||||
return this.get('model').save(this.get('saveAttrNames')).then(() => {
|
||||
this.set('saved', true);
|
||||
|
||||
if (!makeThemeDefault) {
|
||||
setLocalTheme(this.get('model.user_option.theme_key'), this.get('model.user_option.theme_key_seq'));
|
||||
}
|
||||
|
||||
}).catch(popupAjaxError);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,14 @@ export function currentThemeKey() {
|
|||
return themeKey;
|
||||
}
|
||||
|
||||
export function setLocalTheme(key, themeSeq) {
|
||||
if (key) {
|
||||
$.cookie('theme_key', `${key},${themeSeq}`, {path: '/', expires: 9999});
|
||||
} else {
|
||||
$.cookie('theme_key', null, {path: '/', expires: 1});
|
||||
}
|
||||
}
|
||||
|
||||
export function refreshCSS(node, hash, newHref, options) {
|
||||
|
||||
let $orig = $(node);
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
<div class="controls">
|
||||
{{combo-box content=userSelectableThemes value=model.user_option.theme_key}}
|
||||
</div>
|
||||
<div class="controls">
|
||||
{{preference-checkbox labelKey="user.theme_default_on_all_devices" checked=makeThemeDefault}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
|
|
|
@ -267,13 +267,19 @@ class ApplicationController < ActionController::Base
|
|||
resolve_safe_mode
|
||||
return if request.env[NO_CUSTOM]
|
||||
|
||||
theme_key = flash[:preview_theme_key] || current_user&.user_option&.theme_key
|
||||
theme_key = flash[:preview_theme_key]
|
||||
|
||||
# TODO 2018: delete this, old cookie cleanup code
|
||||
if cookies[:theme_key]
|
||||
cookies.delete(:theme_key)
|
||||
user_option = current_user&.user_option
|
||||
|
||||
unless theme_key
|
||||
key, seq = cookies[:theme_key]&.split(",")
|
||||
if key && seq && seq.to_i == user_option&.theme_key_seq
|
||||
theme_key = key
|
||||
end
|
||||
end
|
||||
|
||||
theme_key ||= user_option&.theme_key
|
||||
|
||||
if theme_key && !guardian.allow_theme?(theme_key)
|
||||
theme_key = nil
|
||||
end
|
||||
|
|
|
@ -158,6 +158,7 @@ end
|
|||
# include_tl0_in_digests :boolean default(FALSE)
|
||||
# notification_level_when_replying :integer
|
||||
# theme_key :string
|
||||
# theme_key_seq :integer default(0), not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
|
|
|
@ -19,7 +19,8 @@ class UserOptionSerializer < ApplicationSerializer
|
|||
:email_in_reply_to,
|
||||
:like_notification_frequency,
|
||||
:include_tl0_in_digests,
|
||||
:theme_key
|
||||
:theme_key,
|
||||
:theme_key_seq
|
||||
|
||||
|
||||
def auto_track_topics_after_msecs
|
||||
|
|
|
@ -76,6 +76,11 @@ class UserUpdater
|
|||
|
||||
save_options = false
|
||||
|
||||
# special handling for theme_key cause we need to bump a sequence number
|
||||
if attributes.key?(:theme_key) && user.user_option.theme_key != attributes[:theme_key]
|
||||
user.user_option.theme_key_seq += 1
|
||||
end
|
||||
|
||||
OPTION_ATTR.each do |attribute|
|
||||
if attributes.key?(attribute)
|
||||
save_options = true
|
||||
|
|
|
@ -581,6 +581,7 @@ en:
|
|||
first_notification: "Your first notification! Select it to begin."
|
||||
disable_jump_reply: "Don't jump to my post after I reply"
|
||||
dynamic_favicon: "Show new / updated topic count on browser icon"
|
||||
theme_default_on_all_devices: "Make this my default theme on all my devices"
|
||||
external_links_in_new_tab: "Open all external links in a new tab"
|
||||
enable_quoting: "Enable quote reply for highlighted text"
|
||||
change: "change"
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
class AddThemeKeySeqToUserOptions < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :user_options, :theme_key_seq, :integer, null: false, default: 0
|
||||
end
|
||||
end
|
|
@ -18,8 +18,15 @@ describe TopicsController do
|
|||
end
|
||||
|
||||
describe "themes" do
|
||||
let :theme do
|
||||
Theme.create!(user_id: -1, name: 'bob', user_selectable: true)
|
||||
end
|
||||
|
||||
let :theme2 do
|
||||
Theme.create!(user_id: -1, name: 'bobbob', user_selectable: true)
|
||||
end
|
||||
|
||||
it "selects the theme the user has selected" do
|
||||
theme = Theme.create!(user_id: -1, name: 'bob', user_selectable: true)
|
||||
user = log_in
|
||||
user.user_option.update_columns(theme_key: theme.key)
|
||||
|
||||
|
@ -31,6 +38,26 @@ describe TopicsController do
|
|||
get :show, id: 666
|
||||
expect(controller.theme_key).not_to eq(theme.key)
|
||||
end
|
||||
|
||||
it "can be overridden with a cookie" do
|
||||
user = log_in
|
||||
user.user_option.update_columns(theme_key: theme.key)
|
||||
|
||||
cookies['theme_key'] = "#{theme2.key},#{user.user_option.theme_key_seq}"
|
||||
|
||||
get :show, id: 666
|
||||
expect(controller.theme_key).to eq(theme2.key)
|
||||
|
||||
end
|
||||
|
||||
it "cookie can fail back to user if out of sync" do
|
||||
user = log_in
|
||||
user.user_option.update_columns(theme_key: theme.key)
|
||||
cookies['theme_key'] = "#{theme2.key},#{user.user_option.theme_key_seq-1}"
|
||||
|
||||
get :show, id: 666
|
||||
expect(controller.theme_key).to eq(theme.key)
|
||||
end
|
||||
end
|
||||
|
||||
it "doesn't store an incoming link when there's no referer" do
|
||||
|
|
|
@ -66,6 +66,10 @@ describe UserUpdater do
|
|||
updater = UserUpdater.new(acting_user, user)
|
||||
date_of_birth = Time.zone.now
|
||||
|
||||
theme = Theme.create!(user_id: -1, name: "test", user_selectable: true)
|
||||
|
||||
seq = user.user_option.theme_key_seq
|
||||
|
||||
val = updater.update(bio_raw: 'my new bio',
|
||||
email_always: 'true',
|
||||
mailing_list_mode: true,
|
||||
|
@ -74,7 +78,8 @@ describe UserUpdater do
|
|||
auto_track_topics_after_msecs: 101,
|
||||
notification_level_when_replying: 3,
|
||||
email_in_reply_to: false,
|
||||
date_of_birth: date_of_birth
|
||||
date_of_birth: date_of_birth,
|
||||
theme_key: theme.key
|
||||
)
|
||||
expect(val).to be_truthy
|
||||
|
||||
|
@ -88,6 +93,8 @@ describe UserUpdater do
|
|||
expect(user.user_option.auto_track_topics_after_msecs).to eq 101
|
||||
expect(user.user_option.notification_level_when_replying).to eq 3
|
||||
expect(user.user_option.email_in_reply_to).to eq false
|
||||
expect(user.user_option.theme_key).to eq theme.key
|
||||
expect(user.user_option.theme_key_seq).to eq(seq+1)
|
||||
expect(user.date_of_birth).to eq(date_of_birth.to_date)
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue