Skip to content

Commit

Permalink
FIX: Ensure live-reloading of theme CSS works first time (#8052)
Browse files Browse the repository at this point in the history
The client-side theme-selector would always apply the first in a series of file change notifications. This has been fixed, so it now applies the most recent notification.

Duplicate notifications were being sent because
- The remote_theme autosave was causing every change notification to be doubled
- Color scheme change notifications were being sent every time a theme was uploaded, even if the colors were unchanged

These duplicate notifications have been fixed, and a spec added to ensure it does not regress in future
  • Loading branch information
davidtaylorhq committed Aug 29, 2019
1 parent 8ef831a commit 98fbc01
Show file tree
Hide file tree
Showing 5 changed files with 21 additions and 15 deletions.
12 changes: 4 additions & 8 deletions app/assets/javascripts/discourse/lib/theme-selector.js.es6
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,12 @@ export function setLocalTheme(ids, themeSeq) {
}
}

export function refreshCSS(node, hash, newHref, options) {
export function refreshCSS(node, hash, newHref) {
let $orig = $(node);

if ($orig.data("reloading")) {
if (options && options.force) {
clearTimeout($orig.data("timeout"));
$orig.data("copy").remove();
} else {
return;
}
clearTimeout($orig.data("timeout"));
$orig.data("copy").remove();
}

if (!$orig.data("orig")) {
Expand Down Expand Up @@ -99,7 +95,7 @@ export function previewTheme(ids = []) {
`link[rel=stylesheet][data-target=${theme.target}]`
)[0];
if (node) {
refreshCSS(node, null, theme.new_href, { force: true });
refreshCSS(node, null, theme.new_href);
}
});
}
Expand Down
6 changes: 3 additions & 3 deletions app/models/remote_theme.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class ImportError < StandardError; end
GITHUB_REGEXP = /^https?:\/\/github\.com\//
GITHUB_SSH_REGEXP = /^git@github\.com:/

has_one :theme
has_one :theme, autosave: false
scope :joined_remotes, -> {
joins("JOIN themes ON themes.remote_theme_id = remote_themes.id").where.not(remote_url: "")
}
Expand Down Expand Up @@ -211,7 +211,7 @@ def update_theme_color_schemes(theme, schemes)
color_scheme_color = scheme.color_scheme_colors.to_a.find { |c| c.name == color[:name] } ||
scheme.color_scheme_colors.build(name: color[:name])
color_scheme_color.hex = override || color[:hex]
theme.notify_color_change(color_scheme_color)
theme.notify_color_change(color_scheme_color) if color_scheme_color.hex_changed?
end

# Update advanced colors
Expand All @@ -221,7 +221,7 @@ def update_theme_color_schemes(theme, schemes)
if override
color_scheme_color ||= scheme.color_scheme_colors.build(name: variable_name)
color_scheme_color.hex = override
theme.notify_color_change(color_scheme_color)
theme.notify_color_change(color_scheme_color) if color_scheme_color.hex_changed?
elsif color_scheme_color # No longer specified in about.json, delete record
scheme.color_scheme_colors.delete(color_scheme_color)
theme.notify_color_change(nil, scheme: scheme)
Expand Down
2 changes: 1 addition & 1 deletion app/models/theme.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class Theme < ActiveRecord::Base
has_many :child_themes, -> { order(:name) }, through: :child_theme_relation, source: :child_theme
has_many :parent_themes, -> { order(:name) }, through: :parent_theme_relation, source: :parent_theme
has_many :color_schemes
belongs_to :remote_theme, autosave: true, dependent: :destroy
belongs_to :remote_theme, dependent: :destroy

has_one :settings_field, -> { where(target_id: Theme.targets[:settings], name: "yaml") }, class_name: 'ThemeField'
has_one :javascript_cache, dependent: :destroy
Expand Down
1 change: 1 addition & 0 deletions spec/models/theme_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
it "can automatically disable for mismatching version" do
expect(theme.supported?).to eq(true)
theme.create_remote_theme!(remote_url: "", minimum_discourse_version: "99.99.99")
theme.save!
expect(theme.supported?).to eq(false)

expect(Theme.transform_ids([theme.id])).to be_empty
Expand Down
15 changes: 12 additions & 3 deletions spec/requests/admin/themes_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,20 @@
existing_theme = Fabricate(:theme, name: "Header Icons")
other_existing_theme = Fabricate(:theme, name: "Some other name")

expect do
post "/admin/themes/import.json", params: { bundle: theme_archive, theme_id: other_existing_theme.id }
end.to change { Theme.count }.by (0)
messages = MessageBus.track_publish do
expect do
post "/admin/themes/import.json", params: { bundle: theme_archive, theme_id: other_existing_theme.id }
end.to change { Theme.count }.by (0)
end
expect(response.status).to eq(201)
json = ::JSON.parse(response.body)

# Ensure only one refresh message is sent.
# More than 1 is wasteful, and can trigger unusual race conditions in the client
# If this test fails, it probably means `theme.save` is being called twice - check any 'autosave' relations
file_change_messages = messages.filter { |m| m[:channel] == "/file-change" }
expect(file_change_messages.count).to eq(1)

expect(json["theme"]["name"]).to eq("Some other name")
expect(json["theme"]["id"]).to eq(other_existing_theme.id)
expect(json["theme"]["theme_fields"].length).to eq(5)
Expand Down Expand Up @@ -383,6 +391,7 @@

it 'handles import errors on update' do
theme.create_remote_theme!(remote_url: "https://example.com/repository")
theme.save!

# RemoteTheme is extensively tested, and setting up the test scaffold is a large overhead
# So use a stub here to test the controller
Expand Down

0 comments on commit 98fbc01

Please sign in to comment.