Last active
August 29, 2015 13:56
-
-
Save simshanith/8891036 to your computer and use it in GitHub Desktop.
Facebook JS SDK Wrapper Module for a Simple Photo Selector App
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
// Wrap `FB` from SDK with utility & application methods. | |
// Dependencies | |
// --- | |
// facebook: Facebook's Provided JS SDK, loaded from Facebook's servers. | |
// https://developers.facebook.com/docs/javascript/ | |
// --- | |
// underscore: lodash.underscore provides AMD wrapper (among other benefits) | |
// http://lodash.com/ | |
// --- | |
// q: fast, consistent promises | |
// http://documentup.com/kriskowal/q/ | |
// --- | |
// social-config: application helper that reads JSON embedded in page, keyed by domain | |
// Implementation left as an exercise for the reader. | |
// --- | |
// safeLog: minimal console.log wrapper | |
// Source below | |
define(['facebook', 'underscore', 'q', 'social-config', 'safeLog'], function(FB, _, Q, socialConfig, safeLog){ | |
// declare extended permissions. | |
// should separate read and write, based on action. | |
// In this example (take from <http://leagueofcaptains.gatorade.com>), relying on an `app.currentPage` variable. | |
// https://developers.facebook.com/docs/reference/login/extended-permissions | |
var neededPermissions = []; | |
if ( app.currentPage === 'creator' ) { | |
neededPermissions.push('publish_actions'); | |
} | |
if ( app.currentPage === 'cover' ) { | |
neededPermissions.push('user_photos'); | |
} | |
// initialize with app id based on environemnt | |
// initialize with app id based on environemnt | |
FB.init({ | |
appId : socialConfig.fb_app_id, | |
// appId : '1437004476564609', | |
version: 'v2.0' | |
}); | |
// Create a deferred for Facebook login state. | |
// Resolved only on successful login. | |
// Never rejected; just left pending. | |
// We're optimists. | |
var loginQ = new Q.defer(); | |
// options for Facebook Login Dialog | |
var loginOpts = { | |
// construct scope from array | |
scope: neededPermissions.join(',') | |
}; | |
function checkPermissions() { | |
var deferred = new Q.defer(); | |
// resolved if all needed are accepted; | |
// otherwise rejected | |
// now using 2.0 | |
//https://developers.facebook.com/docs/facebook-login/login-flow-for-web/v2.0#re-asking-declined-permissions | |
FB.api('/me/permissions', function(resp) { | |
// some response checking | |
if( !resp || !resp.data || !resp.data.length) { | |
deferred.reject(false); | |
} | |
// grab permissions granted | |
var perms = _.pluck(_.where(resp.data, {status: 'granted'}), 'permission'); | |
// compare with needed permissions | |
var checkedPerms = _.intersection(neededPermissions, perms); | |
safeLog(checkedPerms); | |
if(checkedPerms.length === neededPermissions.length) { | |
// good to go, resolve with true. | |
deferred.resolve(true); | |
} else { | |
deferred.reject(false); | |
} | |
}); | |
return deferred.promise; | |
}; | |
FB.checkPermissions = checkPermissions; | |
function resolveWhenPermissed() { | |
FB.checkPermissions().then(function() { | |
// good to go, resolve with auth response. | |
loginQ.resolve( FB.getAuthResponse() ); | |
}, function() { | |
safeLog('needed permission declined. allowing rerequest.'); | |
loginQ = new Q.defer(); | |
loginOpts.auth_type = 'rerequest'; | |
}); | |
} | |
// Check if already authed. | |
FB.getLoginStatus(function(auth) { | |
if( auth && auth.status === 'connected' ) { | |
// logged in; check permissions. | |
resolveWhenPermissed(); | |
} | |
}); | |
// Calls passed function when logged in. | |
// Uses deferred's promise to popup login flow only once. | |
FB.loginOnce = function(callback) { | |
// gracefully fail with default no-op for callback. | |
var cont = _.isFunction(callback) ? callback : function(){}; | |
/* @TODO: detect popup window to see if flow is in progress */ | |
// if it hasn't resolved yet, try to login. | |
if( loginQ.promise.isPending() ) { | |
// promise it will happen. | |
loginQ.promise.then(cont); | |
FB.login(function(resp) { | |
if(resp && resp.status === 'connected') { | |
resolveWhenPermissed(); | |
} else { | |
safeLog('error logging in to facebook'); | |
} | |
}, loginOpts); | |
} else { | |
cont( FB.getAuthResponse() ); | |
} | |
}; | |
FB.albums = null; | |
FB.getAlbums = function(callback){ | |
// gracefully fail with default no-op for callback. | |
var cont = _.isFunction(callback) ? callback : function(){}; | |
// if initialized, execute callback and return immediately. | |
if( FB.albums !== null ) { | |
cont(FB.albums); | |
return FB.albums; | |
} | |
// otherwise, construct the query. | |
var queries = { | |
// grab cover photo ids & names of user's albums. | |
"query1":"SELECT cover_pid, name FROM album WHERE owner=me()", | |
// grab cover photo details of user's albums. | |
// chain query based on cover photo ids retrieved above. | |
"query2":"SELECT aid, pid, src, created FROM photo WHERE pid IN(SELECT cover_pid FROM #query1) ORDER BY created DESC" | |
}; | |
FB.api({ | |
method: 'fql.multiquery', | |
queries: queries | |
}, handleResponse); | |
function handleResponse(result) { | |
// query1 | |
var albums = result[0].fql_result_set; | |
// query2 | |
var images = result[1].fql_result_set; | |
// construct albums data with album id, name, and cover photo url. | |
FB.albums = _.chain(albums).map(function(album, i, albums) { | |
var found = _.findWhere(images, {pid: album.cover_pid}); | |
if( found ) { | |
return { | |
id: found.aid, | |
name: album.name, | |
url: found.src | |
}; | |
} | |
}).compact().value(); | |
// execute callback and return. | |
cont(FB.albums); | |
return FB.albums; | |
} | |
}; | |
FB.getImages = function(id, callback){ | |
if( !id ) { | |
throw new Error('Album Id required.'); | |
} | |
var cont = _.isFunction(callback) ? callback : function(){}; | |
var album = null; | |
// check if this album's photos have been retrieved. | |
if( FB.albums !== null ) { | |
// grab the album by id. | |
album = _.findWhere(FB.albums, {id: id}); | |
} | |
if( album && album.photos ) { | |
// call the callback & return the photos immediately. | |
cont(album.photos); | |
return album.photos; | |
} | |
// no photos detected; constuct the api call. | |
FB.api({ | |
method: 'fql.query', | |
// grab photos by album id. | |
query: "SELECT aid, pid, src, src_big, created FROM photo WHERE aid=" + id + " ORDER BY created DESC" | |
}, handleResponse); | |
function handleResponse(result) { | |
// stick the photos on the album. | |
if( album ) { | |
album.photos = result; | |
} | |
// call the callback & return result. | |
cont(result); | |
return album.photos; | |
} | |
}; | |
return FB; | |
}); |
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
// Minimal console.log wrapper. | |
// Dependency | |
// --- | |
// ES5 Shim: Bootstrap fuller ES5 spec into application. Methods ironically not used here. | |
// Array.prototype.slice is well supported. | |
// https://github.com/es-shims/es5-shim/ | |
define(['es5'], function() { | |
function safeLog() { | |
var logs; | |
if(window.console&&window.console.log){ | |
logs = Array.prototype.slice.call(arguments); | |
try { | |
console.log.apply(console, logs); | |
} catch(e) { | |
// attempt to log array | |
console.log(logs); | |
} | |
} | |
} | |
window.safeLog = safeLog; | |
return window.safeLog; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Updated with a Facebook API v2.0 example.