feat: 🎸 implement initial confirm service and decorator
for super easy application of confirmation dialogs on existing action
This commit is contained in:
6 files changed
+98
-2
No files matched your search
@@ -2,6 +2,7 @@ import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import loading from 'ember-loading/decorator';
|
||||
import { confirm } from '../../../utilities/confirm';
|
||||
|
||||
export default class ScopesScopeProjectsRoute extends Route {
|
||||
// =services
|
||||
@@ -9,6 +10,7 @@ export default class ScopesScopeProjectsRoute extends Route {
|
||||
@service intl;
|
||||
@service notify;
|
||||
@service session;
|
||||
@service confirm;
|
||||
|
||||
// =methods
|
||||
|
||||
@@ -84,12 +86,13 @@ export default class ScopesScopeProjectsRoute extends Route {
|
||||
*/
|
||||
@action
|
||||
@loading
|
||||
@confirm('questions.delete-confirm')
|
||||
async delete(project) {
|
||||
try {
|
||||
await project.destroyRecord();
|
||||
await this.replaceWith('scopes.scope.projects');
|
||||
this.refresh();
|
||||
this.notify.success(this.intl.t('notifications.delete-success'));
|
||||
this.transitionTo('scopes.scope.projects');
|
||||
} catch (error) {
|
||||
// TODO: replace with translated strings
|
||||
this.notify.error(error.message, { closeAfter: null });
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import Service from '@ember/service';
|
||||
import { getOwner } from '@ember/application';
|
||||
import { bind } from '@ember/runloop';
|
||||
import { Promise, resolve } from 'rsvp';
|
||||
import config from '../config/environment';
|
||||
|
||||
export default class ConfirmService extends Service {
|
||||
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* When the confirm service is disabled, it always returns resolving promises.
|
||||
* @type {boolean}
|
||||
*/
|
||||
enabled = config.enableConfirmService;
|
||||
|
||||
/**
|
||||
* The window's confirm function, bound to the window scope.
|
||||
* @type {Function}
|
||||
*/
|
||||
get confirmFn() {
|
||||
// The Ember way of accessing globals...
|
||||
const document =
|
||||
getOwner(this).lookup('service:-document').documentElement;
|
||||
// defaultView === window, but without using globals directly
|
||||
const windowRef = document.parentNode.defaultView;
|
||||
const { confirm } = windowRef;
|
||||
return bind(windowRef, confirm);
|
||||
}
|
||||
|
||||
// =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
|
||||
* @return {Promise}
|
||||
*/
|
||||
confirm(text) {
|
||||
if (this.enabled) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.confirmFn(text)) {
|
||||
return resolve();
|
||||
} else {
|
||||
return reject();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { getOwner } from '@ember/application';
|
||||
|
||||
/**
|
||||
* Decorates a method whereby calls to the method will first request
|
||||
* confirmation via the confirmation service. If the user accepts, the
|
||||
* original method is executed. If the user denies, it is not.
|
||||
*/
|
||||
export function confirm(confirmationTextKey) {
|
||||
return function (_target, _propertyKey, desc) {
|
||||
const method = desc.value;
|
||||
desc.value = function () {
|
||||
const owner = getOwner(this);
|
||||
const confirmService = owner.lookup('service:confirm');
|
||||
const intl = owner.lookup('service:intl');
|
||||
const text = intl.t(confirmationTextKey);
|
||||
return confirmService.confirm(text)
|
||||
// If user confirmed, execute the decorated method
|
||||
.then(() => method.apply(this, arguments))
|
||||
// If the user denied, gracefully handle promise rejection with no-op
|
||||
.catch(() => {});
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ module.exports = function (environment) {
|
||||
appName: APP_NAME,
|
||||
|
||||
notifyTimeout: 4000,
|
||||
enableConfirmService: true,
|
||||
|
||||
contentSecurityPolicyHeader: 'Content-Security-Policy',
|
||||
contentSecurityPolicyMeta: true,
|
||||
@@ -98,6 +99,7 @@ module.exports = function (environment) {
|
||||
|
||||
// Notification timeout should be 0 for fast tests
|
||||
ENV.notifyTimeout = 0;
|
||||
ENV.enableConfirmService = false;
|
||||
}
|
||||
|
||||
if (environment === 'production') {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Service | confirm', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
let service = this.owner.lookup('service:confirm');
|
||||
assert.ok(service);
|
||||
});
|
||||
});
|
||||
@@ -17,4 +17,7 @@ titles:
|
||||
global: Global
|
||||
actions: Actions
|
||||
descriptions:
|
||||
empty-set: There are no items to display yet. You may be able to add items or try back later.
|
||||
empty-set: There are no items to display yet. You may be able to add items or try back later.
|
||||
questions:
|
||||
delete-confirm: Are you sure you want to delete this resource?
|
||||
remove-confirm: Are you sure you want to remove this association?
|
||||
Reference in new issue
Block a user