@vyer-technologies/relay-offline
Version:
Forked from @wora Relay Offline Capabilities
160 lines • 6.44 kB
JavaScript
import { Environment as RelayEnvironment, Observable as RelayObservable, } from 'relay-runtime';
import RelayDefaultHandlerProvider from 'relay-runtime/lib/handlers/RelayDefaultHandlerProvider';
import { OfflineFirst } from '@vyer-technologies/offline-first';
import resolveImmediate from 'relay-runtime/lib/util/resolveImmediate';
import { v4 as uuid } from 'uuid';
import warning from 'fbjs/lib/warning';
import { PublishQueue } from '@vyer-technologies/relay-store';
export class Environment extends RelayEnvironment {
_rehydrated = typeof window === 'undefined';
_relayStoreOffline;
promisesRestore;
constructor(config, persistOfflineOptions = {}) {
super(config);
const persistOptionsStoreOffline = {
prefix: 'relay-offline',
serialize: true,
...persistOfflineOptions,
};
this._publishQueue = new PublishQueue(config.store, config.handlerProvider ?? RelayDefaultHandlerProvider, this._getDataID, this._missingFieldHandlers);
this._relayStoreOffline = new OfflineFirst(persistOptionsStoreOffline);
this.setOfflineOptions();
if (this._rehydrated) {
this.promisesRestore = Promise.resolve(true);
}
this._store.setCheckGC(() => this.isOnline());
}
setOfflineOptions(offlineOptions = {}) {
const { onComplete, onDiscard, network, ...others } = offlineOptions;
const options = {
execute: (offlineRecord) => this.executeStoreOffline(network, offlineRecord),
...others,
};
if (onComplete) {
options.onComplete = (options) => {
const { offlineRecord, response } = options;
const { request: { payload: { operation }, }, id, } = offlineRecord;
const snapshot = this.lookup(operation.fragment);
return onComplete({ id, offlinePayload: offlineRecord, snapshot: snapshot.data, response });
};
}
if (onDiscard) {
options.onDiscard = (options) => {
const { offlineRecord, error } = options;
const { id } = offlineRecord;
return onDiscard({ id, offlinePayload: offlineRecord, error });
};
}
this._relayStoreOffline.setOfflineOptions(options);
}
executeStoreOffline(network = this.getNetwork(), offline) {
const { request: { payload }, } = offline;
const { operation, uploadables, cacheConfig } = payload;
const request = operation.request ? operation.request : operation;
const netCacheConfig = request.cacheConfig || cacheConfig || { force: true };
netCacheConfig.metadata = {
...netCacheConfig.metadata,
offline,
};
return network.execute(request.node.params, request.variables, netCacheConfig, uploadables).toPromise();
}
clearCache() {
return Promise.all([this._store.purge()]).then((_result) => {
return true;
});
}
dispose() {
this.getStoreOffline().dispose();
}
hydrate() {
if (!this.promisesRestore) {
this.promisesRestore = Promise.all([this.getStoreOffline().hydrate(), this._store.hydrate()])
.then((_result) => {
this._rehydrated = true;
const updateRecords = this._store.__getUpdatedRecordIDs();
Object.keys(this._store.getSource().toJSON()).forEach((key) => {
updateRecords.add(key);
});
this._store.notify();
return true;
})
.catch((error) => {
this._rehydrated = false;
throw error;
});
}
return this.promisesRestore;
}
isRehydrated() {
return this._rehydrated;
}
isOnline() {
return this.getStoreOffline().isOnline();
}
getStoreOffline() {
return this._relayStoreOffline;
}
retain(operation, configRetain) {
return this._store.retain(operation, configRetain);
}
disposePersisted(id) {
return this._store.disposePersisted(id);
}
executeMutationOffline({ operation, optimisticResponse, optimisticUpdater, updater, uploadables, }) {
let optimisticConfig;
if (optimisticResponse || optimisticUpdater) {
optimisticConfig = {
operation,
response: optimisticResponse,
updater: optimisticUpdater,
};
}
warning(!!optimisticConfig, 'commitMutation offline: no optimistic responses configured. the mutation will not perform any store updates.');
const source = RelayObservable.create((sink) => {
resolveImmediate(() => {
const sinkPublish = optimisticConfig ? this.getStore().getSource()._sink.toJSON() : {};
const backup = {};
Object.keys(sinkPublish).forEach((key) => (backup[key] = this.getStore().getSource()._base.get(key)));
sink.next({
data: optimisticResponse ? optimisticResponse : {},
});
const id = uuid();
const payload = {
operation,
optimisticResponse,
uploadables,
};
const request = {
payload,
backup,
sink: sinkPublish,
};
this.getStoreOffline()
.publish({ id, request, serial: true })
.then((_offlineRecord) => {
this.getStoreOffline().notify();
sink.complete();
})
.catch((error) => {
sink.error(error, true);
});
});
});
return this._execute({
createSource: () => source,
isClientPayload: false,
operation,
optimisticConfig,
updater: optimisticResponse ? updater : optimisticUpdater,
});
}
executeMutation(mutationOptions) {
if (this.isOnline()) {
return super.executeMutation(mutationOptions);
}
else {
return this.executeMutationOffline(mutationOptions);
}
}
}
//# sourceMappingURL=Environment.js.map