Skip to content

Commit

Permalink
Modernise FlashMessagesService (#409)
Browse files Browse the repository at this point in the history
* types(flash object): update temporary declaration file

* refactor(service): drop getWithDefault

* refactor(service): replace EmberArray with tracked variable

* refactor(service): replace computed macros with getters

* refactor(service): replace get() usage

* refactor(service): drop compouted use

* refactor(service): replace setProperties

* refactor(service): replace set()

* refactor(service): replace willDestory with registerDestructor

* fix: bad merge conflict resolution

* fix: copy queue before sorting

* fix: replace `at` with index access
  • Loading branch information
gilest authored Mar 13, 2024
1 parent 32e5eac commit e9d99f5
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 107 deletions.
7 changes: 4 additions & 3 deletions ember-cli-flash/src/flash/object.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,10 @@ export default class FlashObject {
}

_teardown() {
const queue = this.flashService?.queue;
if (queue) {
queue.removeObject(this);
if (this.flashService?.queue) {
this.flashService.queue = this.flashService.queue.filter(
(flash) => flash !== this,
);
}
destroy(this);
this.onDidDestroyMessage?.();
Expand Down
86 changes: 42 additions & 44 deletions ember-cli-flash/src/services/flash-messages.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,42 @@
/* eslint-disable ember/no-computed-properties-in-native-classes */
import { equal, sort, mapBy } from '@ember/object/computed';
import Service from '@ember/service';
import { typeOf, isNone } from '@ember/utils';
import { warn, assert } from '@ember/debug';
import { set, get, setProperties, computed } from '@ember/object';
import { classify } from '@ember/string';
import { A as emberArray } from '@ember/array';
import FlashMessage from '../flash/object';
import objectWithout from '../utils/object-without';
import { getOwner } from '@ember/application';
import flashMessageOptions from '../utils/flash-message-options';
import getWithDefault from '../utils/get-with-default';
import { associateDestroyableChild } from '@ember/destroyable';
import { tracked } from '@glimmer/tracking';
import { registerDestructor } from '@ember/destroyable';

export default class FlashMessagesService extends Service {
@(equal('queue.length', 0).readOnly())
isEmpty;
@tracked queue = [];

get arrangedQueue() {
return this.queue.slice().sort(function (a, b) {
if (a.priority < b.priority) {
return 1;
} else if (a.priority > b.priority) {
return -1;
}
return 0;
});
}

@(mapBy('queue', '_guid').readOnly())
_guids;
get isEmpty() {
return this.queue.length === 0;
}

@(sort('queue', function (a, b) {
if (a.priority < b.priority) {
return 1;
} else if (a.priority > b.priority) {
return -1;
}
return 0;
}).readOnly())
arrangedQueue;
get _guids() {
return this.queue.map((flash) => flash._guid);
}

constructor() {
super(...arguments);
this._setDefaults();
this.queue = emberArray();
}

willDestroy() {
super.willDestroy(...arguments);
this.clearMessages();
registerDestructor(this, this.clearMessages.bind(this));
}

add(options = {}) {
Expand All @@ -48,30 +46,28 @@ export default class FlashMessagesService extends Service {
}

clearMessages() {
const flashes = this.queue;

if (isNone(flashes)) {
if (isNone(this.queue)) {
return;
}

flashes.forEach((flash) => flash.destroyMessage());
flashes.clear();
this.queue.forEach((flash) => flash.destroyMessage());
this.queue = [];

return this;
}

registerTypes(types = emberArray()) {
registerTypes(types = []) {
types.forEach((type) => this._registerType(type));

return this;
}

peekFirst() {
return this.queue.firstObject;
return this.queue[0];
}

peekLast() {
return this.queue.lastObject;
return this.queue[this.queue.length - 1];
}

getFlashObject() {
Expand All @@ -92,16 +88,18 @@ export default class FlashMessagesService extends Service {
);

const flashService = this;
const allDefaults = getWithDefault(this, 'flashMessageDefaults', {});
const defaults = objectWithout(allDefaults, ['types', 'preventDuplicates']);
const defaults = objectWithout(this.flashMessageDefaults, [
'types',
'preventDuplicates',
]);

const flashMessageOptions = Object.assign({}, defaults, { flashService });

for (let key in options) {
const value = get(options, key);
const value = options[key];
const option = this._getOptionOrDefault(key, value);

set(flashMessageOptions, key, option);
flashMessageOptions[key] = option;
}

const message = new FlashMessage(flashMessageOptions);
Expand All @@ -110,8 +108,8 @@ export default class FlashMessagesService extends Service {
}

_getOptionOrDefault(key, value) {
const defaults = getWithDefault(this, 'flashMessageDefaults', {});
const defaultOption = get(defaults, key);
const defaults = this.flashMessageDefaults;
const defaultOption = defaults[key];

if (typeOf(value) === 'undefined') {
return defaultOption;
Expand All @@ -120,32 +118,31 @@ export default class FlashMessagesService extends Service {
return value;
}

@computed
get flashMessageDefaults() {
const config = getOwner(this).resolveRegistration('config:environment');
const overrides = getWithDefault(config, 'flashMessageDefaults', {});
const overrides = config.flashMessageDefaults ?? {};
return flashMessageOptions(overrides);
}

_setDefaults() {
const defaults = getWithDefault(this, 'flashMessageDefaults', {});
const defaults = this.flashMessageDefaults;

for (let key in defaults) {
const classifiedKey = classify(key);
const defaultKey = `default${classifiedKey}`;

set(this, defaultKey, defaults[key]);
this[defaultKey] = defaults[key];
}

this.registerTypes(getWithDefault(this, 'defaultTypes', emberArray()));
this.registerTypes(this.defaultTypes ?? []);
}

_registerType(type) {
assert('The flash type cannot be undefined', type);

this[type] = (message, options = {}) => {
const flashMessageOptions = Object.assign({}, options);
setProperties(flashMessageOptions, { message, type });
Object.assign(flashMessageOptions, { message, type });

return this.add(flashMessageOptions);
};
Expand Down Expand Up @@ -178,6 +175,7 @@ export default class FlashMessagesService extends Service {
}
}

return this.queue.pushObject(flashInstance);
this.queue = [...this.queue, flashInstance];
return this.queue;
}
}
14 changes: 0 additions & 14 deletions ember-cli-flash/src/utils/get-with-default.js

This file was deleted.

18 changes: 9 additions & 9 deletions test-app/tests/unit/services/flash-messages-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ module('FlashMessagesService', function (hooks) {

hooks.afterEach(function () {
run(() => {
this.service.queue.clear();
this.service.queue = [];
this.service.destroy();
});

Expand Down Expand Up @@ -233,7 +233,7 @@ module('FlashMessagesService', function (hooks) {
customProperty: 'ohai',
});

assert.strictEqual(this.service.queue.firstObject.customProperty, 'ohai');
assert.strictEqual(this.service.queue.at(0).customProperty, 'ohai');
});

test('when preventDuplicates is `true`, setting a message is required', function (assert) {
Expand Down Expand Up @@ -268,7 +268,7 @@ module('FlashMessagesService', function (hooks) {
set(this, 'service.defaultPreventDuplicates', false);
const expectedResult = emberArray(['foo', 'foo', 'bar']);
expectedResult.forEach((message) => this.service.success(message));
const result = this.service.queue.mapBy('message');
const result = this.service.queue.map((flash) => flash.message);

assert.deepEqual(
result,
Expand All @@ -287,7 +287,7 @@ module('FlashMessagesService', function (hooks) {
const messages = emberArray(['foo', 'foo', 'bar']);
const expectedResult = messages.uniq();
messages.forEach((message) => this.service.success(message));
const result = this.service.queue.mapBy('message');
const result = this.service.queue.map((flash) => flash.message);

assert.deepEqual(
result,
Expand All @@ -308,7 +308,7 @@ module('FlashMessagesService', function (hooks) {
messages.forEach((message) =>
this.service.success(message, { preventDuplicates: true })
);
const result = this.service.queue.mapBy('message');
const result = this.service.queue.map((flash) => flash.message);

assert.deepEqual(
result,
Expand All @@ -329,7 +329,7 @@ module('FlashMessagesService', function (hooks) {
messages.forEach((message) =>
this.service.success(message, { preventDuplicates: false })
);
const result = this.service.queue.mapBy('message');
const result = this.service.queue.map((flash) => flash.message);

assert.deepEqual(
result,
Expand All @@ -352,7 +352,7 @@ module('FlashMessagesService', function (hooks) {
this.service.success('foo');
this.service.success('baz', { preventDuplicates: true });

const result = this.service.queue.mapBy('message');
const result = this.service.queue.map((flash) => flash.message);

assert.deepEqual(
result,
Expand All @@ -374,12 +374,12 @@ module('FlashMessagesService', function (hooks) {
.meow('bar');

assert.strictEqual(
this.service.queue.firstObject.message,
this.service.queue.at(0).message,
'foo',
'should support chaining'
);
assert.strictEqual(
this.service.queue.lastObject.message,
this.service.queue.at(-1).message,
'bar',
'should support chaining'
);
Expand Down
37 changes: 0 additions & 37 deletions test-app/tests/unit/utils/get-with-default-test.js

This file was deleted.

0 comments on commit e9d99f5

Please sign in to comment.