gethue
Version:
Hue is an Open source SQL Query Editor for Databases/Warehouses
120 lines (107 loc) • 3.57 kB
text/typescript
// Licensed to Cloudera, Inc. under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. Cloudera, Inc. licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { onBeforeUnmount, Ref, watch } from 'vue';
import KnockoutObservable from '@types/knockout';
import huePubSub from '../../utils/huePubSub';
export interface Disposable {
dispose(): void;
}
const valueOrNull = <T>(val?: T): T | null => (typeof val === 'undefined' ? null : val);
export default class SubscriptionTracker {
disposed = false;
disposals: (() => void)[] = [];
constructor() {
onBeforeUnmount(this.dispose.bind(this));
}
addDisposable(disposable: Disposable): void {
this.disposals.push(disposable.dispose.bind(disposable));
}
addEventListener<K extends keyof HTMLElementEventMap>(
element: HTMLElement | Document,
type: K,
listener: (ev: HTMLElementEventMap[K]) => unknown
): void {
element.addEventListener(type, listener as EventListenerOrEventListenerObject);
this.disposals.push(() => {
element.removeEventListener(type, listener as EventListenerOrEventListenerObject);
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
subscribe<T = any>(
subscribable: string | KnockoutSubscribable<T>,
callback: (event: T) => void
): void {
if (typeof subscribable === 'string') {
const pubSub = huePubSub.subscribe(subscribable, callback);
this.disposals.push(() => {
pubSub.remove();
});
} else if (subscribable.subscribe) {
const sub = subscribable.subscribe(callback);
this.disposals.push(() => {
sub.dispose();
});
}
}
/**
* Helper function to link knockout observable with Vue refs. Note that it will update the Vue ref on changes to
* the knockout observable but not the other way around.
*/
trackObservable<T>(
observableRef: Ref<KnockoutObservable<T | undefined | null>>,
vueRef: Ref<T | null>
): void {
const whenDef = new Promise<KnockoutObservable<T | null | undefined>>(resolve => {
const stop = watch(
observableRef,
observable => {
if (observable) {
resolve(observable);
stop();
}
},
{ immediate: true }
);
});
whenDef.then(observable => {
if (this.disposed) {
return;
}
vueRef.value = valueOrNull(observable());
this.subscribe<T | undefined | null>(observable, (newVal?: T | null) => {
vueRef.value = valueOrNull(newVal);
});
});
}
trackTimeout(timeout: number): void {
this.disposals.push(() => {
window.clearTimeout(timeout);
});
}
dispose(): void {
this.disposed = true;
while (this.disposals.length) {
try {
const disposeFn = this.disposals.pop();
if (disposeFn) {
disposeFn();
}
} catch (err) {
console.warn(err);
}
}
}
}