Offline first

GPRS‽

Не ловит‽

;)

Альтернативы
не очень

Но как узнать,
что пользователь
offline?

navigator.onLine
// true | false
if(navigator.onLine){
    // …
}else{
    // …
}
window.addEventListener('online', callback);
window.document.addEventListener('online', callback);
window.document.body.addEventListener('online', callback);

window.addEventListener('offline', callback);
window.document.addEventListener('offline', callback);
window.document.body.addEventListener('offline', callback);

Facebook
так тоже может

Более того…

var form = $('#comment-form'),
	   messages = [];
form.on('submit', function (event) {
    event.preventDefault();
    if(navigator.onLine){
        $.post(
		form.attr('action'), 
		form.serialize()
	);
    }else{
        messages.push(form.serialize());
    }
    form[0].reset();
});
$(window).on('online', function () {
    while(messages.length>0){
        $.post(
		form.attr('action'), 
		messages.shift()
	);
    }
});

Offline storage

var form = $('#comment-form'),
    messages = JSON.parse(localStorage.getItem('messages')) || [];

form.on('submit', function (event) {
    event.preventDefault();
    if(navigator.onLine){
        $.post(form.attr('action'), form.serialize());
    }else{
        messages.push(form.serialize());
        localStorage.setItem('messages',
            JSON.stringify(messages));
    }
    form[0].reset();
});
if(navigator.onLine){
    while(messages.length>0){
        $.post(form.attr('action'), messages.shift());
    }
    localStorage.removeItem('messages');
}

$(window).on('online', function () {
    while(messages.length>0){
        $.post(form.attr('action'), messages.shift());
    }
    localStorage.removeItem('messages');
});

Оffline.js

bower install --save offline
npm install --save offline-js
<link
 rel="stylesheet"
 href="themes/offline-theme-chrome.css">
<link
 rel="stylesheet"
 href="themes/offline-language-english.css">
<script src="offline.min.js"></script>
Offline.options = {
	checks: {…},
	checkmOnLoad: true,
	interceptRequests: true,
	requests: true,
	reconnect: {…}
	};
Offline.check();
Offline.state;
Offline.on(
	event,
	handler,
	context);
Offline.off(
	event,
	handler);
up
down
confirmed-up
confirmed-down
checking
…

Offline.js

QRCODE cо ссылкой на Offline.js

goo.gl/HpvLgT

Offline.js пример

QRCODE cо ссылкой на offlinejs Пример

goo.gl/wAT4rk

npm install -g hoodie-cli
hoodie new
cd my-first-hoodie/
hoodie start
<script src="jquery.min.js"></script>
<script src="/_api/_files/hoodie.js"></script>
hoodie
hoodie.account
hoodie.store
hoodie.account
	.signUp(username, password)
	.done(success_callback)
	.fail(fail_callback);
hoodie.account
	.signIn(username, password)
	.done(success_callback)
	.fail(fail_callback);

HOODIE.JS

QRCODE cо ссылкой на Hoodie

hood.ie

HOODIE Slack

QRCODE cо ссылкой на Hoodie Slack

hood.ie/chat/

HOODIE.JS пример

QRCODE cо ссылкой на Hoodie пример

goo.gl/yH5xJp

Swarm.js

npm install --save swarm

server.js

let swarm = require('swarm'),
    Text = require('swarm/lib/Text'),
    fs = new swarm.FileStorage('id'),
    host = new swarm.Host(
	'swarm~nodejs',
	0,
	fs
    ),
    app = new Text('APP');
ws_server.on('connection', function (ws) {
	host.accept(
		new swarm.EinarosWSStream(ws),
		{delay: 50}
	);
});

client.js

let swarm = require('swarm'),
  Text = require('swarm/lib/Text'),
  host = new swarm.Host('id');

this.model = new Text('APP');
this.model.on(
  this.eventMonitor.bind(this));
host.connect(
  'ws://127.0.0.1:8000/');
window.addEventListener(
  'onbeforeunload',
  this.turnOff.bind(this));
eventMonitor (spec, val, source) {
  if (spec.op() == 'init') {
    // Инициализировался
  } else {
    // Изменился
  }
}
this.model.text
this.model.set(value);
host.close();

Swarm.js пример

QRCODE cо ссылкой на пример Swarm.js

goo.gl/S8odgW

Service Workers

client.js

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register(
    'worker.js',
    {scope: '/'})
  .then(success)
  .catch(error);
}

worker.js

// События

install
activate
fetch
message

Перехват запросов

this.addEventListener(
	'fetch',
	function(event) {});
event.request
event.request.url
event.respondWith(
	new Response('Owned!')
);

Сохранение в кэш

caches.open('id')
  .then(function(cache) {
    return cache.addAll([…]);
  });
event.respondWith(
  caches.open('id')
    .then(function(cache) {
      return cache.match(event.request)
       .then(function(response) {…}
     });
  });
if (response) {
  return response;
} else {
  cache.add(event.request.url);
  return fetch(event.request);
}

Cообщения

// client.js

let message = {
  'command': 'flush'
};
navigator
  .serviceWorker
  .controller
  .postMessage(message);
// worker.js

switch (event.data.command) {
  case 'flush':
    return caches.delete('id');
    break;
}

Отладка

// Chrome
chrome://inspect/#service-workers

// Opera
browser://serviceworker-internals

// Firefox
about:serviceworkers

Service Workers

QRCODE cо ссылкой на спецификацию Service Workers

goo.gl/3LeXLw

Service Workers пример

QRCODE cо ссылкой на пример Service Workers

goo.gl/TRlRi3

Offline
First
Community

QRCODE cо ссылкой на Slack

offlinefirst.org/chat

Offline first
не тортик

Offline first 
вишенка

Вас таких много
стоит выделиться

Не будьте буками!

Сделайте
пользователю
приятно

Антон Немцев

skype: ravencry

QRCODE cо ссылкой на доклад

goo.gl/lTV0bL