Skip to content

Instantly share code, notes, and snippets.

@josemarluedke
Created August 23, 2020 01:07
Show Gist options
  • Save josemarluedke/2e60b6d7303d4eb4fdfbba157e11dc34 to your computer and use it in GitHub Desktop.
Save josemarluedke/2e60b6d7303d4eb4fdfbba157e11dc34 to your computer and use it in GitHub Desktop.
import ApolloService from 'ember-apollo-client/services/apollo';
import { tracked } from '@glimmer/tracking';
import { resource } from 'ember-usable';
import { inject as service } from '@ember/service';
import { getOwner } from '@ember/application';
import {
ApolloQueryResult,
NetworkStatus,
OperationVariables,
DocumentNode,
WatchQueryOptions,
ObservableQuery,
ApolloError
} from '@apollo/client/core';
import Fastboot from 'ember-cli-fastboot/services/fastboot';
interface BaseQueryOptions<TVariables = OperationVariables>
extends Omit<WatchQueryOptions<TVariables>, 'query'> {
ssr?: boolean;
}
type WatchQueryResourceArgs<TVariables = OperationVariables> = [
DocumentNode,
BaseQueryOptions<TVariables>?
];
class WatchQueryResource<TData = {}> {
@service apollo!: ApolloService;
@tracked isLoading = true;
@tracked observable?: ObservableQuery<TData>;
@tracked error?: ApolloError;
subscription?: ZenObservable.Subscription;
@tracked result: ApolloQueryResult<TData> = {
loading: this.isLoading,
networkStatus: NetworkStatus.loading
};
manager = null;
get state(): ApolloQueryResult<TData> {
const loading = this.isLoading ? this.isLoading : this.result.loading;
return {
...this.result,
error: this.result.error || this.error,
loading
};
}
async setup(
query: WatchQueryResourceArgs[0],
options: WatchQueryResourceArgs[1]
): Promise<void> {
console.log('SETUP');
this.isLoading = true;
const fastboot = this.getFastboot();
if (fastboot && fastboot.isFastBoot && options && options.ssr === false) {
return;
}
const [promise, resolve] = this.createPromise(options);
const observable = this.apollo.client.watchQuery({
query,
...(options || {})
});
this.observable = observable;
this.subscription = observable.subscribe({
next: (result) => {
this.updateResult(result);
if (typeof resolve === 'function') {
resolve(this.state);
}
},
error: (e) => {
this.isLoading = false;
this.error = e;
}
});
if (promise && fastboot) {
fastboot.deferRendering(promise);
}
}
updateResult(result: ApolloQueryResult<TData>): void {
console.log('UPDATE RESULT', result);
this.error = undefined;
this.result = result;
this.isLoading = false;
}
update(...args: WatchQueryResourceArgs): void {
console.log('UPDATE RESOURCE', ...args);
this.teardown();
this.setup(...args);
}
teardown(): void {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
private createPromise(
options: { ssr?: boolean } | undefined
): [Promise<unknown> | undefined, (val?: unknown) => void | undefined] {
let promise: Promise<unknown> | undefined;
let resolvePromise: (val?: unknown) => void | undefined;
const fastboot = this.getFastboot();
if (fastboot && fastboot.isFastBoot && options && options.ssr !== false) {
promise = new Promise((resolve) => {
resolvePromise = resolve;
});
}
return [promise, resolvePromise!]; //eslint-disable-line
}
private getFastboot(): Fastboot | undefined {
return getOwner(this)?.lookup('service:fastboot') as Fastboot;
}
}
export function watchQuery<TData = {}, TVariables = OperationVariables>(
...args: WatchQueryResourceArgs<TVariables>
): WatchQueryResource<TData>['state'] {
return resource<
WatchQueryResource<TData>,
WatchQueryResource<TData>['state'],
WatchQueryResourceArgs<TVariables>
>(WatchQueryResource)(...args);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment