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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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;
}
// 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 || [];
if (userTipId) {
if (seenUserTips.includes(userTips[userTipId])) {
return;
}
hideUserTip(userTipId);
seenUserTips.push(userTips[userTipId]);
} else {
if (seenUserTips.includes(-1)) {
return;
}
hideAllUserTips();
seenUserTips = [-1];
}
// Show next user tip in queue.
showNextUserTip();
// Save seen user tips on the server.
if (!this.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 pretender, { response } from "discourse/tests/helpers/create-pretender";
import { getOwner } from "discourse-common/lib/get-owner";
import * as userTips from "discourse/lib/user-tips";
module("Unit | Model | user", function (hooks) {
setupTest(hooks);
@ -199,12 +200,11 @@ module("Unit | Model | user", function (hooks) {
test("hideUserTipForever() makes a single request", 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: "test" });
const user = store.createRecord("user", { username: "eviltrout" });
let requestsCount = 0;
pretender.put("/u/test.json", () => {
pretender.put("/u/eviltrout.json", () => {
requestsCount += 1;
return response(200, {
user: {
@ -221,4 +221,30 @@ module("Unit | Model | user", function (hooks) {
await user.hideUserTipForever("first_notification");
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.destroy_scheduled_timings
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)
end

View File

@ -526,16 +526,33 @@ RSpec.describe UserUpdater do
end
context 'when skip_new_user_tips is edited' do
it 'updates all fields' do
UserUpdater.new(Discourse.system_user, user).update(skip_new_user_tips: true)
it 'updates seen_popups too' do
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.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.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