1b9586b65e
This commit addresses some left over admin partition FIXMEs 1. Adds Partition correctly to Service Instances 2. Converts non-important 'we can do this later' FIXMEs to TODOs 3. Removes some FIXMEs that I've double checked and addressed. Most of the remaining FIXMEs I'm waiting on responses to questions from the consul core folks for. I'll address those in a separate PR.
207 lines
5.4 KiB
JavaScript
207 lines
5.4 KiB
JavaScript
import Component from '@glimmer/component';
|
|
import { inject as service } from '@ember/service';
|
|
import { tracked } from '@glimmer/tracking';
|
|
import { action, get } from '@ember/object';
|
|
import { schedule } from '@ember/runloop';
|
|
import { runInDebug } from '@ember/debug';
|
|
|
|
/**
|
|
* Utility function to set, but actually replace if we should replace
|
|
* then call a function on the thing to be replaced (usually a clean up function)
|
|
*
|
|
* @param obj - target object with the property to replace
|
|
* @param prop {string} - property to replace on the target object
|
|
* @param value - value to use for replacement
|
|
* @param destroy {(prev: any, value: any) => any} - teardown function
|
|
*/
|
|
const replace = function(
|
|
obj,
|
|
prop,
|
|
value,
|
|
destroy = (prev = null, value) => (typeof prev === 'function' ? prev() : null)
|
|
) {
|
|
const prev = obj[prop];
|
|
if (prev !== value) {
|
|
destroy(prev, value);
|
|
}
|
|
return (obj[prop] = value);
|
|
};
|
|
|
|
const noop = () => {};
|
|
const optional = op => (typeof op === 'function' ? op : noop);
|
|
|
|
// possible values for @loading=""
|
|
const LOADING = ['eager', 'lazy'];
|
|
|
|
export default class DataSource extends Component {
|
|
@service('data-source/service') dataSource;
|
|
@service('dom') dom;
|
|
@service('logger') logger;
|
|
|
|
@tracked isIntersecting = false;
|
|
@tracked data;
|
|
@tracked error;
|
|
|
|
constructor(owner, args) {
|
|
super(...arguments);
|
|
this._listeners = this.dom.listeners();
|
|
this._lazyListeners = this.dom.listeners();
|
|
}
|
|
|
|
get loading() {
|
|
return LOADING.includes(this.args.loading) ? this.args.loading : LOADING[0];
|
|
}
|
|
|
|
get disabled() {
|
|
return typeof this.args.disabled !== 'undefined' ? this.args.disabled : false;
|
|
}
|
|
|
|
onchange(e) {
|
|
this.error = undefined;
|
|
this.data = e.data;
|
|
optional(this.args.onchange)(e);
|
|
}
|
|
|
|
onerror(e) {
|
|
this.error = e.error || e;
|
|
optional(this.args.onerror)(e);
|
|
}
|
|
|
|
@action
|
|
connect($el) {
|
|
// $el is only a DOM node when loading = lazy
|
|
// otherwise its an array from the did-insert-helper
|
|
if (!Array.isArray($el)) {
|
|
this._lazyListeners.add(
|
|
this.dom.isInViewport($el, inViewport => {
|
|
this.isIntersecting = inViewport;
|
|
if (!this.isIntersecting) {
|
|
this.close();
|
|
} else {
|
|
this.open();
|
|
}
|
|
})
|
|
);
|
|
} else {
|
|
this._lazyListeners.remove();
|
|
this.open();
|
|
}
|
|
}
|
|
|
|
@action
|
|
disconnect() {
|
|
// TODO: Should we be doing this here? Fairly sure we should be so if this
|
|
// TODO gets old enough (6 months/ 1 year or so) feel free to remove
|
|
if (
|
|
typeof this.data !== 'undefined' &&
|
|
typeof this.data.length === 'undefined' &&
|
|
typeof this.data.rollbackAttributes === 'function'
|
|
) {
|
|
this.data.rollbackAttributes();
|
|
}
|
|
this.close();
|
|
this._listeners.remove();
|
|
this._lazyListeners.remove();
|
|
}
|
|
|
|
@action
|
|
attributeChanged([name, value]) {
|
|
switch (name) {
|
|
case 'src':
|
|
if (this.loading === 'eager' || this.isIntersecting) {
|
|
this.open();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// keep this argumentless
|
|
@action
|
|
open() {
|
|
const src = this.args.src;
|
|
// get a new source and replace the old one, cleaning up as we go
|
|
const source = replace(
|
|
this,
|
|
'source',
|
|
this.dataSource.open(src, this, this.open),
|
|
(prev, source) => {
|
|
// Makes sure any previous source (if different) is ALWAYS closed
|
|
this.dataSource.close(prev, this);
|
|
}
|
|
);
|
|
const error = err => {
|
|
try {
|
|
const error = get(err, 'error.errors.firstObject') || {};
|
|
if (get(error, 'status') !== '429') {
|
|
this.onerror(err);
|
|
}
|
|
this.logger.execute(err);
|
|
} catch (err) {
|
|
this.logger.execute(err);
|
|
}
|
|
};
|
|
// set up the listeners (which auto cleanup on component destruction)
|
|
const remove = this._listeners.add(this.source, {
|
|
message: e => {
|
|
try {
|
|
this.onchange(e);
|
|
} catch (err) {
|
|
error(err);
|
|
}
|
|
},
|
|
error: e => {
|
|
error(e);
|
|
},
|
|
});
|
|
replace(this, '_remove', remove);
|
|
// dispatch the current data of the source if we have any
|
|
if (typeof source.getCurrentEvent === 'function') {
|
|
const currentEvent = source.getCurrentEvent();
|
|
if (currentEvent) {
|
|
let method;
|
|
if (typeof currentEvent.error !== 'undefined') {
|
|
method = 'onerror';
|
|
this.error = currentEvent.error;
|
|
} else {
|
|
this.error = undefined;
|
|
this.data = currentEvent.data;
|
|
method = 'onchange';
|
|
}
|
|
|
|
// avoid the re-render error
|
|
schedule('afterRender', () => {
|
|
try {
|
|
this[method](currentEvent);
|
|
} catch (err) {
|
|
error(err);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
@action
|
|
async invalidate() {
|
|
this.source.readyState = 2;
|
|
this.disconnect();
|
|
schedule('afterRender', () => {
|
|
// TODO: Support lazy data-sources by keeping a reference to $el
|
|
runInDebug(_ =>
|
|
console.error(
|
|
`Invalidation is only supported for non-lazy data sources. If you want to use this you should fixup support for lazy data sources`
|
|
)
|
|
);
|
|
this.connect([]);
|
|
});
|
|
}
|
|
|
|
// keep this argumentless
|
|
@action
|
|
close() {
|
|
if (typeof this.source !== 'undefined') {
|
|
this.dataSource.close(this.source, this);
|
|
replace(this, '_remove', undefined);
|
|
this.source = undefined;
|
|
}
|
|
}
|
|
}
|