Skip to content

Commit

Permalink
Broadcast queue job info to other tabs
Browse files Browse the repository at this point in the history
Resolves #13990
  • Loading branch information
brandonkelly committed Dec 10, 2023
1 parent 81f6766 commit cdeac24
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 31 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- It’s no longer possible to dismiss asset conflict resolution modals by pressing <kbd>Esc</kbd> or clicking outside of the modal. ([#14002](https://github.com/craftcms/cms/issues/14002))
- Improved performance for sites with lots of custom fields in non-global contexts. ([#13992](https://github.com/craftcms/cms/issues/13992))
- Queue job info is now broadcasted to other browser tabs opened to the same control panel. ([#13990](https://github.com/craftcms/cms/issues/13990))
- Added `craft\db\Connection::onAfterTransaction()`.
- Added `craft\fieldlayoutelements\TextField::$inputType`. ([#13988](https://github.com/craftcms/cms/issues/13988))
- Deprecated `craft\fieldlayoutelements\TextField::$type`. `$inputType` should be used instead. ([#13988](https://github.com/craftcms/cms/issues/13988))
Expand Down
2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/cp.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/cp.js.map

Large diffs are not rendered by default.

114 changes: 85 additions & 29 deletions src/web/assets/cp/src/js/CP.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ Craft.CP = Garnish.Base.extend(
displayedJobInfo: null,
displayedJobInfoUnchanged: 1,
trackJobProgressTimeout: null,
trackingJobProgress: false,
jobProgressCancelToken: null,
jobProgressIcon: null,

checkingForUpdates: false,
Expand Down Expand Up @@ -1262,49 +1264,90 @@ Craft.CP = Garnish.Base.extend(
},

trackJobProgress: function (delay, force) {
if (force && this.trackJobProgressTimeout) {
clearTimeout(this.trackJobProgressTimeout);
this.trackJobProgressTimeout = null;
}

// Ignore if we're already tracking jobs, or the queue is disabled
if (this.trackJobProgressTimeout || !this.enableQueue) {
if ((this.trackJobProgressTimeout && !force) || !this.enableQueue) {
return;
}

if (delay === true) {
this.cancelJobTracking();

if (delay) {
// Determine the delay based on how long the displayed job info has remained unchanged
var timeout = Math.min(60000, this.displayedJobInfoUnchanged * 500);
if (delay === true) {
delay = this.getNextJobDelay();
}
this.trackJobProgressTimeout = setTimeout(
this._trackJobProgressInternal.bind(this),
timeout
delay
);
} else {
this._trackJobProgressInternal();
}
},

getNextJobDelay: function () {
return Math.min(60000, this.displayedJobInfoUnchanged * 500);
},

_trackJobProgressInternal: function () {
Craft.queue.push(
() =>
new Promise((resolve, reject) => {
Craft.sendActionRequest(
'POST',
'queue/get-job-info?limit=50&dontExtendSession=1'
)
.then(({data}) => {
this.trackJobProgressTimeout = null;
this.totalJobs = data.total;
this.setJobInfo(data.jobs);
if (this.jobInfo.length) {
// Check again after a delay
this.trackJobProgress(true);
}
resolve();
})
.catch(reject);
})
);
this.trackingJobProgress = true;

Craft.queue.push(async () => {
// has this been cancelled?
if (!this.trackingJobProgress) {
return;
}

// Tell other browser windows to stop tracking job progress
if (Craft.broadcaster) {
Craft.broadcaster.postMessage({
event: 'beforeTrackJobProgress',
});
}

this.jobProgressCancelToken = axios.CancelToken.source();

let data;
try {
const response = await Craft.sendActionRequest(
'POST',
'queue/get-job-info?limit=50&dontExtendSession=1',
{
cancelToken: this.jobProgressCancelToken.token,
}
);
data = response.data;
} catch (e) {
// only throw if we weren't expecting this
if (this.trackingJobProgress) {
throw e;
}
} finally {
this.trackingJobProgress = false;
this.trackJobProgressTimeout = null;
this.jobProgressCancelToken = null;
}

this.setJobData(data);

if (this.jobInfo.length) {
// Check again after a delay
this.trackJobProgress(true);
}

// Notify the other browser tabs about the jobs
if (Craft.broadcaster) {
Craft.broadcaster.postMessage({
event: 'trackJobProgress',
jobData: data,
});
}
});
},

setJobData: function (data) {
this.totalJobs = data.total;
this.setJobInfo(data.jobs);
},

setJobInfo: function (jobInfo) {
Expand Down Expand Up @@ -1339,6 +1382,19 @@ Craft.CP = Garnish.Base.extend(
this.trigger('setJobInfo');
},

cancelJobTracking: function () {
this.trackingJobProgress = false;

if (this.trackJobProgressTimeout) {
clearTimeout(this.trackJobProgressTimeout);
this.trackJobProgressTimeout = null;
}

if (this.jobProgressCancelToken) {
this.jobProgressCancelToken.cancel();
}
},

/**
* Returns info for the job that should be displayed in the control panel sidebar
*/
Expand Down
20 changes: 20 additions & 0 deletions src/web/assets/cp/src/js/Craft.js
Original file line number Diff line number Diff line change
Expand Up @@ -2444,6 +2444,26 @@ if (typeof BroadcastChannel !== 'undefined') {
Craft.broadcaster = new BroadcastChannel(channelName);
Craft.messageReceiver = new BroadcastChannel(channelName);

Craft.broadcaster.addEventListener('message', (ev) => {
switch (ev.data.event) {
case 'beforeTrackJobProgress':
Craft.cp.cancelJobTracking();
break;

case 'trackJobProgress':
Craft.cp.setJobData(ev.data.jobData);

if (Craft.cp.jobInfo.length) {
// Check again after a longer delay than usual,
// as it looks like another browser tab is driving for now
const delay = Craft.cp.getNextJobDelay() + 1000;
Craft.cp.trackJobProgress(delay);
}

break;
}
});

Craft.messageReceiver.addEventListener('message', (ev) => {
if (ev.data.event === 'saveElement') {
// Are there any instances of the same element on the page?
Expand Down

0 comments on commit cdeac24

Please sign in to comment.