FEATURE: Sync user tips status between client (#19095)

The user attributes are not updated between clients and that is a
problem with user tips because the same user tip will be displayed
multiple times, once for every client.
This commit is contained in:
Bianca Nenciu
2022-11-21 20:57:02 +02:00
committed by GitHub
parent be99c3eec7
commit c78eb60cea
5 changed files with 94 additions and 14 deletions

View File

@ -0,0 +1,29 @@
export default {
name: "user-tips",
after: "message-bus",
initialize(container) {
const currentUser = container.lookup("service:current-user");
if (!currentUser) {
return;
}
const messageBus = container.lookup("service:message-bus");
const site = container.lookup("service:site");
messageBus.subscribe("/user-tips", function (seenUserTips) {
currentUser.set("seen_popups", seenUserTips);
if (!currentUser.user_option) {
currentUser.set("user_option", {});
}
currentUser.set("user_option.seen_popups", seenUserTips);
(seenUserTips || []).forEach((userTipId) => {
currentUser.hideUserTipForever(
Object.keys(site.user_tips).find(
(id) => site.user_tips[id] === userTipId
)
);
});
});
},
};

View File

@ -1173,27 +1173,28 @@ const User = RestModel.extend({
return; return;
} }
// Hide any shown user tips. // Hide user tips and maybe show the next one.
if (userTipId) {
hideUserTip(userTipId);
showNextUserTip();
} else {
hideAllUserTips();
}
// Update list of seen user tips.
let seenUserTips = this.seen_popups || []; let seenUserTips = this.seen_popups || [];
if (userTipId) { if (userTipId) {
if (seenUserTips.includes(userTips[userTipId])) { if (seenUserTips.includes(userTips[userTipId])) {
return; return;
} }
hideUserTip(userTipId);
seenUserTips.push(userTips[userTipId]); seenUserTips.push(userTips[userTipId]);
} else { } else {
if (seenUserTips.includes(-1)) { if (seenUserTips.includes(-1)) {
return; return;
} }
hideAllUserTips();
seenUserTips = [-1]; seenUserTips = [-1];
} }
// Show next user tip in queue.
showNextUserTip();
// Save seen user tips on the server. // Save seen user tips on the server.
if (!this.user_option) { if (!this.user_option) {
this.set("user_option", {}); this.set("user_option", {});

View File

@ -6,6 +6,7 @@ import { settled } from "@ember/test-helpers";
import User from "discourse/models/user"; import User from "discourse/models/user";
import pretender, { response } from "discourse/tests/helpers/create-pretender"; import pretender, { response } from "discourse/tests/helpers/create-pretender";
import { getOwner } from "discourse-common/lib/get-owner"; import { getOwner } from "discourse-common/lib/get-owner";
import * as userTips from "discourse/lib/user-tips";
module("Unit | Model | user", function (hooks) { module("Unit | Model | user", function (hooks) {
setupTest(hooks); setupTest(hooks);
@ -199,12 +200,11 @@ module("Unit | Model | user", function (hooks) {
test("hideUserTipForever() makes a single request", async function (assert) { test("hideUserTipForever() makes a single request", async function (assert) {
const site = getOwner(this).lookup("service:site"); const site = getOwner(this).lookup("service:site");
site.set("user_tips", { first_notification: 1 }); site.set("user_tips", { first_notification: 1 });
const store = getOwner(this).lookup("service:store"); const store = getOwner(this).lookup("service:store");
const user = store.createRecord("user", { username: "test" }); const user = store.createRecord("user", { username: "eviltrout" });
let requestsCount = 0; let requestsCount = 0;
pretender.put("/u/test.json", () => { pretender.put("/u/eviltrout.json", () => {
requestsCount += 1; requestsCount += 1;
return response(200, { return response(200, {
user: { user: {
@ -221,4 +221,30 @@ module("Unit | Model | user", function (hooks) {
await user.hideUserTipForever("first_notification"); await user.hideUserTipForever("first_notification");
assert.strictEqual(requestsCount, 1); assert.strictEqual(requestsCount, 1);
}); });
test("hideUserTipForever() can hide the user tip", async function (assert) {
const site = getOwner(this).lookup("service:site");
site.set("user_tips", { first_notification: 1 });
const store = getOwner(this).lookup("service:store");
const user = store.createRecord("user", { username: "eviltrout" });
const hideSpy = sinon.spy(userTips, "hideUserTip");
const showNextSpy = sinon.spy(userTips, "showNextUserTip");
await user.hideUserTipForever("first_notification");
assert.ok(hideSpy.calledWith("first_notification"));
assert.ok(showNextSpy.calledWith());
});
test("hideUserTipForever() can hide all the user tips", async function (assert) {
const site = getOwner(this).lookup("service:site");
site.set("user_tips", { first_notification: 1 });
const store = getOwner(this).lookup("service:store");
const user = store.createRecord("user", { username: "eviltrout" });
const hideAllSpy = sinon.spy(userTips, "hideAllUserTips");
await user.hideUserTipForever();
assert.ok(hideAllSpy.calledWith());
});
}); });

View File

@ -248,6 +248,13 @@ class UserUpdater
user_notification_schedule.create_do_not_disturb_timings(delete_existing: true) : user_notification_schedule.create_do_not_disturb_timings(delete_existing: true) :
user_notification_schedule.destroy_scheduled_timings user_notification_schedule.destroy_scheduled_timings
end end
if attributes.key?(:seen_popups) || attributes.key?(:skip_new_user_tips)
MessageBus.publish(
'/user-tips',
user.user_option.seen_popups,
user_ids: [user.id]
)
end
DiscourseEvent.trigger(:user_updated, user) DiscourseEvent.trigger(:user_updated, user)
end end

View File

@ -526,16 +526,33 @@ RSpec.describe UserUpdater do
end end
context 'when skip_new_user_tips is edited' do context 'when skip_new_user_tips is edited' do
it 'updates all fields' do it 'updates seen_popups too' do
UserUpdater.new(Discourse.system_user, user).update(skip_new_user_tips: true) messages = MessageBus.track_publish('/user-tips') do
UserUpdater.new(Discourse.system_user, user).update(skip_new_user_tips: true)
end
expect(user.user_option.skip_new_user_tips).to eq(true) expect(user.user_option.skip_new_user_tips).to eq(true)
expect(user.user_option.seen_popups).to eq([-1]) expect(user.user_option.seen_popups).to eq([-1])
expect(messages.map(&:data)).to contain_exactly([-1])
UserUpdater.new(Discourse.system_user, user).update(skip_new_user_tips: false) messages = MessageBus.track_publish('/user-tips') do
UserUpdater.new(Discourse.system_user, user).update(skip_new_user_tips: false)
end
expect(user.user_option.skip_new_user_tips).to eq(false) expect(user.user_option.skip_new_user_tips).to eq(false)
expect(user.user_option.seen_popups).to eq(nil) expect(user.user_option.seen_popups).to eq(nil)
expect(messages.map(&:data)).to contain_exactly(nil)
end
end
context 'when seen_popups is edited' do
it 'publishes a message' do
messages = MessageBus.track_publish('/user-tips') do
UserUpdater.new(Discourse.system_user, user).update(seen_popups: [1])
end
expect(user.user_option.seen_popups).to eq([1])
expect(messages.map(&:data)).to contain_exactly([1])
end end
end end