Last active
August 29, 2015 14:08
-
-
Save thehydroimpulse/46ce18966a07b105afd4 to your computer and use it in GitHub Desktop.
Offline experience
This file contains hidden or 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
# Provide an interface for working with an offline cache | |
# that makes it easy to create an offline experience. | |
class Jobber.Cache | |
@extend Jobber.EmitterMixin | |
# An in-memory cache for simplistic case. This should be something on disk to ensure | |
# the persistence quality one needs for an offline experience. | |
# | |
# @property _cache Array<CacheItem> | |
@_cache: [] | |
# Put a new item into the cache | |
# | |
# @param {Any} request | |
# @param {Any} item | |
# @chainable | |
# @return Jobber.Cache | |
@put: (request, item) -> | |
@_cache[request] = item | |
@_cache.push item | |
this | |
@matches: (request) -> | |
for key, value of @_cache | |
if value is request | |
return Promise.resolve(value) | |
Promise.reject() | |
getPhotos = () -> | |
return new Promise( (resolve, reject) -> | |
cachedData = null | |
# Try and fetch from the cache first. | |
Jobber.Cache.matches('http://flickr.com/api/photos/1').then((item) -> | |
cachedData = item | |
# This is where we would do some DOM operations or something with the cached data. The consumers | |
# could listen on a `cached` event and do some early work so the user can use the cached data **before** | |
# the API calls are triggered. | |
).finally -> | |
# The cache might have failed or succeeded, we don't care. We'll try and | |
# retrive the most up-to-date version anyways. | |
Jobber.Request.create('http://flickr.com/api/photos/1').exec().then( (res, req) -> | |
# Update the cache with the up-to-date version if we ever get to this state in the app. | |
Jobber.Cache.put req, res.body.photos | |
resolve(res.body.photos); | |
).fail -> | |
# Well, we failed to perform an HTTP request. We're probably in an offline state. | |
jobberApp.offline(1); | |
if cachedData? | |
return resolve(cachedData) | |
else | |
return reject("No internet connectivity, failed to do work!") | |
) | |
# Show a simple spinner | |
# @global | |
showSpinner = -> | |
$('.spinner').show() | |
Promise.resolve() | |
# Hide the global spinner | |
# @global | |
hideSpinner = -> | |
$('.spinner').hide() | |
Promise.resolve() | |
# Now we can **easily** fetch the photos effectively: | |
showSpinner() | |
.then(getPhotos) | |
.then( (photos) -> | |
# Do something with the photos. These can be cached photos or | |
# up-to-date versions, we don't really care. | |
photosEl = $ '.photos' | |
containerEl = document.createElement 'div' | |
for photo in photos | |
el = document.createElement 'div' | |
el.classLists.add 'photo' | |
el.innerHTML = "<img src='#{photo.src}' alt='#{photo.alt}' />" | |
# Append each photo element into a container div. This still isn't | |
# touching the DOM and is used to minimize the amount of operations we perform on | |
# the DOM. Instead of performing an `.append` for each photo, which is at least O(n) | |
# (Not counting the DOM internals work) we now achieve O(1) which is much better. | |
containerEl.append el | |
# Add the container to the DOM. | |
photosEl.append containerEl | |
).fail( (err) -> | |
# Show a small error message to say we have failed. | |
el = $('.js-errorMessage') | |
el.innerHTML = err | |
el.show() | |
).finally(hideSpinner) |
This file contains hidden or 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
# A slightly more abstracted version now using a `Resource` concept. Resources build | |
# on-top of a `Request` and `Cache` seeing as there's a common idiom being formed in the above | |
# file. Resource will check the cache first, then do the request and propagate errors exactly | |
# the same way. | |
# | |
# But, this is super short and clear and this fully works offline. | |
getPhotos = () -> | |
Jobber.Resource('http://flickr.com/api/photos/1').then (res) -> | |
res.body.photos | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment