diff --git a/app/assets/javascripts/discourse/app/models/user.js b/app/assets/javascripts/discourse/app/models/user.js index 098a902023e..213a452de7d 100644 --- a/app/assets/javascripts/discourse/app/models/user.js +++ b/app/assets/javascripts/discourse/app/models/user.js @@ -1180,23 +1180,37 @@ User.reopenClass(Singleton, { // user status tracking User.reopen(Evented, { + _subscribersCount: 0, _clearStatusTimerId: null, // always call stopTrackingStatus() when done with a user trackStatus() { - this.addObserver("status", this, "_statusChanged"); + if (this._subscribersCount === 0) { + this.addObserver("status", this, "_statusChanged"); - this.appEvents.on("user-status:changed", this, this._updateStatus); + this.appEvents.on("user-status:changed", this, this._updateStatus); - if (this.status && this.status.ends_at) { - this._scheduleStatusClearing(this.status.ends_at); + if (this.status && this.status.ends_at) { + this._scheduleStatusClearing(this.status.ends_at); + } } + + this._subscribersCount++; }, stopTrackingStatus() { - this.removeObserver("status", this, "_statusChanged"); - this.appEvents.off("user-status:changed", this, this._updateStatus); - this._unscheduleStatusClearing(); + if (this._subscribersCount === 0) { + return; + } + + if (this._subscribersCount === 1) { + // the last subscriber is unsubscribing + this.removeObserver("status", this, "_statusChanged"); + this.appEvents.off("user-status:changed", this, this._updateStatus); + this._unscheduleStatusClearing(); + } + + this._subscribersCount--; }, _statusChanged(sender, key) { diff --git a/app/assets/javascripts/discourse/tests/unit/models/user-test.js b/app/assets/javascripts/discourse/tests/unit/models/user-test.js index 58eb7d7cbb1..534a2b4455c 100644 --- a/app/assets/javascripts/discourse/tests/unit/models/user-test.js +++ b/app/assets/javascripts/discourse/tests/unit/models/user-test.js @@ -5,13 +5,7 @@ import PreloadStore from "discourse/lib/preload-store"; import sinon from "sinon"; import { settled } from "@ember/test-helpers"; -module("Unit | Model | user", function (hooks) { - hooks.afterEach(function () { - if (this.clock) { - this.clock.restore(); - } - }); - +module("Unit | Model | user", function () { test("staff", function (assert) { let user = User.create({ id: 1, username: "eviltrout" }); @@ -112,6 +106,29 @@ module("Unit | Model | user", function (hooks) { assert.ok(spyMomentGuess.notCalled); }); + test("subsequent calls to trackStatus and stopTrackingStatus increase and decrease subscribers counter", function (assert) { + const user = User.create(); + assert.equal(user._subscribersCount, 0); + + user.trackStatus(); + assert.equal(user._subscribersCount, 1); + + user.trackStatus(); + assert.equal(user._subscribersCount, 2); + + user.stopTrackingStatus(); + assert.equal(user._subscribersCount, 1); + + user.stopTrackingStatus(); + assert.equal(user._subscribersCount, 0); + }); + + test("attempt to stop tracking status if status wasn't tracked doesn't throw", function (assert) { + const user = User.create(); + user.stopTrackingStatus(); + assert.ok(true); + }); + test("clears statuses of several users correctly when receiving status updates via appEvents", function (assert) { const status1 = { description: "user1 status",