Skip to content
This repository has been archived by the owner on Nov 10, 2017. It is now read-only.

Add a DaybedStorage PoC #25

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
</p>
</footer>
</div>
<script src="js/vendor/hawk.js"></script>
<script src="js/vendor/daybed.js"></script>
<script src="js/vendors.js"></script>
<script src="js/kept.js"></script>
</body>
Expand Down
38 changes: 25 additions & 13 deletions src/js/components/KeptApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,19 @@ var KeptApp = React.createClass({

getInitialState: function() {
return {
items: this.props.store.load()
items: []
};
},

componentDidMount: function() {
this.props.store.load()
Copy link
Owner

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I did sir.

.then(function(items) {
this.setState({
items: items
});
}.bind(this));
},

/**
* Provided for the UndoStack mixin.
*/
Expand Down Expand Up @@ -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);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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.
We return the promise so you can use a .then() on it.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok makes a lot of sense.

},

formCreator: function(type) {
Expand All @@ -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() {
Expand Down
52 changes: 52 additions & 0 deletions src/js/data/daybed_schema.json
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
}
]
}
]
}
}
33 changes: 33 additions & 0 deletions src/js/data/definition
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": [

Choose a reason for hiding this comment

The 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.

{name: tasks,
 type: list,
 itemtype: object,
 parameters: fields: [...]
 }

Which makes me wonder one thing : why the h**ck didn't I design the list field using a single "item" property :

{name: tasks,
 type: list,
 item: {
    type: object,
    fields: [...]
 }

{
"name": "label",
"type": "string",
"label": "Label",
"required": true
},
{
"name": "done",
"type": "boolean",
"label": "Check when done!",
"required": true
}
]
}
]
}
}
8 changes: 8 additions & 0 deletions src/js/data/record
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}
]
}
15 changes: 11 additions & 4 deletions src/js/kept.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@
var React = require("react");
var KeptApp = require("./components/KeptApp");
var KeptStore = require("./store");
var store = new KeptStore();

React.renderComponent(<KeptApp store={store} />,
document.getElementById('kept'));
var DaybedStore = require("./store/daybed");
// var store = new KeptStore();
var store = new DaybedStore({
"host": "http://localhost:8000",
"tokenId": "8ac94860337bcd6497bc6dd7c17836f2ce027fb442666ee8bace96c8c58e3594",
"tokenKey": "1d24f64c424cdefe4797a13c1d43b73d20988c803a1dcf80a684e6cd336db31e"
});
store.setUp().then(function() {
React.renderComponent(<KeptApp store={store} />,
document.getElementById('kept'));
});
127 changes: 127 additions & 0 deletions src/js/store/daybed.js
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;
37 changes: 21 additions & 16 deletions src/js/store/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

call reject() ?

}
resolve();
}.bind(this));
}
};

Expand Down
Loading