Last active
January 22, 2019 03:11
-
-
Save imana97/a024ad59f2b8eefecca65ac238ec1381 to your computer and use it in GitHub Desktop.
ParseMobx replace Parse Objects and makes them observable
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Import from mobx | |
import {action, configure, extendObservable, runInAction} from "mobx"; | |
// Configure mobx strictMode. so any changes to observable must be in actions. | |
configure({enforceActions: "observed"}); | |
/** | |
* Main Class | |
*/ | |
export default class ParseMobx { | |
/** | |
* | |
* @param {ParseObject} obj The parse object. | |
*/ | |
constructor(obj) { | |
// make sure objects are saved. | |
if (obj.isNew()) { | |
throw new Error(`Only Saved Parse objects can be converted to ParseMobx objects. | |
not saved object: ${obj.className}`); | |
} | |
// keep a ref of parse object. | |
this._parseObj = obj; | |
// convert to ParseMobx recursively. | |
this._convert(obj); | |
} | |
/** | |
* Convert a ParseObject or array of ParseObjects to ParseMobx object or array of ParseMobx objects. | |
* @static | |
* @param param | |
* @returns {ParseMobx|<ParseMobx>|null} | |
*/ | |
static toParseMobx(param) { | |
return typeof param === "function" | |
? obj => param(new ParseMobx(obj)) | |
: Array.isArray(param) | |
? param.map(obj => new ParseMobx(obj)) | |
: param | |
? new ParseMobx(param) | |
: null; | |
} | |
/** | |
* | |
* @param list | |
* @param item | |
* @param key | |
*/ | |
static deleteListItem(list, item, key = "objectId") { | |
list.splice(list.findIndex(obj => obj[key] === item[key]), 1); | |
}; | |
/** | |
* | |
* @param list | |
* @param item | |
* @param key | |
*/ | |
static updateListItem(list, item, key = "objectId") { | |
(list[list.findIndex(obj => obj[key] === item[key])] = item); | |
}; | |
/** | |
* Convert turns a Parse object into observable ParseMobx object. | |
* @param obj | |
* @private | |
*/ | |
_convert(obj) { | |
// copy id | |
this.id = obj.id; | |
this.attributes = {createdAt: obj.get('createdAt')}; | |
// store props to be observed. | |
const observableObject = {}; | |
for (let key in obj.attributes) { | |
const attribute = obj.attributes[key]; | |
if (attribute.constructor.name === 'ParseObjectSubclass') { | |
this.attributes[key] = new ParseMobx(attribute); | |
} else if (attribute.constructor.name === 'ParseObjectSubclass') { | |
observableObject[key] = attribute.map((el) => | |
(el.constructor.name === 'ParseObjectSubclass') ? ParseMobx(el) : el); | |
} else if ( | |
attribute.constructor.name !== 'ParseRelation' && | |
attribute.constructor.name !== 'ParseACL' && | |
key !== 'createdAt' | |
) { | |
observableObject[key] = attribute; | |
} | |
} | |
extendObservable(this.attributes, observableObject); | |
} | |
/** | |
* | |
* @param key | |
* @param value | |
* @private | |
*/ | |
_checkDefined(key, value) { | |
if (typeof this.attributes[key] === 'undefined') { | |
const objToExtend = {}; | |
objToExtend[key] = value; | |
extendObservable(this.attributes, objToExtend); | |
} | |
} | |
_checkType(key, type) { | |
return (this.attributes[key].constructor.name === type); | |
} | |
/** | |
* todo: update Model | |
* Atomically add an object to the end of the array associated with a given key. | |
* @param attr | |
* @param item | |
* @returns {ParseMobx} | |
*/ | |
add(attr, item) { | |
this._parseObj.add(attr, item); | |
return this; | |
} | |
/** | |
* todo: update Model | |
* Atomically add the objects to the end of the array associated with a given key. | |
* @param attr | |
* @param item | |
* @returns {ParseMobx} | |
*/ | |
addAll(attr, item) { | |
this._parseObj.addAll(attr, item); | |
return this; | |
} | |
/** | |
* todo: update Model | |
* Atomically add the objects to the array associated with a given key, | |
* only if it is not already present in the array. The position of the insert is not guaranteed. | |
* @param attr | |
* @param items | |
* @returns {ParseMobx} | |
*/ | |
addAllUnique(attr, items) { | |
this._parseObj.addAllUnique(attr, items); | |
return this; | |
} | |
/** | |
* Atomically add an object to the array associated with a given key, | |
* only if it is not already present in the array. The position of the insert is not guaranteed. | |
* @param key | |
* @param value | |
* @returns {ParseMobx} | |
*/ | |
@action addUnique(key, value) { | |
this._checkDefined(key, []); | |
if (this._checkType(key, 'Array')) { | |
if (this.attributes[key].indexOf(value) === -1) { | |
this._checkType(value, 'ParseObjectSubclass') ? | |
this.attributes[key].push(new ParseMobx(value)) : | |
this.attributes[key].push(value); | |
} | |
} | |
this._parseObj.addUnique(key, value); | |
return this; | |
} | |
clear() { | |
this._parseObj.clear(); | |
} | |
clone() { | |
this._parseObj.clone(); | |
} | |
/** | |
* | |
* @returns {Promise<*>} | |
*/ | |
async destroy(options) { | |
return this._parseObj.destroy(options); | |
} | |
dirty(attr) { | |
return this._parseObj.dirty(attr); | |
} | |
dirtyKeys() { | |
return this._parseObj.dirtyKeys(); | |
} | |
equals(other) { | |
return this._parseObj.equals(other); | |
} | |
escape(attr) { | |
return this._parseObj.escape(attr); | |
} | |
existed() { | |
return this._parseObj.existed(); | |
} | |
async fetch(options) { | |
const newParseObj = await this._parseObj.fetch(options); | |
// convert new objcet | |
this._convert(newParseObj); | |
return this; | |
} | |
async fetchWithInclude(keys, options) { | |
const newParseObj = await this._parseObj.fetchWithInclude(keys, options); | |
// convert new objcet | |
this._convert(newParseObj); | |
return this; | |
} | |
/** | |
* | |
* @param key | |
* @returns {*} | |
*/ | |
get(key) { | |
return this.attributes[key]; | |
} | |
/** | |
* | |
* @returns {ParseMobx} | |
*/ | |
getACL() { | |
this._parseObj.getACL(arguments); | |
return this; | |
} | |
has(attr) { | |
return this._parseObj.has(attr); | |
} | |
/** | |
* | |
* @param attr | |
* @param amount | |
*/ | |
@action increment(attr, amount = 1) { | |
// set 0 to attr if undefined. | |
this._checkDefined(attr, 0); | |
if (this._checkType(attr, 'Number')) { | |
this.attributes[attr] += amount; | |
} | |
this._parseObj.increment(attr, amount); | |
} | |
isNew() { | |
return false; | |
} | |
isValid() { | |
return this._parseObj.isValid(); | |
} | |
newInstance() { | |
return this._parseObj.newInstance(); | |
} | |
op(attr) { | |
return this._parseObj.op(attr); | |
} | |
/** | |
* | |
* @returns {*|Parse.Relation} | |
*/ | |
relation() { | |
return this._parseObj.relation(arguments); | |
} | |
/** | |
* | |
* @param key | |
* @param value | |
* @returns {ParseMobx} | |
*/ | |
@action remove(key, value) { | |
this._checkDefined(key, []); | |
if (this._checkType(key, 'Array')) { | |
if (this.attributes[key].indexOf(value) !== -1) { | |
this.attributes[key].splice(this.attributes[key].indexOf(value), 1); | |
} | |
} | |
this._parseObj.remove(key, value); | |
return this; | |
} | |
@action removeAll(attr, items) { | |
this._checkDefined(attr, []); | |
if (this._checkType(attr, 'Array') && this._checkType(items, 'Array')) { | |
items.forEach(item => { | |
if (this.attributes[attr].indexOf(item) !== -1) { | |
this.attributes[attr].splice(this.attributes[attr].indexOf(item), 1); | |
} | |
}); | |
} | |
this._parseObj.removeAll(attr, items); | |
return this; | |
} | |
@action revert() { | |
this._parseObj.revert(); | |
this._convert(this._parseObj); | |
return this; | |
} | |
/** | |
* | |
* @returns {Promise<void>} | |
*/ | |
@action async save(options) { | |
await this._parseObj.save(options); | |
runInAction(() => this.set('updatedAt', new Date().toISOString())); | |
} | |
/** | |
* | |
* @param key | |
* @param value | |
* @returns {ParseMobx} | |
*/ | |
@action set(key, value, options) { | |
if (value.constructor.name === 'ParseRelation') { | |
throw new Error('You can not add relations with set'); | |
} | |
if (value.constructor.name === 'ParseACL') { | |
throw new Error('Please use setACL() instead'); | |
} | |
if (typeof this.attributes[key] !== 'undefined') { | |
// if it is parse subclass, create parse object. | |
if (value.constructor.name === 'ParseObjectSubclass') { | |
this.attributes[key] = new ParseMobx(value); | |
} else { | |
this.attributes[key] = value; | |
} | |
} else { | |
const objToExtend = {}; | |
objToExtend[key] = value; | |
extendObservable(this.attributes, objToExtend); | |
} | |
this._parseObj.set(key, value, options); | |
return this; | |
} | |
/** | |
* | |
* @returns {ParseMobx} | |
*/ | |
setACL(acl, options) { | |
this._parseObj.setACL(acl, options); | |
return this; | |
} | |
/** | |
* | |
* @returns {*} | |
*/ | |
toJSON() { | |
return this._parseObj.toJSON(); | |
} | |
toPointer() { | |
return this; | |
} | |
/** | |
* todo: update model? | |
* @param attr | |
*/ | |
unset(attr) { | |
this._parseObj.unset(attr); | |
this._convert(this._parseObj); | |
} | |
validate(attrs) { | |
return this._parseObj.validate(attrs); | |
} | |
/** | |
* | |
* @returns {ParseObject} | |
*/ | |
getParseObject() { | |
return this._parseObj; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment