-
Notifications
You must be signed in to change notification settings - Fork 271
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
275 additions
and
1 deletion.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import sphinx as Sphinx | ||
from typing import Any, Dict, List | ||
import os | ||
from docutils import nodes | ||
import json | ||
import shutil | ||
from urllib.parse import urljoin, urlparse, urlunparse | ||
from sphinx.util import logging | ||
from sphinx.util.console import green, red, yellow # pylint: disable=no-name-in-module | ||
|
||
manifest = { | ||
"name": "", | ||
"short_name": "", | ||
"theme_color": "", | ||
"background_color": "", | ||
"display": "standalone", | ||
"scope": "/", | ||
"start_url": "/index.html", | ||
"icons": [], | ||
} | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
def get_files_to_cache(outDir: str, config: Dict[str, Any]): | ||
files_to_cache = [] | ||
for (dirpath, dirname, filenames) in os.walk(outDir): | ||
dirpath = dirpath.split(outDir)[1] | ||
|
||
# skip adding sources to cache | ||
if os.sep + "_sources" + os.sep in dirpath: | ||
continue | ||
|
||
# add files to cache | ||
for name in filenames: | ||
if "sw.js" in name: | ||
continue | ||
|
||
dirpath = dirpath.replace("\\", "/") | ||
dirpath = dirpath.lstrip("/") | ||
|
||
# we have to use absolute urls in our cache resource, because fetch will return an absolute url | ||
# this means that we cannot accurately cache resources that are in PRs because RTD does not give us | ||
# the url | ||
if config["html_baseurl"] is not None: | ||
# readthedocs uses html_baseurl for sphinx > 1.8 | ||
parse_result = urlparse(config["html_baseurl"]) | ||
|
||
# Grab root url from canonical url | ||
url = parse_result.netloc | ||
|
||
# enables RTD multilanguage support | ||
if os.getenv("READTHEDOCS"): | ||
url = "https://" + url + "/" + os.getenv("READTHEDOCS_LANGUAGE") + "/" + os.getenv("READTHEDOCS_VERSION") + "/" | ||
|
||
if config["html_baseurl"] is None and not os.getenv("CI"): | ||
logger.warning( | ||
red(f"html_baseurl is not configured. This can be ignored if deployed in RTD environments.") | ||
) | ||
url = "" | ||
|
||
if dirpath == "": | ||
resource_url = urljoin( | ||
url, name | ||
) | ||
files_to_cache.append(resource_url) | ||
else: | ||
resource_url = url + dirpath + "/" + name | ||
files_to_cache.append(resource_url) | ||
|
||
return files_to_cache | ||
|
||
|
||
def build_finished(app: Sphinx, exception: Exception): | ||
outDir = app.outdir | ||
outDirStatic = outDir + os.sep + "_static" + os.sep | ||
files_to_cache = get_files_to_cache(outDir, app.config) | ||
|
||
# dumps a json file with our cache | ||
with open(outDirStatic + "cache.json", "w") as f: | ||
json.dump(files_to_cache, f) | ||
|
||
# copies over our service worker | ||
shutil.copyfile( | ||
os.path.dirname(__file__) + os.sep + "pwa_service_files" + os.sep + "sw.js", | ||
outDir + os.sep + "sw.js", | ||
) | ||
|
||
|
||
def html_page_context( | ||
app: Sphinx, | ||
pagename: str, | ||
templatename: str, | ||
context: Dict[str, Any], | ||
doctree: nodes.document, | ||
) -> None: | ||
if pagename == "index": | ||
context[ | ||
"metatags" | ||
] += '<script>"serviceWorker"in navigator&&navigator.serviceWorker.register("sw.js").catch((e) => window.alert(e));</script>' | ||
context[ | ||
"metatags" | ||
] += f'<link rel="manifest" href="_static/frcdocs.webmanifest"/>' | ||
|
||
if app.config["pwa_apple_icon"] is not None: | ||
context[ | ||
"metatags" | ||
] += f'<link rel="apple-touch-icon" href="{app.config["pwa_apple_icon"]}">' | ||
|
||
|
||
def setup(app: Sphinx) -> Dict[str, Any]: | ||
app.add_config_value("pwa_name", "", "html") | ||
app.add_config_value("pwa_short_name", "", "html") | ||
app.add_config_value("pwa_theme_color", "", "html") | ||
app.add_config_value("pwa_background_color", "", "html") | ||
app.add_config_value("pwa_display", "standalone", "html") | ||
app.add_config_value("pwa_icons", [], "html") | ||
app.add_config_value("pwa_apple_icon", "", "html") | ||
|
||
app.connect("html-page-context", html_page_context) | ||
app.connect("build-finished", build_finished) | ||
|
||
return { | ||
"parallel_read_safe": True, | ||
"parallel_write_safe": True, | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
"use strict"; | ||
// extend this to update the service worker every push | ||
// https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Offline_Service_workers | ||
let cacheName = 'js13kPWA-v1'; | ||
|
||
// todo test | ||
self.addEventListener('install', function (e) { | ||
e.waitUntil(async function() { | ||
await fetch('_static/cache.json') | ||
.then(response => response.json()) | ||
.then(async (data) => { | ||
for (let i = 0; i < data.length / 10; i++) { | ||
const tofetch = data.slice(i * 10, i * 10 + 10); | ||
await addKeys(tofetch) | ||
} | ||
}) | ||
}()); | ||
}); | ||
|
||
// opt for a cache first response, for quickest load times | ||
// we'll still update the page assets in the background | ||
self.addEventListener('fetch', function (event) { | ||
event.respondWith(async function () { | ||
let request_url = event.request.url; | ||
|
||
try { | ||
await addKeys([request_url]) //put in format our addKeys function expects | ||
} catch (error) { | ||
console.error("Error downloading from remote:", error) | ||
} | ||
|
||
let res = await getKey(request_url) | ||
|
||
console.log("Fetching:", event.request.url) | ||
return res; | ||
}()); | ||
}); | ||
|
||
let dbPromise; | ||
|
||
async function getDB() { | ||
if (dbPromise) { | ||
return dbPromise; | ||
} else { | ||
let request = indexedDB.open("frc-docs", "1") | ||
|
||
dbPromise = new Promise((resolve, reject) => { | ||
request.onsuccess = function (event) { | ||
console.log("Successfully opened database!") | ||
resolve(event.target.result) | ||
} | ||
|
||
request.onerror = function (event) { | ||
console.error("Error opening database for getKey():", request.error) | ||
reject() | ||
} | ||
|
||
request.onupgradeneeded = function (event) { | ||
let db = event.target.result; | ||
db.createObjectStore("urls", { keyPath: 'key' }) | ||
} | ||
}); | ||
|
||
return dbPromise; | ||
} | ||
} | ||
|
||
async function getKey(key) { | ||
let db = await getDB() | ||
console.log("Grabbing key", key) | ||
return new Promise((resolve, reject) => { | ||
try { | ||
let transaction = db.transaction("urls").objectStore("urls"); | ||
let request = transaction.get(key) | ||
|
||
request.onsuccess = function (event) { | ||
let res = request.result; | ||
console.log("Successfully retrieved result:", res) | ||
resolve(new Response(res.value)); | ||
} | ||
|
||
request.onerror = function (event) { | ||
console.error("Error on retrieving blob:", key, request.error) | ||
reject() | ||
} | ||
|
||
} catch (ex) { | ||
console.error(ex.message); | ||
reject() | ||
} | ||
}) | ||
} | ||
|
||
async function addKeys(datas) { | ||
let db = await getDB() | ||
return Promise.all( | ||
datas.map(async (data) => { | ||
let fetchedData = await fetch(data) | ||
.then(x => x.blob()) | ||
.catch((error) => { | ||
console.error("Error fetching", data) | ||
return new Promise((resolve, reject) => { | ||
reject(); | ||
}) | ||
}) | ||
let transaction = db.transaction("urls", "readwrite").objectStore("urls") | ||
let request = transaction.put({key: data, value: fetchedData}) | ||
|
||
return new Promise((resolve, reject) => { | ||
request.onsuccess = function() { | ||
resolve() | ||
} | ||
request.onerror = function () { | ||
console.log(request.error) | ||
reject(request.error) | ||
} | ||
}); | ||
}) | ||
); | ||
// data is already a key/value object with url/data | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"name": "FRC Docs", | ||
"short_name": "FRC Docs", | ||
"theme_color": "#003974", | ||
"background_color": "#003974", | ||
"display": "standalone", | ||
"scope": "../", | ||
"start_url": "../index.html", | ||
"icons": [ | ||
{ | ||
"src": "/_static/first-logo-256px.png", | ||
"type": "image/png", | ||
"sizes": "256x256" | ||
}, | ||
{ | ||
"src": "/_static/first-logo-512px.png", | ||
"type": "image/png", | ||
"sizes": "512x512" | ||
} | ||
] | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains 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