| 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "flag" |
| 5 | "fmt" |
| 6 | "image" |
| 7 | "image/color" |
| 8 | "image/png" |
| 9 | "log" |
| 10 | "net" |
| 11 | "net/http" |
| 12 | "strconv" |
| 13 | |
| 14 | "amadan.net/rastrillo/aviso" |
| 15 | "amadan.net/rastrillo/pwa" |
| 16 | ) |
| 17 | |
| 18 | func main() { |
| 19 | addr := flag.String("addr", "127.0.0.1:8080", "listen address") |
| 20 | flag.Parse() |
| 21 | mux := http.NewServeMux() |
| 22 | manifest, err := (pwa.Manifest{ID: "/", Name: "PWA Example", StartURL: "/", Scope: "/", |
| 23 | ThemeColor: "#234d45", BackgroundColor: "#ffffff", |
| 24 | Icons: []pwa.Icon{ |
| 25 | {Src: "/icon/192", Sizes: "192x192", Type: "image/png"}, |
| 26 | {Src: "/icon/512", Sizes: "512x512", Type: "image/png"}, |
| 27 | }, |
| 28 | }).Handler() |
| 29 | if err != nil { |
| 30 | log.Fatal(err) |
| 31 | } |
| 32 | mux.Handle("/manifest.webmanifest", manifest) |
| 33 | mux.Handle("/pwa/", http.StripPrefix("/pwa", pwa.Assets())) |
| 34 | mux.HandleFunc("GET /aviso-sw.js", func(w http.ResponseWriter, r *http.Request) { |
| 35 | w.Header().Set("Content-Type", "text/javascript") |
| 36 | w.Header().Set("Cache-Control", "no-cache") |
| 37 | _, _ = w.Write(aviso.WorkerJS()) |
| 38 | }) |
| 39 | mux.HandleFunc("GET /sw.js", func(w http.ResponseWriter, r *http.Request) { |
| 40 | w.Header().Set("Content-Type", "text/javascript") |
| 41 | w.Header().Set("Cache-Control", "no-cache") |
| 42 | fmt.Fprint(w, `importScripts("/pwa/worker.js", "/aviso-sw.js"); |
| 43 | RastrilloPWA.install(); |
| 44 | self.addEventListener("push", e => e.waitUntil(AvisoSW.handlePush(e, { |
| 45 | fallback: () => ({title: "New activity", options: {data: {url: "/"}}}) |
| 46 | }))); |
| 47 | self.addEventListener("notificationclick", e => e.waitUntil(AvisoSW.handleClick(e, {fallbackURL: "/"}))); |
| 48 | `) |
| 49 | }) |
| 50 | mux.HandleFunc("GET /icon/{size}", func(w http.ResponseWriter, r *http.Request) { |
| 51 | size, _ := strconv.Atoi(r.PathValue("size")) |
| 52 | if size != 180 && size != 192 && size != 512 { |
| 53 | http.NotFound(w, r) |
| 54 | return |
| 55 | } |
| 56 | im := image.NewRGBA(image.Rect(0, 0, size, size)) |
| 57 | for y := 0; y < size; y++ { |
| 58 | for x := 0; x < size; x++ { |
| 59 | im.Set(x, y, color.RGBA{35, 77, 69, 255}) |
| 60 | } |
| 61 | } |
| 62 | w.Header().Set("Content-Type", "image/png") |
| 63 | _ = png.Encode(w, im) |
| 64 | }) |
| 65 | mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) { |
| 66 | w.Header().Set("Content-Type", "text/html; charset=utf-8") |
| 67 | w.Header().Set("Cache-Control", "no-store") |
| 68 | fmt.Fprint(w, `<!doctype html><html lang="en"><meta charset="utf-8"> |
| 69 | <meta name="viewport" content="width=device-width,initial-scale=1"> |
| 70 | <link rel="manifest" href="/manifest.webmanifest"><link rel="apple-touch-icon" href="/icon/180"> |
| 71 | <meta name="theme-color" content="#234d45"><title>PWA Example</title> |
| 72 | <main><h1>PWA Example</h1><p>Add this app to your Home Screen or use your browser's install option.</p> |
| 73 | <p>Open it once online. Then disconnect and reload to see the offline page.</p> |
| 74 | <label>Try an unsaved edit <input id="draft"></label> |
| 75 | <p id="update" hidden>An update is ready. Save your work in all tabs, then close and reopen the app.</p> |
| 76 | <script type="module"> |
| 77 | import {register} from "/pwa/client.mjs"; |
| 78 | register({onUpdate: () => {document.querySelector("#update").hidden = false;}}).catch(console.error); |
| 79 | </script></main></html>`) |
| 80 | }) |
| 81 | listener, err := net.Listen("tcp", *addr) |
| 82 | if err != nil { |
| 83 | log.Fatal(err) |
| 84 | } |
| 85 | fmt.Printf("http://%s\n", listener.Addr()) |
| 86 | log.Fatal(http.Serve(listener, mux)) |
| 87 | } |
| 88 | |