Files
threshold-ui/addons/core/addon/services/confirm.js
T

157 lines
4.2 KiB
JavaScript

import Service from '@ember/service';
import { filterBy } from '@ember/object/computed';
import { A } from '@ember/array';
import { tracked } from '@glimmer/tracking';
import { defer, resolve } from 'rsvp';
import { getOwner } from '@ember/application';
/**
* A simple service that emits Confirmation instances and
* exposes a `pending` property to discover pending
* confirmations.
* @augments Service
*
* @example
* const confirmation = confirmationService.confirm('Text', { type: 'discard' });
*/
export default class ConfirmationsService extends Service {
// =attributes
#enabled;
/**
* When the confirm service is disabled, it always returns resolving promises.
* By default the service is enabled but it is useful to disable it for test
* purposes.
* @type {boolean}
*/
get enabled() {
const config = getOwner(this).resolveRegistration('config:environment');
return this.#enabled ?? config.enableConfirmService ?? true;
}
set enabled(value) {
this.#enabled = value;
}
/**
* @type {Array}
*/
confirmations = A();
/**
* An array of pending confirmations (those where `done === false`).
* @type {Array}
*/
@filterBy('confirmations', 'done', false) pending;
// =methods
/**
* When the service is enabled, returns a promise that resolves if the user
* accepts the confirmation, otherwise rejects. If the service is disabled,
* this method always returns a resolving promise.
* @param {string} text
* @param {object} options
* @return {Promise}
*/
confirm(text, options) {
if (this.enabled) {
const confirmation = new Confirmation(text, options);
this.confirmations.addObject(confirmation);
return confirmation;
} else {
return resolve();
}
}
}
/**
* A promise-like object that is either "confirmed" or "dismissed", represented
* internally by a promise that resolves or rejects. Confirmations may be used
* as promises via `then`, `catch`, and `finally`.
*
* Not intended for instantiation directly. Use the confirmation service
* to create confirmation instances.
*
* @example
* const confirmation = new Confirmation('Text', { type: 'discard' });
* confirmation.confirm();
* // confirmation.done now equals true
* confirmation
* .then(() => { }) // do something
* .catch(() => { }) // not called, since the confirmation is confirmed
* .finally(() => { }); // do something
*
* @example
* const confirmation = new Confirmation('Text', { type: 'discard' });
* confirmation.dismiss();
* // confirmation.done now equals true
* confirmation
* .then(() => { }) // not called, since the confirmation is dismissed
* .catch(() => { }) // do something
* .finally(() => { }); // do something
*/
class Confirmation {
// =properties
/**
* Indicates whether the confirmation has been confirmed or dismissed.
* Automatically set to `true` after `confirm` or `dismiss` are called.
* @type {Boolean}
*/
@tracked done = false;
#deferred = null;
/**
* Sets the type field to the specified type and sets up a defer.
* @param {String} type An optional type annotation for convenience.
* Has no effect on confirmation.
*/
constructor(text, options = {}) {
this.text = text;
this.options = options;
this.#deferred = defer();
}
/**
* Sets the confirmation to done and resolves the defer.
*/
confirm() {
this.done = true;
this.#deferred.resolve();
}
/**
* Sets the confirmation to done and rejects the defer.
*/
dismiss() {
this.done = true;
this.#deferred.reject();
}
/**
* Delegates to the internal defer promise, allowing the
* confirmation to be treated like a promise.
*/
then() {
return this.#deferred.promise.then(...arguments);
}
/**
* Delegates to the internal defer promise, allowing the
* confirmation to be treated like a promise.
*/
catch() {
return this.#deferred.promise.catch(...arguments);
}
/**
* Delegates to the internal defer promise, allowing the
* confirmation to be treated like a promise.
*/
finally() {
return this.#deferred.promise.finally(...arguments);
}
}