mirror of
https://github.com/discourse/discourse.git
synced 2024-11-23 02:19:27 +08:00
DEV: Minor refactor of screen-track service (#25699)
Set/Map, async/await, inline vars, updated tests
This commit is contained in:
parent
9199c52e5e
commit
428db40deb
|
@ -252,28 +252,27 @@ export default MountWidget.extend({
|
|||
this._currentPercent = null;
|
||||
}
|
||||
|
||||
const onscreenPostNumbers = [];
|
||||
const readPostNumbers = [];
|
||||
const onscreenPostNumbers = new Set();
|
||||
const readPostNumbers = new Set();
|
||||
|
||||
const prev = this._previouslyNearby;
|
||||
const newPrev = {};
|
||||
const newPrev = new Set();
|
||||
nearby.forEach((idx) => {
|
||||
const post = posts.objectAt(idx);
|
||||
const postNumber = post.post_number;
|
||||
|
||||
delete prev[postNumber];
|
||||
this._previouslyNearby.delete(post.post_number);
|
||||
|
||||
if (onscreen.includes(idx)) {
|
||||
onscreenPostNumbers.push(postNumber);
|
||||
onscreenPostNumbers.add(post.post_number);
|
||||
if (post.read) {
|
||||
readPostNumbers.push(postNumber);
|
||||
readPostNumbers.add(post.post_number);
|
||||
}
|
||||
}
|
||||
newPrev[postNumber] = post;
|
||||
|
||||
newPrev.add(post.post_number, post);
|
||||
uncloak(post, this);
|
||||
});
|
||||
|
||||
Object.values(prev).forEach((node) => cloak(node, this));
|
||||
Object.values(this._previouslyNearby).forEach((node) => cloak(node, this));
|
||||
|
||||
this._previouslyNearby = newPrev;
|
||||
this.screenTrack.setOnscreen(onscreenPostNumbers, readPostNumbers);
|
||||
|
@ -323,7 +322,7 @@ export default MountWidget.extend({
|
|||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
this._previouslyNearby = {};
|
||||
this._previouslyNearby = new Set();
|
||||
|
||||
this.appEvents.on("post-stream:refresh", this, "_debouncedScroll");
|
||||
const opts = {
|
||||
|
|
|
@ -27,16 +27,17 @@ export default class ScreenTrack extends Service {
|
|||
@service siteSettings;
|
||||
@service topicTrackingState;
|
||||
|
||||
_ajaxFailures = 0;
|
||||
_consolidatedTimings = [];
|
||||
_lastTick = null;
|
||||
_lastScrolled = null;
|
||||
_lastFlush = 0;
|
||||
_timings = {};
|
||||
_totalTimings = {};
|
||||
_timings = new Map();
|
||||
_totalTimings = new Map();
|
||||
_topicTime = 0;
|
||||
_onscreen = [];
|
||||
_readOnscreen = [];
|
||||
_readPosts = {};
|
||||
_onscreen = null;
|
||||
_readOnscreen = null;
|
||||
_readPosts = new Set();
|
||||
_inProgress = false;
|
||||
|
||||
constructor() {
|
||||
|
@ -45,9 +46,7 @@ export default class ScreenTrack extends Service {
|
|||
}
|
||||
|
||||
start(topicId, topicController) {
|
||||
const currentTopicId = this._topicId;
|
||||
|
||||
if (currentTopicId && currentTopicId !== topicId) {
|
||||
if (this._topicId && this._topicId !== topicId) {
|
||||
this.tick();
|
||||
this.flush();
|
||||
}
|
||||
|
@ -98,12 +97,12 @@ export default class ScreenTrack extends Service {
|
|||
this._lastTick = now;
|
||||
this._lastScrolled = now;
|
||||
this._lastFlush = 0;
|
||||
this._timings = {};
|
||||
this._totalTimings = {};
|
||||
this._timings.clear();
|
||||
this._totalTimings.clear();
|
||||
this._topicTime = 0;
|
||||
this._onscreen = [];
|
||||
this._readOnscreen = [];
|
||||
this._readPosts = {};
|
||||
this._onscreen = null;
|
||||
this._readOnscreen = null;
|
||||
this._readPosts.clear();
|
||||
this._inProgress = false;
|
||||
}
|
||||
|
||||
|
@ -117,13 +116,12 @@ export default class ScreenTrack extends Service {
|
|||
}
|
||||
|
||||
consolidateTimings(timings, topicTime, topicId) {
|
||||
let foundIndex = this._consolidatedTimings.findIndex(
|
||||
const foundIndex = this._consolidatedTimings.findIndex(
|
||||
(elem) => elem.topicId === topicId
|
||||
);
|
||||
|
||||
if (foundIndex > -1) {
|
||||
let found = this._consolidatedTimings[foundIndex];
|
||||
|
||||
const found = this._consolidatedTimings[foundIndex];
|
||||
const lastIndex = this._consolidatedTimings.length - 1;
|
||||
|
||||
if (foundIndex !== lastIndex) {
|
||||
|
@ -132,12 +130,12 @@ export default class ScreenTrack extends Service {
|
|||
this._consolidatedTimings[lastIndex - 1] = last;
|
||||
}
|
||||
|
||||
const oldTimings = found.timings;
|
||||
Object.keys(oldTimings).forEach((id) => {
|
||||
Object.keys(found.timings).forEach((id) => {
|
||||
if (timings[id]) {
|
||||
oldTimings[id] += timings[id];
|
||||
found.timings[id] += timings[id];
|
||||
}
|
||||
});
|
||||
|
||||
found.topicTime += topicTime;
|
||||
found.timings = { ...timings, ...found.timings };
|
||||
} else {
|
||||
|
@ -158,7 +156,7 @@ export default class ScreenTrack extends Service {
|
|||
return getHighestReadCache(topicId);
|
||||
}
|
||||
|
||||
sendNextConsolidatedTiming() {
|
||||
async sendNextConsolidatedTiming() {
|
||||
if (this._consolidatedTimings.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -174,8 +172,6 @@ export default class ScreenTrack extends Service {
|
|||
return;
|
||||
}
|
||||
|
||||
this._ajaxFailures = this._ajaxFailures || 0;
|
||||
|
||||
const { timings, topicTime, topicId } = this._consolidatedTimings.pop();
|
||||
const data = {
|
||||
timings,
|
||||
|
@ -185,82 +181,76 @@ export default class ScreenTrack extends Service {
|
|||
|
||||
this._inProgress = true;
|
||||
|
||||
return ajax("/topics/timings", {
|
||||
data,
|
||||
type: "POST",
|
||||
headers: {
|
||||
"X-SILENCE-LOGGER": "true",
|
||||
"Discourse-Background": "true",
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
if (this.isDestroying || this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._ajaxFailures = 0;
|
||||
const topicController = this._topicController;
|
||||
if (topicController) {
|
||||
const postNumbers = Object.keys(timings).map((v) => parseInt(v, 10));
|
||||
topicController.readPosts(topicId, postNumbers);
|
||||
|
||||
const cachedHighestRead = this.highestReadFromCache(topicId);
|
||||
if (
|
||||
cachedHighestRead &&
|
||||
cachedHighestRead <= postNumbers.lastObject
|
||||
) {
|
||||
resetHighestReadCache(topicId);
|
||||
}
|
||||
}
|
||||
|
||||
this.appEvents.trigger("topic:timings-sent", data);
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.jqXHR && ALLOWED_AJAX_FAILURES.includes(e.jqXHR.status)) {
|
||||
const delay = AJAX_FAILURE_DELAYS[this._ajaxFailures];
|
||||
this._ajaxFailures += 1;
|
||||
|
||||
if (delay) {
|
||||
this._blockSendingToServerTill = Date.now() + delay;
|
||||
// we did not send to the server, got to re-queue it
|
||||
this.consolidateTimings(timings, topicTime, topicId);
|
||||
}
|
||||
}
|
||||
|
||||
if (window.console && window.console.warn && e.jqXHR) {
|
||||
window.console.warn(
|
||||
`Failed to update topic times for topic ${topicId} due to ${e.jqXHR.status} error`
|
||||
);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this._inProgress = false;
|
||||
this._lastFlush = 0;
|
||||
try {
|
||||
await ajax("/topics/timings", {
|
||||
data,
|
||||
type: "POST",
|
||||
headers: {
|
||||
"X-SILENCE-LOGGER": "true",
|
||||
"Discourse-Background": "true",
|
||||
},
|
||||
});
|
||||
|
||||
if (this.isDestroying || this.isDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._ajaxFailures = 0;
|
||||
if (this._topicController) {
|
||||
const postNumbers = Object.keys(timings).map((v) => parseInt(v, 10));
|
||||
this._topicController.readPosts(topicId, postNumbers);
|
||||
|
||||
const cachedHighestRead = this.highestReadFromCache(topicId);
|
||||
if (cachedHighestRead && cachedHighestRead <= postNumbers.lastObject) {
|
||||
resetHighestReadCache(topicId);
|
||||
}
|
||||
}
|
||||
|
||||
this.appEvents.trigger("topic:timings-sent", data);
|
||||
} catch (e) {
|
||||
if (e.jqXHR && ALLOWED_AJAX_FAILURES.includes(e.jqXHR.status)) {
|
||||
const delay = AJAX_FAILURE_DELAYS[this._ajaxFailures];
|
||||
this._ajaxFailures += 1;
|
||||
|
||||
if (delay) {
|
||||
this._blockSendingToServerTill = Date.now() + delay;
|
||||
// we did not send to the server, got to re-queue it
|
||||
this.consolidateTimings(timings, topicTime, topicId);
|
||||
}
|
||||
}
|
||||
|
||||
if (window.console && window.console.warn && e.jqXHR) {
|
||||
window.console.warn(
|
||||
`Failed to update topic times for topic ${topicId} due to ${e.jqXHR.status} error`
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
this._inProgress = false;
|
||||
this._lastFlush = 0;
|
||||
}
|
||||
}
|
||||
|
||||
flush() {
|
||||
const newTimings = {};
|
||||
const totalTimings = this._totalTimings;
|
||||
|
||||
const timings = this._timings;
|
||||
Object.keys(this._timings).forEach((postNumber) => {
|
||||
const time = timings[postNumber];
|
||||
totalTimings[postNumber] = totalTimings[postNumber] || 0;
|
||||
for (const [postNumber, time] of this._timings) {
|
||||
if (!this._totalTimings.has(postNumber)) {
|
||||
this._totalTimings.set(postNumber, 0);
|
||||
}
|
||||
|
||||
if (time > 0 && totalTimings[postNumber] < MAX_TRACKING_TIME) {
|
||||
totalTimings[postNumber] += time;
|
||||
const totalTiming = this._totalTimings.get(postNumber);
|
||||
if (time > 0 && totalTiming < MAX_TRACKING_TIME) {
|
||||
this._totalTimings.set(postNumber, totalTiming + time);
|
||||
newTimings[postNumber] = time;
|
||||
}
|
||||
timings[postNumber] = 0;
|
||||
});
|
||||
|
||||
this._timings.set(postNumber, 0);
|
||||
}
|
||||
|
||||
const topicId = parseInt(this._topicId, 10);
|
||||
let highestSeen = 0;
|
||||
|
||||
// Workaround to avoid ignored posts being "stuck unread"
|
||||
const controller = this._topicController;
|
||||
const stream = controller ? controller.get("model.postStream") : null;
|
||||
const stream = this._topicController?.get("model.postStream");
|
||||
if (
|
||||
this.currentUser && // Logged in
|
||||
this.currentUser.get("ignored_users.length") && // At least 1 user is ignored
|
||||
|
@ -281,10 +271,9 @@ export default class ScreenTrack extends Service {
|
|||
] = 1;
|
||||
}
|
||||
|
||||
const newTimingsKeys = Object.keys(newTimings);
|
||||
newTimingsKeys.forEach((postNumber) => {
|
||||
highestSeen = Math.max(highestSeen, parseInt(postNumber, 10));
|
||||
});
|
||||
const highestSeen = Object.keys(newTimings)
|
||||
.map((postNumber) => parseInt(postNumber, 10))
|
||||
.reduce((a, b) => Math.max(a, b), 0);
|
||||
|
||||
const highestSeenByTopic = this.session.get("highestSeenByTopic");
|
||||
if ((highestSeenByTopic[topicId] || 0) < highestSeen) {
|
||||
|
@ -293,7 +282,7 @@ export default class ScreenTrack extends Service {
|
|||
|
||||
this.topicTrackingState.updateSeen(topicId, highestSeen);
|
||||
|
||||
if (newTimingsKeys.length > 0) {
|
||||
if (highestSeen > 0) {
|
||||
if (this.currentUser) {
|
||||
this.consolidateTimings(newTimings, this._topicTime, topicId);
|
||||
|
||||
|
@ -301,15 +290,15 @@ export default class ScreenTrack extends Service {
|
|||
this.sendNextConsolidatedTiming();
|
||||
}
|
||||
} else if (this._anonCallback) {
|
||||
// Anonymous viewer - save to localStorage
|
||||
const storage = this.keyValueStore;
|
||||
|
||||
// Save total time
|
||||
const existingTime = storage.getInt("anon-topic-time");
|
||||
storage.setItem("anon-topic-time", existingTime + this._topicTime);
|
||||
const existingTime = this.keyValueStore.getInt("anon-topic-time");
|
||||
this.keyValueStore.setItem(
|
||||
"anon-topic-time",
|
||||
existingTime + this._topicTime
|
||||
);
|
||||
|
||||
// Save unique topic IDs up to a max
|
||||
let topicIds = storage.get("anon-topic-ids");
|
||||
let topicIds = this.keyValueStore.get("anon-topic-ids");
|
||||
if (topicIds) {
|
||||
topicIds = topicIds.split(",").map((e) => parseInt(e, 10));
|
||||
} else {
|
||||
|
@ -321,7 +310,7 @@ export default class ScreenTrack extends Service {
|
|||
topicIds.length < ANON_MAX_TOPIC_IDS
|
||||
) {
|
||||
topicIds.push(topicId);
|
||||
storage.setItem("anon-topic-ids", topicIds.join(","));
|
||||
this.keyValueStore.setItem("anon-topic-ids", topicIds.join(","));
|
||||
}
|
||||
|
||||
// Inform the observer
|
||||
|
@ -349,15 +338,13 @@ export default class ScreenTrack extends Service {
|
|||
this._lastFlush += diff;
|
||||
this._lastTick = now;
|
||||
|
||||
const totalTimings = this._totalTimings;
|
||||
const timings = this._timings;
|
||||
const nextFlush = this.siteSettings.flush_timings_secs * 1000;
|
||||
|
||||
const rush = Object.keys(timings).some((postNumber) => {
|
||||
const rush = [...this._timings.entries()].some(([postNumber, timing]) => {
|
||||
return (
|
||||
timings[postNumber] > 0 &&
|
||||
!totalTimings[postNumber] &&
|
||||
!this._readPosts[postNumber]
|
||||
timing > 0 &&
|
||||
!this._totalTimings.get(postNumber) &&
|
||||
!this._readPosts.has(postNumber)
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -373,13 +360,15 @@ export default class ScreenTrack extends Service {
|
|||
if (this.session.hasFocus) {
|
||||
this._topicTime += diff;
|
||||
|
||||
this._onscreen.forEach(
|
||||
(postNumber) =>
|
||||
(timings[postNumber] = (timings[postNumber] || 0) + diff)
|
||||
this._onscreen?.forEach((postNumber) =>
|
||||
this._timings.set(
|
||||
postNumber,
|
||||
(this._timings.get(postNumber) ?? 0) + diff
|
||||
)
|
||||
);
|
||||
|
||||
this._readOnscreen.forEach((postNumber) => {
|
||||
this._readPosts[postNumber] = true;
|
||||
this._readOnscreen?.forEach((postNumber) => {
|
||||
this._readPosts.add(postNumber);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,32 +22,24 @@ module("Unit | Service | screen-track", function (hooks) {
|
|||
|
||||
await tracker.sendNextConsolidatedTiming();
|
||||
|
||||
assert.equal(
|
||||
assert.strictEqual(
|
||||
tracker.highestReadFromCache(2),
|
||||
4,
|
||||
"caches highest read post number for second topic"
|
||||
);
|
||||
});
|
||||
|
||||
test("ScreenTrack has appEvents", async function (assert) {
|
||||
const tracker = this.owner.lookup("service:screen-track");
|
||||
assert.ok(tracker.appEvents);
|
||||
});
|
||||
|
||||
test("appEvent topic:timings-sent is triggered after posting consolidated timings", async function (assert) {
|
||||
assert.timeout(1000);
|
||||
|
||||
const tracker = this.owner.lookup("service:screen-track");
|
||||
const appEvents = this.owner.lookup("service:app-events");
|
||||
|
||||
const done = assert.async();
|
||||
|
||||
appEvents.on("topic:timings-sent", () => {
|
||||
assert.ok(true);
|
||||
done();
|
||||
assert.step("sent");
|
||||
});
|
||||
|
||||
tracker.consolidateTimings({ 1: 10, 2: 5 }, 10, 1);
|
||||
await tracker.sendNextConsolidatedTiming();
|
||||
|
||||
await assert.verifySteps(["sent"]);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user