miniflux/ui/static/static.go
Frédéric Guillot 8a812cd8ec Add basic PWA offline page
- Remove feed_icons cache because it's causing more problems that it solve.
- Add basic offline mode when using the service worker.
- Starting in Chrome 93, offline mode is going to be a requirement to install the PWA.

https://developer.chrome.com/blog/improved-pwa-offline-detection/#enforcement-starting-chrome-93-august-2021
2021-03-07 15:44:42 -08:00

173 lines
4.2 KiB
Go

// Copyright 2021 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
package static // import "miniflux.app/ui/static"
import (
"bytes"
"crypto/sha256"
"embed"
"fmt"
"github.com/tdewolff/minify/v2"
"github.com/tdewolff/minify/v2/css"
"github.com/tdewolff/minify/v2/js"
)
// Static assets.
var (
StylesheetBundleChecksums map[string]string
StylesheetBundles map[string][]byte
JavascriptBundleChecksums map[string]string
JavascriptBundles map[string][]byte
)
//go:embed bin/*
var binaryFiles embed.FS
//go:embed css/*.css
var stylesheetFiles embed.FS
//go:embed js/*.js
var javascriptFiles embed.FS
var binaryFileChecksums map[string]string
// CalculateBinaryFileChecksums generates hash of embed binary files.
func CalculateBinaryFileChecksums() error {
binaryFileChecksums = make(map[string]string)
dirEntries, err := binaryFiles.ReadDir("bin")
if err != nil {
return err
}
for _, dirEntry := range dirEntries {
data, err := LoadBinaryFile(dirEntry.Name())
if err != nil {
return err
}
binaryFileChecksums[dirEntry.Name()] = fmt.Sprintf("%x", sha256.Sum256(data))
}
return nil
}
// LoadBinaryFile loads an embed binary file.
func LoadBinaryFile(filename string) ([]byte, error) {
return binaryFiles.ReadFile(fmt.Sprintf(`bin/%s`, filename))
}
// GetBinaryFileChecksum returns a binary file checksum.
func GetBinaryFileChecksum(filename string) (string, error) {
if _, found := binaryFileChecksums[filename]; !found {
return "", fmt.Errorf(`static: unable to find checksum for %q`, filename)
}
return binaryFileChecksums[filename], nil
}
// GenerateStylesheetsBundles creates CSS bundles.
func GenerateStylesheetsBundles() error {
var bundles = map[string][]string{
"light_serif": {"css/light.css", "css/serif.css", "css/common.css"},
"light_sans_serif": {"css/light.css", "css/sans_serif.css", "css/common.css"},
"dark_serif": {"css/dark.css", "css/serif.css", "css/common.css"},
"dark_sans_serif": {"css/dark.css", "css/sans_serif.css", "css/common.css"},
"system_serif": {"css/system.css", "css/serif.css", "css/common.css"},
"system_sans_serif": {"css/system.css", "css/sans_serif.css", "css/common.css"},
}
StylesheetBundles = make(map[string][]byte)
StylesheetBundleChecksums = make(map[string]string)
minifier := minify.New()
minifier.AddFunc("text/css", css.Minify)
for bundle, srcFiles := range bundles {
var buffer bytes.Buffer
for _, srcFile := range srcFiles {
fileData, err := stylesheetFiles.ReadFile(srcFile)
if err != nil {
return err
}
buffer.Write(fileData)
}
minifiedData, err := minifier.Bytes("text/css", buffer.Bytes())
if err != nil {
return err
}
StylesheetBundles[bundle] = minifiedData
StylesheetBundleChecksums[bundle] = fmt.Sprintf("%x", sha256.Sum256(minifiedData))
}
return nil
}
// GenerateJavascriptBundles creates JS bundles.
func GenerateJavascriptBundles() error {
var bundles = map[string][]string{
"app": {
"js/dom_helper.js",
"js/touch_handler.js",
"js/keyboard_handler.js",
"js/request_builder.js",
"js/modal_handler.js",
"js/app.js",
"js/bootstrap.js",
},
"service-worker": {
"js/service_worker.js",
},
}
var prefixes = map[string]string{
"app": "(function(){'use strict';",
}
var suffixes = map[string]string{
"app": "})();",
}
JavascriptBundles = make(map[string][]byte)
JavascriptBundleChecksums = make(map[string]string)
minifier := minify.New()
minifier.AddFunc("text/javascript", js.Minify)
for bundle, srcFiles := range bundles {
var buffer bytes.Buffer
if prefix, found := prefixes[bundle]; found {
buffer.WriteString(prefix)
}
for _, srcFile := range srcFiles {
fileData, err := javascriptFiles.ReadFile(srcFile)
if err != nil {
return err
}
buffer.Write(fileData)
}
if suffix, found := suffixes[bundle]; found {
buffer.WriteString(suffix)
}
minifiedData, err := minifier.Bytes("text/javascript", buffer.Bytes())
if err != nil {
return err
}
JavascriptBundles[bundle] = minifiedData
JavascriptBundleChecksums[bundle] = fmt.Sprintf("%x", sha256.Sum256(minifiedData))
}
return nil
}