atom-react
Version:
An opiniated way to use ReactJS in a functional way in plain old Javascript, inspired by popular Clojurescript wrappers like Om
159 lines (126 loc) • 4.72 kB
JavaScript
;
var _ = require("lodash");
var AtomAsyncValueStates = {
LOADING: "LOADING",
SUCCESS: "SUCCESS",
ERROR: "ERROR"
};
exports.AtomAsyncValueStates = AtomAsyncValueStates;
var AtomAsyncValue = function AtomAsyncValue(state) {
this.state = state || AtomAsyncValueStates.LOADING;
};
AtomAsyncValue.prototype.isLoading = function() {
return this.state === AtomAsyncValueStates.LOADING;
};
AtomAsyncValue.prototype.isSuccess = function() {
return this.state === AtomAsyncValueStates.SUCCESS;
};
AtomAsyncValue.prototype.isError = function() {
return this.state === AtomAsyncValueStates.ERROR;
};
AtomAsyncValue.prototype.toSuccess = function(value) {
var async = new AtomAsyncValue(AtomAsyncValueStates.SUCCESS);
async.value = value;
return async;
};
AtomAsyncValue.prototype.asSuccess = function() {
if ( !this.isSuccess() ) throw new Error("Can't convert async value as success becauuse its state is " + this.state);
return this.value;
};
AtomAsyncValue.prototype.toError = function(error) {
var async = new AtomAsyncValue(AtomAsyncValueStates.ERROR);
async.error = error;
return async;
};
AtomAsyncValue.prototype.asError = function() {
if ( !this.isError() ) throw new Error("Can't convert async value as error becauuse its state is " + this.state);
return this.error;
};
exports.AtomAsyncValue = AtomAsyncValue;
// This returns a promise of the swapped result if the swap was successful
function setupAsyncValueSwapping(atom, path, asyncValue, promise, logCompletion) {
return new Promise(function (resolve, reject) {
promise.then(
function asyncCompletionSuccess(data) {
var swapped = atom.compareAndSwapPathValue(path, asyncValue, asyncValue.toSuccess(data));
if (logCompletion) {
console.debug("Async value completion", path, "Swap success=", swapped);
}
if (swapped) {
resolve(data);
} else {
reject(new Error("Async value completion for path " + path + " but swap=false"));
}
},
function asyncCompletionError(error) {
var swapped = atom.compareAndSwapPathValue(path, asyncValue, asyncValue.toError(error));
if (logCompletion) {
console.error("Async value completion error", path, "Swap success=", swapped);
console.error(error.stack ? error.stack : error);
}
reject(error);
})
});
}
function setPathAsyncValue(atom,path,promise,logCompletion) {
var asyncValue = new AtomAsyncValue();
atom.setPathValue(path,asyncValue);
return setupAsyncValueSwapping(atom,path,asyncValue,promise,logCompletion);
};
exports.setPathAsyncValue = setPathAsyncValue;
function setPathResolvedAsyncValue(atom,path,resolvedValue) {
var asyncValue = new AtomAsyncValue().toSuccess(resolvedValue);
atom.setPathValue(path,asyncValue);
};
exports.setPathResolvedAsyncValue = setPathResolvedAsyncValue;
function pushPathAsyncValue(atom,listPath,promise,logCompletion) {
var list = atom.getPathValue(listPath) || [];
if ( list instanceof Array ) {
var asyncValueIndex = list.length;
var asyncValuePath = listPath.concat([asyncValueIndex]);
var asyncValue = new AtomAsyncValue();
var newList = list.concat(asyncValue);
atom.setPathValue(listPath,newList);
return setupAsyncValueSwapping(atom,asyncValuePath,asyncValue,promise,logCompletion);
} else {
throw new Error("Can't push async value in list because list is " + JSON.stringify(list));
}
};
exports.pushPathAsyncValue = pushPathAsyncValue;
function followValues(cursor) {
if ( !cursor.exists() ) {
return [];
}
if ( cursor.isInstanceOf(Array) ) {
return cursor.list();
}
else {
return [cursor];
}
}
function getAsyncValueCursors(asyncValueCursor) {
if ( asyncValueCursor.get().isSuccess() ) {
return followValues(asyncValueCursor.follow("value"));
}
else {
return [];
}
}
function getPathAsyncValueListCursors(cursor) {
if ( !cursor.exists() ) {
return [];
}
else if ( cursor.isInstanceOf(AtomAsyncValue) ) {
return getAsyncValueCursors(cursor);
}
else if ( cursor.isInstanceOf(Array) ) {
var cursorsArray = cursor.list().map(function(asyncValueCursor) {
return getAsyncValueCursors(asyncValueCursor);
});
return _.flatten(cursorsArray);
}
else {
throw new Error("getPathAsyncValueListCursors can only be called on an array, not a "+asyncValueList+" for path="+cursor.atomPath);
}
}
exports.getPathAsyncValueListCursors = getPathAsyncValueListCursors;