This is an automated email from the ASF dual-hosted git repository. rubys pushed a commit to branch master in repository https://git-dual.apache.org/repos/asf/whimsy.git
commit 62cd170e7674e6cac59ec84cd51aae4529316252 Author: Sam Ruby <ru...@intertwingly.net> AuthorDate: Wed Jul 27 13:42:59 2016 -0400 rough in service workers; disabled for now --- www/board/agenda/routes.rb | 1 - www/board/agenda/views/app.js.rb | 1 + www/board/agenda/views/layout/main.js.rb | 19 ++++++- www/board/agenda/views/models/agenda.js.rb | 7 +-- www/board/agenda/views/models/events.js.rb | 4 -- www/board/agenda/views/models/jsonstorage.js.rb | 15 +++--- www/board/agenda/views/models/pagecache.js.rb | 35 ++++++++++--- www/board/agenda/views/pages/bootstrap.js.rb | 9 ++++ www/board/agenda/views/pages/cache.js.rb | 67 +++++++++++++++++++++++++ www/board/agenda/views/router.js.rb | 3 ++ www/board/agenda/views/sw.js.rb | 67 +++++++++++++++++++++++++ 11 files changed, 201 insertions(+), 27 deletions(-) diff --git a/www/board/agenda/routes.rb b/www/board/agenda/routes.rb index 15f1fa8..a980a89 100755 --- a/www/board/agenda/routes.rb +++ b/www/board/agenda/routes.rb @@ -115,7 +115,6 @@ get %r{/(\d\d\d\d-\d\d-\d\d)/(.*)} do |date, path| @cssmtime = File.mtime('public/stylesheets/app.css').to_i if path == 'bootstrap.html' - @page[:path] = '' @page[:parsed] = [@page[:parsed].first] @page[:digest] = nil @page[:etag] = nil diff --git a/www/board/agenda/views/app.js.rb b/www/board/agenda/views/app.js.rb index 9242ef3..26095b6 100644 --- a/www/board/agenda/views/app.js.rb +++ b/www/board/agenda/views/app.js.rb @@ -10,6 +10,7 @@ require_relative 'layout/footer' # Individual pages require_relative 'pages/adjournment' +require_relative 'pages/bootstrap' require_relative 'pages/index' require_relative 'pages/report' require_relative 'pages/action-items' diff --git a/www/board/agenda/views/layout/main.js.rb b/www/board/agenda/views/layout/main.js.rb index 4a993fe..0a593b7 100644 --- a/www/board/agenda/views/layout/main.js.rb +++ b/www/board/agenda/views/layout/main.js.rb @@ -46,6 +46,7 @@ class Main < React Agenda.load(@@page.parsed, @@page.digest) Minutes.load(@@page.minutes) + self.route(@@page.path, @@page.query) # free memory @@ -92,7 +93,15 @@ class Main < React # store initial state in history, taking care not to overwrite # history set by the Search component. if not history.state or not history.state.query - history.replaceState({path: @@page.path}, nil, @@page.path) + path = @@page.path + + if path == 'bootstrap.html' + path = document.location.href + base = document.getElementsByTagName('base')[0].href + path = path.slice(base.length) if path.start_with? base + end + + history.replaceState({path: path}, nil, path) end # listen for back button, and re-route/re-render when it occcurs @@ -144,7 +153,13 @@ class Main < React Agenda.fetch(@@page.etag, @@page.digest) # start backchannel - Events.monitor() + if PageCache.enabled + # use Service Workers + PageCache.register() + else + # use localStorage + Events.monitor() + end end # after each subsequent re-rendering, resize main window diff --git a/www/board/agenda/views/models/agenda.js.rb b/www/board/agenda/views/models/agenda.js.rb index 49a9350..519362d 100644 --- a/www/board/agenda/views/models/agenda.js.rb +++ b/www/board/agenda/views/models/agenda.js.rb @@ -64,7 +64,7 @@ class Agenda # if bootstrapping and cache is available, load it if not digest caches.open('board/agenda').then do |cache| - cache.match("#{@@date}.json").then do |response| + cache.match("../#{@@date}.json").then do |response| if response response.json().then do |json| Agenda.load(json) unless loaded @@ -78,9 +78,10 @@ class Agenda # set fetch options: credentials and etag options = {credentials: 'include'} options['headers'] = {'If-None-Match' => @@etag} if @@etag + request = Request.new("../#{@@date}.json", options) # perform fetch - fetch("../#{@@date}.json", options).then do |response| + fetch(request).then do |response| if response loaded = true @@ -93,7 +94,7 @@ class Agenda # save response in the cache caches.open('board/agenda').then do |cache| - cache.put("#{@@date}.json", response) + cache.put(request, response) end end end diff --git a/www/board/agenda/views/models/events.js.rb b/www/board/agenda/views/models/events.js.rb index 8d20572..351b8ae 100644 --- a/www/board/agenda/views/models/events.js.rb +++ b/www/board/agenda/views/models/events.js.rb @@ -40,10 +40,6 @@ class Events end def self.monitor() - window.addEventListener :load do |event| - PageCache.preload() - end - @@prefix = JSONStorage.prefix # pick something unique to identify this tab/window diff --git a/www/board/agenda/views/models/jsonstorage.js.rb b/www/board/agenda/views/models/jsonstorage.js.rb index 760356c..94e3f48 100644 --- a/www/board/agenda/views/models/jsonstorage.js.rb +++ b/www/board/agenda/views/models/jsonstorage.js.rb @@ -54,16 +54,13 @@ class JSONStorage fetched = nil clock_counter += 1 - # construct arguments to fetch - args = { - method: 'get', - credentials: 'include', - headers: {'Accept' => 'application/json'}, - } + # construct request + request = Request.new("../json/#{name}", method: 'get', + credentials: 'include', headers: {'Accept' => 'application/json'}) # dispatch request - fetch("../json/#{name}", args).then do |response| - cache.put(name, response.clone()) + fetch(request).then do |response| + cache.put(request, response.clone()) response.json().then do |json| unless fetched and fetched.inspect == json.inspect @@ -76,7 +73,7 @@ class JSONStorage end # check cache - cache.match(name).then do |response| + cache.match("../json/#{name}").then do |response| if response and not fetched response.json().then do |json| clock_counter -= 1 diff --git a/www/board/agenda/views/models/pagecache.js.rb b/www/board/agenda/views/models/pagecache.js.rb index a41779b..44a9ee5 100644 --- a/www/board/agenda/views/models/pagecache.js.rb +++ b/www/board/agenda/views/models/pagecache.js.rb @@ -11,11 +11,32 @@ class PageCache # is page cache available? def self.enabled + # disable service workers for now. See: + # https://lists.w3.org/Archives/Public/public-webapps/2016JulSep/0016.html + return false + unless location.protocol == 'https:' or location.hostname == 'localhost' return false end - return defined? ServiceWorker + defined?(ServiceWorker) and defined?(navigator) + end + + # registration and related startup actions + def self.register() + # preload page cache once page finishes loading + window.addEventListener :load do |event| + PageCache.preload() + end + + # register service worker + scope = URL.new('..', document.getElementsByTagName('base')[0].href) + navigator.serviceWorker.register(scope + 'sw.js', scope) + + # listen for events + navigator.serviceWorker.addEventListener :message do |event| + Events.dispatch event + end end # fetch and cache page and referenced scripts and stylesheets @@ -23,9 +44,9 @@ class PageCache return unless PageCache.enabled? base = document.getElementsByTagName('base')[0].href - fetch_options = {credentials: 'include'} + request = Request.new('bootstrap.html', credentials: 'include') - fetch('bootstrap.html', fetch_options).then do |response| + fetch(request).then do |response| response.clone().text().then do |text| urls = [] @@ -48,13 +69,11 @@ class PageCache # add bootstrap.html and each URL to the cache. caches.open('board/agenda').then do |cache| agenda = Agenda.file[/\d+_\d+_\d+/].gsub('_', '-') - cache.put("#{agenda}.html", response) + cache.put(request, response) urls.each do |url| - fetch(url.pathname, fetch_options).then do |response| - basename = url.pathname.split('/').pop().split('?')[0] - cache.put(basename, response) - end + request2 = Request.new(url, credentials: 'include') + fetch(request2).then {|response| cache.put(request2, response)} end end end diff --git a/www/board/agenda/views/pages/bootstrap.js.rb b/www/board/agenda/views/pages/bootstrap.js.rb new file mode 100644 index 0000000..202c0e9 --- /dev/null +++ b/www/board/agenda/views/pages/bootstrap.js.rb @@ -0,0 +1,9 @@ +# +# Blank canvas shown during bootstrapping +# + +class BootStrapPage < React + def render + _p '' + end +end diff --git a/www/board/agenda/views/pages/cache.js.rb b/www/board/agenda/views/pages/cache.js.rb index c94c8e6..8f04b7b 100644 --- a/www/board/agenda/views/pages/cache.js.rb +++ b/www/board/agenda/views/pages/cache.js.rb @@ -3,6 +3,10 @@ # class CacheStatus < React + def self.buttons() + return [{button: ClearCache}, {button: UnregisterWorker}] + end + def initialize @cache = [] @registrations = [] @@ -86,6 +90,69 @@ class CacheStatus < React end # +# A button that clear the cache +# +class ClearCache < React + def initialize + @disabled = true + end + + def render + _button.btn.btn_primary 'Clear Cache', onClick: self.click, + disabled: @disabled + end + + # update on first update + def componentDidMount() + self.componentWillReceiveProps() + end + + # enable button if there is anything in the cache + def componentWillReceiveProps() + if defined? caches + caches.open('board/agenda').then do |cache| + cache.matchAll().then do |responses| + @disabled = responses.empty? + end + end + end + end + + def click(event) + if defined? caches + caches.delete('board/agenda').then do |status| + Main.refresh() + end + end + end +end + +# +# A button that removes the service worker. Sadly, it doesn't seem to have +# any affect on the list of registrations that is dynamically returned. +# +class UnregisterWorker < React + def render + _button.btn.btn_primary 'Unregister ServiceWorker', onClick: self.click + end + + def click(event) + if defined? caches + navigator.serviceWorker.getRegistrations().then do |registrations| + base = URL.new('..', document.getElementsByTagName('base')[0].href).href + registrations.each do |registration| + if registration.scope == base + registration.unregister().then do |status| + Main.refresh() + end + end + end + end + end + end +end + +# # Individual Cache page # diff --git a/www/board/agenda/views/router.js.rb b/www/board/agenda/views/router.js.rb index 4e1c28f..3aa1a14 100644 --- a/www/board/agenda/views/router.js.rb +++ b/www/board/agenda/views/router.js.rb @@ -75,6 +75,9 @@ class Router elsif path == 'help' item = {view: Help} + elsif path == 'bootstrap.html' + item = {view: BootStrapPage, title: ' '} + elsif path == 'cache/' item = {view: CacheStatus} diff --git a/www/board/agenda/views/sw.js.rb b/www/board/agenda/views/sw.js.rb new file mode 100644 index 0000000..b69fcc1 --- /dev/null +++ b/www/board/agenda/views/sw.js.rb @@ -0,0 +1,67 @@ +# +# A very simple service worker +# +# 1) Return back cached bootstrap page instead of fetching agenda pages +# from the network. Bootstrap will construct page from cached +# agenda.json, as well as updating the cache. +# +# 2) For all other pages, serve cached content when offline +# + +# polyfill if necessary +window = self +importScripts 'assets/eventsource.min.js' unless defined? EventSource + +events = nil + +self.addEventListener :activate do |event| + # close any pre-existing event socket + if events + begin + events.close() + rescue => e + events = nil + end + end + + # create a new event source + events = EventSource.new('events') + + # dispatch any events received to clients + events.addEventListener :message do |event| + clients.matchAll().then do |list| + list.each do |client| + client.postMessage(event.data) + end + end + end +end + +self.addEventListener :fetch do |event| + scope = self.registration.scope + url = event.request.url + url = url.slice(scope.length) if url.start_with? scope + + if url =~ %r{^\d\d\d\d-\d\d-\d\d/} and event.request.method == 'GET' + event.respondWith( + caches.open('board/agenda').then do |cache| + date = url.split('/')[0] + return cache.match("#{date}/bootstrap.html").then do |response| + console.log response + return response || fetch(event.request.url, credentials: 'include') + end + end + ) + else + event.respondWith( + fetch(event.request, credentials: 'include').catch do |error| + caches.open('board/agenda').then do |cache| + return cache.match(event.request.url) do |response| + console.log response + return response || error + end + end + end + ) + end +end -- To stop receiving notification emails like this one, please contact "commits@whimsical.apache.org" <commits@whimsical.apache.org>.