Skip to content

Commit

Permalink
Synchronization with Google Drive
Browse files Browse the repository at this point in the history
- Synchronize the flag state with Google Drive using the realtime API.
- Handle Google sign in.
- Keep in sync the localstorage state.
  • Loading branch information
alefranz committed Dec 18, 2014
1 parent dae8ce7 commit 5b71abf
Show file tree
Hide file tree
Showing 3 changed files with 235 additions and 4 deletions.
16 changes: 15 additions & 1 deletion css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ a {
padding-bottom: 2px;
}

#print, #reset {
#print, #reset, #login {
border-radius: 5px;
background-color: #369;
color: white;
Expand All @@ -77,6 +77,15 @@ a {
background-color: #DF5941;
color: #FFF;
}
#signin {
background-color: transparent;
border: 1px solid #DF5941;
color: #DF5941;
}
.no-touch #signin:hover {
background-color: #DF5941;
color: #FFF;
}

#header {
padding: 0.8rem;
Expand Down Expand Up @@ -221,6 +230,11 @@ small {
font-weight: lighter;
}

#gdrivebox span {
font-size: 0.9em;
display: none;
}


@media (min-width: 1024px) {
body {
Expand Down
67 changes: 64 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,14 @@ <h1>UX Project Checklist</h1>
</li>
</ul>
</div>
<div id="gdrivebox" class="hide-for-print">
<span id="signin-do"><button id="signin">Sign in with Google</button> to sync across devices.</span>
<span id="signin-failed">Sign in failed! :(</span>
<span id="signin-success">You're signed in with Google.</span>
</div>
</div>

<form id="checklist-form" data-persist="garlic" method="POST">
<form id="checklist-form" method="POST">
<div class="row clearfix">
<div id="Research" class="title"><span>Research</span></div>
<div class="cards">
Expand Down Expand Up @@ -490,10 +495,13 @@ <h1>UX Project Checklist</h1>
<script>
$(document).ready(function(){
$(":checkbox").labelauty({ label: false });
$("#checklist-form").garlic();
});
$("#reset").on('click', function(){
$("#checklist-form")[0].reset();
location.reload();
$('input:checkbox').each(function() {
$(this).prop('checked', false);
});
$("#checklist-form").garlic('destroy');
});
$("#footer a").on('click', function(e){
var elm = e.currentTarget;
Expand All @@ -509,6 +517,59 @@ <h1>UX Project Checklist</h1>
'eventLabel': id
});
});

//realtime api
var checkboxes = {};
var view = null;
var controller = null;

$().ready(function(){
var isFn = function(id, e) { return $(e).is(':checked'); };
var setFn = function(id, e, val) {
$(e).prop('checked', val);
$(e).garlic('persist');
};
$('input:checkbox').each(function() {
var id = $(this).attr('id');
checkboxes[id] = new Realtime.Model.CheckBox(id, this, isFn, setFn);
});
view = new Realtime.View(checkboxes);
var cb = view.checkboxes;
console.log(cb);
controller = new Realtime.Controller(view);

gapi.load("auth:client,drive-realtime,drive-share", function () {
controller.init();
$('form').on('change', 'input:checkbox', function(ev){
console.log(ev);
controller.onCheckBoxChange($(ev.target).attr('id'));
});

var signinSuccess = function() {
$('#signin-do').hide();
$('#signin-fail').hide();
$('#signin-success').show();
$("#reset").on('click', function(){
controller.save();
});
};

var signinFailed = function() {
$('#signin-fail').show();
};

controller.auth(true,
signinSuccess,
function(){$('#signin-do').show();}
);

$('#signin').on('click', function(){
controller.auth(false, signinSuccess, signinFailed);
});
});
});
</script>
<script type="text/javascript" src="https://apis.google.com/js/api.js"></script>
<script type="text/javascript" src="js/realtime.js"></script>
</body>
</html>
156 changes: 156 additions & 0 deletions js/realtime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
Handle synchronization with Google Drive using the realtime API.
author: Alessio Franceschelli - @alefranz
*/

"use strict";

var Realtime = Realtime || {};

Realtime.Controller = function(view) {
this.view = view;
this.uxchecklist = null;
}

Realtime.Controller.prototype.log = function(message) {
if(document.location.hostname == "localhost") {
console.log(message);
}
}

Realtime.Controller.prototype.loaded = function(result) {
var model = result.getModel();
this.uxchecklist = model.getRoot().get('uxchecklist');
this.log(this.uxchecklist);
this.log(this.uxchecklist.version);

var self = this;
this.uxchecklist.checkboxes.addEventListener(gapi.drive.realtime.EventType.VALUE_CHANGED, function(value){
self.log(value);
if (!value.isLocal)
{
var key = value.property;
var val = value.newValue;
self.log("changed " + key + " as " + val);
self.view.checkboxes[key].setChecked(val);
}
});

var keys = this.uxchecklist.checkboxes.keys();
this.log(keys);
for (var i=0; i < keys.length; i++) {
var key = keys[i];
var val = this.uxchecklist.checkboxes.get(key);
this.log("loaded " + key + " as " + val);
this.view.checkboxes[key].setChecked(val);
}
this.log("ready!");
};

Realtime.Controller.prototype.onCheckBoxChange = function(key) {
var value = this.view.checkboxes[key].isChecked();
this.log("saved " + key + " as " + value);
this.uxchecklist.checkboxes.set(key, value);
};

Realtime.Controller.prototype.start = function() {
var self = this;
this.createFile(function (file) {
gapi.drive.realtime.load(file.id,
function(r) { self.loaded(r); },
function(model) { self.initializeModel(model); })
});
};

Realtime.Controller.prototype.initializeModel = function(model) {
var uxchecklist = model.create(Realtime.Model.UxCheckList);
model.getRoot().set('uxchecklist', uxchecklist);
uxchecklist.version = 1;
uxchecklist.checkboxes = model.createMap();
this.log(uxchecklist.version);
this.save(uxchecklist);
};

Realtime.Controller.prototype.save = function(uxchecklist) {
uxchecklist = uxchecklist || this.uxchecklist;
for(var key in this.view.checkboxes) {
var cb = this.view.checkboxes[key];
var value = cb.isChecked();
uxchecklist.checkboxes.set(key, value);
this.log("saved " + key + " as " + value);
}
};

Realtime.Controller.prototype.init = function() {
gapi.drive.realtime.custom.registerType(Realtime.Model.UxCheckList, 'UxCheckList');
Realtime.Model.UxCheckList.prototype.version = gapi.drive.realtime.custom.collaborativeField('version');
Realtime.Model.UxCheckList.prototype.checkboxes = gapi.drive.realtime.custom.collaborativeField('checkboxes');
};

Realtime.Controller.prototype.auth = function(immediate, success, fail) {
var self = this;
gapi.auth.authorize({
'client_id': '939842792990-97uqc8rc3h645k65ecd4j7p3u0al17aj.apps.googleusercontent.com',
'scope': 'https://www.googleapis.com/auth/drive.file email profile',
'immediate': immediate
}, function(r) { self.checkAuth(r, success, fail); });
};

Realtime.Controller.prototype.checkAuth = function(authResult, success, fail) {
if (authResult && !authResult.error) {
this.log(authResult);
success();
this.start();
} else {
fail();
}
};

Realtime.Controller.prototype.createFile = function(callback) {
var self = this;
gapi.client.load('drive', 'v2', function () {
var mimeType = 'application/vnd.google-apps.drive-sdk';
var title = 'UxCheckList';
gapi.client.drive.files.list({'q': "title = '" + title + "' and mimeType contains '" + mimeType + "' and trashed = false" })
.execute(function(r){
self.log(r);
if (!r || r.items.length < 1) {
self.log("create");
gapi.client.drive.files.insert({
'resource': {
mimeType: mimeType,
title: title
}
}).execute(callback);
} else {
var file = r.items[0];
self.log(file);
callback(file);
}
});
});
};

Realtime.View = function(checkboxes) {
this.checkboxes = checkboxes;
};

Realtime.Model = Realtime.Model || {};

Realtime.Model.UxCheckList = function () {};

Realtime.Model.CheckBox = function(id, element, isCheckedFn, setCheckedFn) {
this.id = id;
this.element = element;
this.isCheckedFn = isCheckedFn;
this.setCheckedFn = setCheckedFn;
};

Realtime.Model.CheckBox.prototype.isChecked = function() {
return this.isCheckedFn(this.id, this.element);
};

Realtime.Model.CheckBox.prototype.setChecked = function(val) {
this.setCheckedFn(this.id, this.element, val);
};

0 comments on commit 5b71abf

Please sign in to comment.