145 lines
5.8 KiB
Text
145 lines
5.8 KiB
Text
// Serviceworkers file. This code gets installed in users browsers and runs code before the request is made.
|
|
<% unless Rails.env.test? %>
|
|
const staticCacheName = 'static-1.2';
|
|
const expectedCaches = [
|
|
staticCacheName
|
|
];
|
|
|
|
self.addEventListener('install', event => {
|
|
self.skipWaiting();
|
|
|
|
// Populate initial serviceworker cache.
|
|
event.waitUntil(
|
|
caches.open(staticCacheName)
|
|
.then(cache => cache.addAll([
|
|
"/shell_top", // head, top bar, inline styles
|
|
"/shell_bottom", // footer
|
|
"/async_info/shell_version", // For comparing changes in the shell. Should be incremented with style changes.
|
|
"/404.html", // Not found page
|
|
"/500.html", // Error page
|
|
"/offline.html" //Offline page
|
|
]))
|
|
);
|
|
});
|
|
|
|
// remove caches that aren't in expectedCaches
|
|
self.addEventListener('activate', event => {
|
|
event.waitUntil(
|
|
caches.keys().then(keys => Promise.all(
|
|
keys.map(key => {
|
|
if (!expectedCaches.includes(key)) return caches.delete(key);
|
|
})
|
|
))
|
|
);
|
|
});
|
|
|
|
// Create a composed streamed webpage with shell and core content
|
|
function createPageStream(request) {
|
|
const stream = new ReadableStream({
|
|
start(controller) {
|
|
if (!caches.match('/shell_top') || !caches.match('/shell_bottom')) { //return if shell isn't cached.
|
|
return
|
|
}
|
|
|
|
// the body url is the request url plus 'include'
|
|
const url = new URL(request.url);
|
|
url.searchParams.set('i', 'i'); // Adds ?i=i or &i=i, which is our indicator for "internal" partial page
|
|
const startFetch = caches.match('/shell_top');
|
|
const endFetch = caches.match('/shell_bottom');
|
|
const middleFetch = fetch(url).then(response => {
|
|
if (!response.ok && response.status === 404) {
|
|
return caches.match('/404.html');
|
|
}
|
|
if (!response.ok && response.status != 404) {
|
|
return caches.match('/500.html');
|
|
}
|
|
return response;
|
|
}).catch(err => caches.match('/offline.html'));
|
|
|
|
function pushStream(stream) {
|
|
const reader = stream.getReader();
|
|
return reader.read().then(function process(result) {
|
|
if (result.done) return;
|
|
controller.enqueue(result.value);
|
|
return reader.read().then(process);
|
|
});
|
|
}
|
|
startFetch
|
|
.then(response => pushStream(response.body))
|
|
.then(() => middleFetch)
|
|
.then(response => pushStream(response.body))
|
|
.then(() => endFetch)
|
|
.then(response => pushStream(response.body))
|
|
.then(() => controller.close());
|
|
}
|
|
});
|
|
|
|
return new Response(stream, {
|
|
headers: {'Content-Type': 'text/html; charset=utf-8'}
|
|
});
|
|
}
|
|
|
|
self.addEventListener('fetch', event => {
|
|
const url = new URL(event.request.url);
|
|
if (/iPhone|CriOS|iPad/i.test(navigator.userAgent) && event.request.referrer.includes('t.co')) {
|
|
// Twitter on iOS seems to cause problems
|
|
return;
|
|
}
|
|
if (url.origin === location.origin) {
|
|
if (event.clientId === "" && // Not fetched via AJAX after page load.
|
|
event.request.method == "GET" && // Don't fetch on POST, DELETE, etc.
|
|
!event.request.referrer.includes('/signout_confirm') && // If this is the referrer, we instead want to flush.
|
|
!url.href.includes('i=i') && // Parameter representing "internal" navigation.
|
|
!url.href.includes('?signin') && // Don't run on sign in.
|
|
!url.href.includes('.js') && // Don't run on JS.
|
|
!url.href.includes('.css') && // Don't run on CSS.
|
|
!url.href.includes('/onboarding') && // Don't run on onboarding.
|
|
!url.href.includes('/api/') && // Don't run on API endpoints.
|
|
!url.href.includes('/users/auth') && // Don't run on authentication.
|
|
!url.href.includes('/shell_') && // Don't fetch for shell.
|
|
!url.href.includes('/admin') && // Don't fetch for administrate dashboard.
|
|
!url.href.includes('/internal') && // Don't fetch for internal dashboard.
|
|
!url.href.includes('/future') && // Skip for /future.
|
|
!url.href.includes('?preview=') && // Skip for /future.
|
|
!url.href.includes('/delayed_job') && // Skip for Delayed Job dashboard
|
|
!url.href.includes('/sidekiq') && // Skip for Sidekiq dashboard
|
|
caches.match('/shell_top') && // Ensure shell_top is in the cache.
|
|
caches.match('/shell_bottom')) { // Ensure shell_bottom is in the cache.
|
|
event.respondWith(createPageStream(event.request)); // Respond with the stream
|
|
|
|
// Ping version endpoint to see if we should fetch new shell.
|
|
if (!caches.match('/async_info/shell_version')) { // Check if we have a cached shell version
|
|
caches.open(staticCacheName)
|
|
.then(cache => cache.addAll([
|
|
"/async_info/shell_version",
|
|
]));
|
|
return;
|
|
}
|
|
|
|
fetch('/async_info/shell_version').then(response => response.json()).then(json => {
|
|
caches.match('/async_info/shell_version').then(cachedResponse => cachedResponse.json()).then(cacheJson => {
|
|
if (cacheJson['version'] != json['version']) {
|
|
caches.open(staticCacheName)
|
|
.then(cache => cache.addAll([
|
|
"/shell_top",
|
|
"/shell_bottom",
|
|
"/async_info/shell_version"
|
|
]));
|
|
}
|
|
})
|
|
})
|
|
return;
|
|
}
|
|
|
|
// Fetch new shell upon events that signify change in session.
|
|
if (event.clientId === "" &&
|
|
(event.request.referrer.includes('/signout_confirm') || url.href.includes('?signin') || url.href.includes('/onboarding'))) {
|
|
caches.open(staticCacheName)
|
|
.then(cache => cache.addAll([
|
|
"/shell_top",
|
|
"/shell_bottom",
|
|
]));
|
|
}
|
|
}
|
|
});
|
|
<% end %>
|