ember-cli-new-version
Version:
A convention based update notification for Ember. With this addon, you can detect a new version and notify the user to refresh the page
188 lines (162 loc) • 4.82 kB
JavaScript
import { getOwner } from '@ember/application';
import { later } from '@ember/runloop';
import Service from '@ember/service';
import { waitFor } from '@ember/test-waiters';
import { tracked } from '@glimmer/tracking';
import Ember from 'ember';
import { task, timeout } from 'ember-concurrency';
import fetch from 'fetch';
let taskRunCounter = 0;
const ONE_MINUTE = 60000;
/**
* @typedef {object} Configuration
* @property {string} versionFileName
* @property {number} firstCheckInterval
* @property {number} updateInterval
* @property {boolean} enableInTests
* @property {boolean} enableInDev
* @property {number} maxCountInTesting
* @property {string} currentVersion
*/
export default class NewVersionService extends Service {
get _fastboot() {
let owner = getOwner(this);
return owner.lookup('service:fastboot');
}
get _config() {
return getOwner(this).resolveRegistration('config:environment');
}
get isDev() {
return this._config.environment === 'development';
}
/**
* @type Configuration
*/
get _newVersionConfig() {
return this._config.newVersion;
}
/**
* @type {string}
*/
get currentVersion() {
return this._newVersionConfig.currentVersion;
}
/**
* @type {string | undefined}
*/
latestVersion = undefined;
ignoredVersions = [];
/**
* Templates can use this attribute to show or hide a proposition to reload the page.
* This getter can be overriden to change the update strategy.
*
* By default, a new version is considered available when there is a difference
* between the local version and the remote version.
*
* @returns {boolean} true if a new version is available.
*/
get isNewVersionAvailable() {
return (
!this.ignoredVersions.includes(this.latestVersion) &&
this.latestVersion &&
this.currentVersion != this.latestVersion
);
}
get url() {
const versionFileName = this._newVersionConfig.versionFileName;
const baseUrl =
this._config.prepend || this._config.rootURL || this._config.baseURL;
return baseUrl + versionFileName;
}
get updateIntervalWithTesting() {
let enableInTests = this._newVersionConfig.enableInTests;
return !enableInTests && Ember.testing
? 0
: this._newVersionConfig.updateInterval;
}
constructor() {
super(...arguments);
if (this._fastboot?.isFastBoot) {
return;
}
// TODO: move the testing logic to a test version of the service
if (Ember.testing) {
taskRunCounter = 0;
}
if (
(!Ember.testing || this._newVersionConfig.enableInTests) &&
(!this.isDev || this._newVersionConfig.enableInDev)
) {
if (this._newVersionConfig.firstCheckInterval > 0) {
later(
this,
() => {
this.updateVersion.perform();
},
this._newVersionConfig.firstCheckInterval
);
} else {
this.updateVersion.perform();
}
}
}
*updateVersion() {
const url = this.url;
try {
yield fetch(url + '?_=' + Date.now())
.then((response) => {
if (!response.ok) throw new Error(response.statusText);
return response.text();
})
.then((res) => {
this.latestVersion = res ? res.trim() : undefined;
// Call kept for compatibility with older version of the lib
if (this.isNewVersionAvailable) {
this.onNewVersion(
this.latestVersion,
this.ignoredVersions[this.ignoredVersions.length - 1] ||
this.currentVersion
);
}
});
} catch (e) {
this.onError(e);
} finally {
let updateInterval = this.updateIntervalWithTesting;
if (updateInterval === null || updateInterval === undefined) {
updateInterval = ONE_MINUTE;
}
yield timeout(updateInterval);
if (
Ember.testing &&
++taskRunCounter > this._newVersionConfig.maxCountInTesting
) {
return;
}
if (Ember.testing && !this._newVersionConfig.enableInTests) {
return;
}
this.updateVersion.perform();
}
}
/**
* Tells NewVersionService to ignore the given version.
* If ignored, it won't trigger set `isNewVersionAvailable` to `true`.
* The list of ignored is kept in memory only: if the site is reloaded, the list is empty.
* @param {string} version
*/
ignoreVersion(version) {
this.ignoredVersions = [...this.ignoredVersions, version];
}
// eslint-disable-next-line no-unused-vars
onNewVersion(newVersion, currentVersion) {
// Kept for compatibility with older version of the lib
}
onError(error) {
if (!Ember.testing) {
console.log(error);
}
}
}