-
Notifications
You must be signed in to change notification settings - Fork 27
Add a DaybedStorage PoC #25
base: master
Are you sure you want to change the base?
Changes from all commits
151b54b
3719a26
742e685
55079d7
ca491bc
5ff23e9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,10 +16,19 @@ var KeptApp = React.createClass({ | |
|
||
getInitialState: function() { | ||
return { | ||
items: this.props.store.load() | ||
items: [] | ||
}; | ||
}, | ||
|
||
componentDidMount: function() { | ||
this.props.store.load() | ||
.then(function(items) { | ||
this.setState({ | ||
items: items | ||
}); | ||
}.bind(this)); | ||
}, | ||
|
||
/** | ||
* Provided for the UndoStack mixin. | ||
*/ | ||
|
@@ -56,13 +65,15 @@ var KeptApp = React.createClass({ | |
}, | ||
|
||
save: function(items) { | ||
this.props.store.save(items); | ||
this.snapshot(); | ||
this.setState({items: items}); | ||
return this.props.store.save(items) | ||
.then(function() { | ||
this.snapshot(); | ||
this.setState({items: items}); | ||
}.bind(this)); | ||
}, | ||
|
||
loadSamples: function() { | ||
this.save(initial); | ||
return this.save(initial); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is if you need to wait for the promise to complete there. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok makes a lot of sense. |
||
}, | ||
|
||
formCreator: function(type) { | ||
|
@@ -80,34 +91,35 @@ var KeptApp = React.createClass({ | |
}, | ||
|
||
create: function(itemData) { | ||
itemData.id = utils.nextId(this.state.items); | ||
this.save(this.state.items.concat([itemData])); | ||
this.resetForm(); | ||
itemData.id = utils.nextId(); | ||
return this.save(this.state.items.concat([itemData])) | ||
.then(this.resetForm.bind(this)); | ||
}, | ||
|
||
edit: function(itemData) { | ||
this.formCreator(itemData.type)(itemData); | ||
}, | ||
|
||
update: function(updatedItem) { | ||
this.save(this.state.items.map(function(item) { | ||
return this.save(this.state.items.map(function(item) { | ||
if (item.id === updatedItem.id) | ||
return updatedItem; | ||
return item; | ||
})); | ||
this.resetForm(); | ||
})).then(function() { | ||
this.resetForm(); | ||
}.bind(this)); | ||
}, | ||
|
||
remove: function(itemData) { | ||
this.save(this.state.items.filter(function(data) { | ||
return this.save(this.state.items.filter(function(data) { | ||
return itemData !== data; | ||
})); | ||
}, | ||
|
||
move: function(fromIndex, toIndex) { | ||
var items = this.state.items.slice(0); | ||
items.splice(toIndex, 0, items.splice(fromIndex, 1)[0]); | ||
this.save(items); | ||
return this.save(items); | ||
}, | ||
|
||
render: function() { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
{ | ||
"text": { | ||
"title": "Keep — Text", | ||
"description": "Store user text kept card.", | ||
"fields": [ | ||
{ | ||
"name": "title", | ||
"type": "string", | ||
"label": "Title", | ||
"required": true | ||
}, | ||
{ | ||
"name": "text", | ||
"type": "text", | ||
"label": "Text", | ||
"required": true | ||
} | ||
] | ||
}, | ||
"todo": { | ||
"title": "Keep — Todo", | ||
"description": "Store user text kept card.", | ||
"fields": [ | ||
{ | ||
"name": "title", | ||
"type": "string", | ||
"label": "Title", | ||
"required": true | ||
}, | ||
{ | ||
"name": "tasks", | ||
"type": "list", | ||
"label": "Tasks", | ||
"description": "List of todo list's tasks.", | ||
"fields": [ | ||
{ | ||
"name": "label", | ||
"type": "string", | ||
"label": "Label", | ||
"required": true | ||
}, | ||
{ | ||
"name": "done", | ||
"type": "boolean", | ||
"label": "Check when done!", | ||
"required": true | ||
} | ||
] | ||
} | ||
] | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
{"definition": { | ||
"title": "Keep — Todo", | ||
"description": "Store user text kept card.", | ||
"fields": [ | ||
{ | ||
"name": "title", | ||
"type": "string", | ||
"label": "Title", | ||
"required": true | ||
}, | ||
{ | ||
"name": "tasks", | ||
"type": "list", | ||
"label": "Tasks", | ||
"description": "List of todo list's tasks.", | ||
"fields": [ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lacks one level : a list has an "itemtype", which in this case would be "object", and its parameters would have those "fields". I don't think this definition is valid at all.
Which makes me wonder one thing : why the h**ck didn't I design the
|
||
{ | ||
"name": "label", | ||
"type": "string", | ||
"label": "Label", | ||
"required": true | ||
}, | ||
{ | ||
"name": "done", | ||
"type": "boolean", | ||
"label": "Check when done!", | ||
"required": true | ||
} | ||
] | ||
} | ||
] | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"title": "Today", | ||
"tasks": [ | ||
{"label": "Walk the dog", "done": true}, | ||
{"label": "Clean the car", "done": false}, | ||
{"label": "Buy a new carpet", "done": false} | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
"use strict"; | ||
var PREFIX = "keep_"; // Schema table prefix | ||
var schema = require("../data/daybed_schema"); | ||
var objectEquals = require("../utils").objectEquals; | ||
|
||
|
||
function DaybedStore(options) { | ||
if (!options.hasOwnProperty("host")) { | ||
throw new Error("Please define your Daybed storage host urls."); | ||
} | ||
if (!options.hasOwnProperty("tokenId") || | ||
!options.hasOwnProperty("tokenKey")) { | ||
throw new Error("Please define your Daybed credentials " + | ||
"(tokenId, tokenKey)."); | ||
} | ||
this._types = Object.keys(schema); | ||
|
||
this._store = new Daybed.Session(options.host, { | ||
id: options.tokenId, | ||
key: options.tokenKey, | ||
algorithm: "sha256" | ||
}); | ||
} | ||
|
||
|
||
DaybedStore.prototype = { | ||
setUp: function() { | ||
return this._store.getModels() | ||
.then(function(models) { | ||
// Make sure all needed models are defined. | ||
var modelIds = []; | ||
models.forEach(function(doc) { | ||
modelIds.push(doc.id); | ||
}); | ||
return Promise.all(this._types.map(function(type) { | ||
var daybedModelName = PREFIX + type; | ||
if (modelIds.indexOf(daybedModelName) === -1) { | ||
// If not create the model | ||
return this._store.addModel(daybedModelName, schema[type]) | ||
.catch(function(err) { | ||
console.error("Add model", daybedModelName, ":", err); | ||
throw new Error("Add model " + daybedModelName + ": " + err); | ||
}); | ||
} else { | ||
console.log("Model", daybedModelName, "already exists."); | ||
} | ||
}.bind(this))); | ||
}.bind(this)); | ||
}, | ||
load: function() { | ||
var data = []; | ||
return Promise.all(this._types.map(function(type) { | ||
return this._store.getRecords(PREFIX + type) | ||
.then(function(doc) { | ||
data = data.concat(doc.records.map(function(record) { | ||
record.type = type; | ||
return record; | ||
})); | ||
}) | ||
.catch(console.error); | ||
}.bind(this))) | ||
.then(function() { | ||
return data; | ||
}); | ||
}, | ||
|
||
save: function(data) { | ||
console.log(data); | ||
var self = this; | ||
var currentDataObj = {}, | ||
previousDataObj = {}, | ||
currentDataIds, previousDataIds, | ||
droppedObjIds, changedObjIds; | ||
|
||
return self.load() | ||
.then(function(items) { | ||
console.log(items); | ||
// Get current data ids | ||
currentDataIds = data.map(function(doc) { | ||
currentDataObj[doc.id] = doc; | ||
return doc.id; | ||
}); | ||
|
||
previousDataIds = items.map(function(doc) { | ||
previousDataObj[doc.id] = doc; | ||
return doc.id | ||
}); | ||
|
||
// Get deleted objects | ||
droppedObjIds = previousDataIds.filter(function(docId) { | ||
return currentDataIds.indexOf(docId) < 0; | ||
}); | ||
|
||
// Get changed objects | ||
changedObjIds = currentDataIds.filter(function(docId) { | ||
console.log(docId, currentDataObj[docId], previousDataObj[docId], | ||
objectEquals(currentDataObj[docId], previousDataObj[docId])); | ||
return objectEquals(currentDataObj[docId], previousDataObj[docId]) !== true; | ||
}); | ||
|
||
// Make the change to the backend | ||
return Promise.all(changedObjIds.map(function(docId) { | ||
var doc = currentDataObj[docId]; | ||
var daybedModelName; | ||
try { | ||
doc = JSON.parse(JSON.stringify(doc)); | ||
daybedModelName = PREFIX + doc.type; | ||
delete doc.type; | ||
} catch (e) { | ||
console.error(doc, "failed saving keep data", e); | ||
return; | ||
} | ||
return self._store.addRecord(daybedModelName, doc) | ||
.catch(console.error); | ||
})).then(function() { | ||
return Promise.all(droppedObjIds.map(function(docId) { | ||
var doc = previousDataObj[docId]; | ||
var daybedModelName = PREFIX + doc.type; | ||
return self._store.deleteRecord(daybedModelName, docId) | ||
.catch(console.error); | ||
})); | ||
}); | ||
}); | ||
} | ||
}; | ||
|
||
module.exports = DaybedStore; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,25 +7,30 @@ function KeptStore(options) { | |
|
||
KeptStore.prototype = { | ||
load: function() { | ||
var data = this._store.getItem("keep.data"); | ||
if (!data) { | ||
console.error("empty stored kept data:", data); | ||
return []; | ||
} | ||
try { | ||
return JSON.parse(data) || []; | ||
} catch (e) { | ||
console.error("failed parsing kept data:", data, e); | ||
return []; | ||
} | ||
return new Promise(function(resolve, reject) { | ||
var data = this._store.getItem("keep.data"); | ||
if (!data) { | ||
console.error("empty stored kept data:", data); | ||
resolve([]); | ||
} | ||
try { | ||
resolve(JSON.parse(data) || []); | ||
} catch (e) { | ||
console.error("failed parsing kept data:", data, e); | ||
resolve([]); | ||
} | ||
}.bind(this)); | ||
}, | ||
|
||
save: function(data) { | ||
try { | ||
this._store["keep.data"] = JSON.stringify(data); | ||
} catch (e) { | ||
console.error("failed saving keep data", e); | ||
} | ||
return new Promise(function(resolve, reject) { | ||
try { | ||
this._store["keep.data"] = JSON.stringify(data); | ||
} catch (e) { | ||
console.error("failed saving keep data", e); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. call reject() ? |
||
} | ||
resolve(); | ||
}.bind(this)); | ||
} | ||
}; | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes me think we'll have to update the localStorage store to be async & promise-compliant as well, if we want to keep backend-agnostic :/
Edit: wow, you did it. You're fast :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I did sir.