add offline availability with service worker and appcache
This commit is contained in:
parent
8616d79aad
commit
d8696bea1d
4 changed files with 132 additions and 39 deletions
BIN
icon.png
Normal file
BIN
icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
35
index.html
35
index.html
|
@ -4,12 +4,45 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
<meta name="viewport" content="width=device-width, user-scalable=no">
|
||||||
<meta name="application-name" content="Brawl Chat"/>
|
<meta name="application-name" content="Brawl Chat"/>
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||||
|
<meta name="apple-mobile-web-app-title" content="Brawl Chat">
|
||||||
|
<meta name="description" content="A matrix chat application">
|
||||||
<link rel="stylesheet" type="text/css" href="src/ui/web/css/main.css">
|
<link rel="stylesheet" type="text/css" href="src/ui/web/css/main.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<script type="module">
|
<script id="phone-debug-pre" type="disabled">
|
||||||
|
window.DEBUG = true;
|
||||||
|
window.debugConsoleBuffer = "";
|
||||||
|
console.error = (...params) => {
|
||||||
|
const lastLines = "...\n" + window.debugConsoleBuffer.split("\n").slice(-10).join("\n");
|
||||||
|
// window.debugConsoleBuffer = window.debugConsoleBuffer + "ERR " + params.join(" ") + "\n";
|
||||||
|
// const location = new Error().stack.split("\n")[2];
|
||||||
|
alert(params.join(" ") +"\n...\n" + lastLines);
|
||||||
|
};
|
||||||
|
console.log = console.info = console.warn = (...params) => {
|
||||||
|
window.debugConsoleBuffer = window.debugConsoleBuffer + params.join(" ") + "\n";
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<script id="main" type="module">
|
||||||
import main from "./src/main.js";
|
import main from "./src/main.js";
|
||||||
main(document.body);
|
main(document.body);
|
||||||
</script>
|
</script>
|
||||||
|
<script id="phone-debug-post" type="disabled">
|
||||||
|
setTimeout(() => {
|
||||||
|
const showlogs = document.getElementById("showlogs");
|
||||||
|
showlogs.addEventListener("click", () => {
|
||||||
|
const lastLines = "...\n" + window.debugConsoleBuffer.split("\n").slice(-20).join("\n");
|
||||||
|
alert(lastLines);
|
||||||
|
}, true);
|
||||||
|
showlogs.innerText = "Show last 20 log lines";
|
||||||
|
}, 5000);
|
||||||
|
</script>
|
||||||
|
<script id="service-worker" type="disabled">
|
||||||
|
if('serviceWorker' in navigator) {
|
||||||
|
navigator.serviceWorker.register('sw.js')
|
||||||
|
.then(function() { console.log("Service Worker registered"); });
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -14,30 +14,7 @@ const __dirname = dirname(__filename);
|
||||||
const projectDir = path.join(__dirname, "../");
|
const projectDir = path.join(__dirname, "../");
|
||||||
const targetDir = path.join(projectDir, "target");
|
const targetDir = path.join(projectDir, "target");
|
||||||
|
|
||||||
const jsPostfix = `
|
const debug = true;
|
||||||
window.DEBUG = true;
|
|
||||||
let buf = "";
|
|
||||||
console.error = (...params) => {
|
|
||||||
const lastLines = "...\\n" + buf.split("\\n").slice(-10).join("\\n");
|
|
||||||
// buf = buf + "ERR " + params.join(" ") + "\\n";
|
|
||||||
// const location = new Error().stack.split("\\n")[2];
|
|
||||||
alert(params.join(" ") +"\\n...\\n" + lastLines);
|
|
||||||
};
|
|
||||||
console.log = console.info = console.warn = (...params) => {
|
|
||||||
buf = buf + params.join(" ") + "\\n";
|
|
||||||
};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const jsSuffix = `
|
|
||||||
setTimeout(() => {
|
|
||||||
const showlogs = document.getElementById("showlogs");
|
|
||||||
showlogs.addEventListener("click", () => {
|
|
||||||
const lastLines = "...\\n" + buf.split("\\n").slice(-20).join("\\n");
|
|
||||||
alert(lastLines);
|
|
||||||
}, true);
|
|
||||||
showlogs.innerText = "Show last 20 log lines";
|
|
||||||
}, 10000);
|
|
||||||
`;
|
|
||||||
|
|
||||||
async function build() {
|
async function build() {
|
||||||
// get version number
|
// get version number
|
||||||
|
@ -45,15 +22,32 @@ async function build() {
|
||||||
// clear target dir
|
// clear target dir
|
||||||
await removeDirIfExists(targetDir);
|
await removeDirIfExists(targetDir);
|
||||||
await fs.mkdir(targetDir);
|
await fs.mkdir(targetDir);
|
||||||
|
|
||||||
|
await buildHtml();
|
||||||
|
await buildJs();
|
||||||
|
await buildCss();
|
||||||
|
await buildOffline(version);
|
||||||
|
|
||||||
|
console.log(`built brawl ${version} successfully`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildHtml() {
|
||||||
// transform html file
|
// transform html file
|
||||||
const devHtml = await fs.readFile(path.join(projectDir, "index.html"), "utf8");
|
const devHtml = await fs.readFile(path.join(projectDir, "index.html"), "utf8");
|
||||||
const doc = cheerio.load(devHtml);
|
const doc = cheerio.load(devHtml);
|
||||||
doc("link[rel=stylesheet]").attr("href", "brawl.css");
|
doc("link[rel=stylesheet]").attr("href", "brawl.css");
|
||||||
// doc("html").attr("manifest", "manifest.appcache");
|
doc("script#main").replaceWith(
|
||||||
doc("script").replaceWith(
|
|
||||||
`<script type="text/javascript" src="brawl.js"></script>` +
|
`<script type="text/javascript" src="brawl.js"></script>` +
|
||||||
`<script type="text/javascript">${jsPostfix} main(document.body); ${jsSuffix}</script>`);
|
`<script type="text/javascript">main(document.body);</script>`);
|
||||||
|
removeOrEnableScript(doc("script#phone-debug-pre"), debug);
|
||||||
|
removeOrEnableScript(doc("script#phone-debug-post"), debug);
|
||||||
|
removeOrEnableScript(doc("script#service-worker"), false);
|
||||||
|
doc("html").attr("manifest", "manifest.appcache");
|
||||||
|
doc("head").append(`<link rel="manifest" href="manifest.json">`);
|
||||||
await fs.writeFile(path.join(targetDir, "index.html"), doc.html(), "utf8");
|
await fs.writeFile(path.join(targetDir, "index.html"), doc.html(), "utf8");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildJs() {
|
||||||
// create js bundle
|
// create js bundle
|
||||||
const rollupConfig = {
|
const rollupConfig = {
|
||||||
input: 'src/main.js',
|
input: 'src/main.js',
|
||||||
|
@ -65,23 +59,58 @@ async function build() {
|
||||||
};
|
};
|
||||||
const bundle = await rollup.rollup(rollupConfig);
|
const bundle = await rollup.rollup(rollupConfig);
|
||||||
await bundle.write(rollupConfig);
|
await bundle.write(rollupConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildOffline(version) {
|
||||||
|
// write offline availability
|
||||||
|
const offlineFiles = ["brawl.js", "brawl.css", "index.html", "icon-192.png"];
|
||||||
|
|
||||||
|
// write appcache manifest
|
||||||
|
const manifestLines = [
|
||||||
|
`CACHE MANIFEST`,
|
||||||
|
`# v${version}`,
|
||||||
|
`NETWORK`,
|
||||||
|
`"*"`,
|
||||||
|
`CACHE`,
|
||||||
|
];
|
||||||
|
manifestLines.push(...offlineFiles);
|
||||||
|
const manifest = manifestLines.join("\n") + "\n";
|
||||||
|
await fs.writeFile(path.join(targetDir, "manifest.appcache"), manifest, "utf8");
|
||||||
|
// write service worker
|
||||||
|
let swSource = await fs.readFile(path.join(projectDir, "src/service-worker.template.js"), "utf8");
|
||||||
|
swSource = swSource.replace(`"%%VERSION%%"`, `"${version}"`);
|
||||||
|
swSource = swSource.replace(`"%%FILES%%"`, JSON.stringify(offlineFiles));
|
||||||
|
await fs.writeFile(path.join(targetDir, "sw.js"), swSource, "utf8");
|
||||||
|
// write web manifest
|
||||||
|
const webManifest = {
|
||||||
|
name: "Brawl Chat",
|
||||||
|
short_name: "Brawl",
|
||||||
|
display: "standalone",
|
||||||
|
start_url: "index.html",
|
||||||
|
icons: [{"src": "icon-192.png", "sizes": "192x192", "type": "image/png"}],
|
||||||
|
};
|
||||||
|
await fs.writeFile(path.join(targetDir, "manifest.json"), JSON.stringify(webManifest), "utf8");
|
||||||
|
// copy icon
|
||||||
|
let icon = await fs.readFile(path.join(projectDir, "icon.png"));
|
||||||
|
await fs.writeFile(path.join(targetDir, "icon-192.png"), icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildCss() {
|
||||||
// create css bundle
|
// create css bundle
|
||||||
const cssMainFile = path.join(projectDir, "src/ui/web/css/main.css");
|
const cssMainFile = path.join(projectDir, "src/ui/web/css/main.css");
|
||||||
const preCss = await fs.readFile(cssMainFile, "utf8");
|
const preCss = await fs.readFile(cssMainFile, "utf8");
|
||||||
const cssBundler = postcss([postcssImport]);
|
const cssBundler = postcss([postcssImport]);
|
||||||
const postCss = await cssBundler.process(preCss, {from: cssMainFile});
|
const postCss = await cssBundler.process(preCss, {from: cssMainFile});
|
||||||
await fs.writeFile(path.join(targetDir, "brawl.css"), postCss, "utf8");
|
await fs.writeFile(path.join(targetDir, "brawl.css"), postCss, "utf8");
|
||||||
// write appcache manifest
|
}
|
||||||
const manifestLines = [
|
|
||||||
`CACHE MANIFEST`,
|
|
||||||
`# v${version}`,
|
function removeOrEnableScript(scriptNode, enable) {
|
||||||
"brawl.js",
|
if (enable) {
|
||||||
"brawl.css",
|
scriptNode.attr("type", "text/javascript");
|
||||||
"index.html",
|
} else {
|
||||||
""
|
scriptNode.remove();
|
||||||
];
|
}
|
||||||
await fs.writeFile(path.join(targetDir, "manifest.appcache"), manifestLines.join("\n"), "utf8");
|
|
||||||
console.log(`built brawl ${version} successfully`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeDirIfExists(targetDir) {
|
async function removeDirIfExists(targetDir) {
|
||||||
|
|
31
src/service-worker.template.js
Normal file
31
src/service-worker.template.js
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
const VERSION = "%%VERSION%%";
|
||||||
|
const FILES = "%%FILES%%";
|
||||||
|
const cacheName = `brawl-${VERSION}`;
|
||||||
|
|
||||||
|
self.addEventListener('install', function(e) {
|
||||||
|
e.waitUntil(
|
||||||
|
caches.open(cacheName).then(function(cache) {
|
||||||
|
return cache.addAll(FILES);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener('activate', (event) => {
|
||||||
|
event.waitUntil(
|
||||||
|
caches.keys().then((keyList) => {
|
||||||
|
return Promise.all(keyList.map((key) => {
|
||||||
|
if (key !== cacheName) {
|
||||||
|
return caches.delete(key);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
self.addEventListener('fetch', (event) => {
|
||||||
|
event.respondWith(
|
||||||
|
caches.open(cacheName)
|
||||||
|
.then(cache => cache.match(event.request))
|
||||||
|
.then((response) => response || fetch(event.request))
|
||||||
|
);
|
||||||
|
});
|
Reference in a new issue