maddalax / htmgo

htmgo - build simple and scalable systems with go + htmx
https://htmgo.dev
MIT License
741 stars 19 forks source link

needs a web manifest #89

Open gedw99 opened 19 hours ago

gedw99 commented 19 hours ago

My use case is for Deep Linking so that I can use this system on Web, Mobile and Desktop with a Webview, and A2HS for non WebViews scenarios so Users can easily add this to their Laptops and Mobiles.

This requires a Web Manifest: https://www.w3.org/TR/appmanifest/

I propose that https://github.com/maddalax/htmgo/tree/master/templates/starter/assets/public has these requires skeleton files.

Here is a basic generator for reference: https://github.com/aerogo/manifest but it's pretty basic IMHO. just a reference really.

Here is the Webview code: https://github.com/gioui-plugins/gio-plugins/tree/main/webviewer that I am testing with.

gedw99 commented 19 hours ago

Example html layout:


        <!-- Add a viewport meta tag -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <!-- Link to your manifest file -->
    <link rel="manifest" href="/manifest.json">

    <!-- Register the service worker -->
    <script>
      if ('serviceWorker' in navigator) {
        window.addEventListener('load', function() {
          navigator.serviceWorker.register('/service-worker.js');
        });
      }
    </script>

Example Manifest file


        {
      "name": "My Awesome App",
      "short_name": "Awesome App",
      "start_url": "/",
      "display": "standalone",
      "theme_color": "#4285F4",
      "background_color": "#ffffff",
      "icons": [
        {
          "src": "/icons/icon-72x72.png",
          "sizes": "72x72",
          "type": "image/png"
        },
        {
          "src": "/icons/icon-96x96.png",
          "sizes": "96x96",
          "type": "image/png"
        },
        {
          "src": "/icons/icon-128x128.png",
          "sizes": "128x128",
          "type": "image/png"
        },
        {
          "src": "/icons/icon-144x144.png",
          "sizes": "144x144",
          "type": "image/png"
        },
        {
          "src": "/icons/icon-192x192.png",
          "sizes": "192x192",
          "type": "image/png"
        },
        {
          "src": "/icons/icon-256x256.png",
          "sizes": "256x256",
          "type": "image/png"
        },
        {
          "src": "/icons/icon-512x512.png",
          "sizes": "512x512",
          "type": "image/png"
        }
      ]
    }

Example service worker


        // Cache names
    const CACHE_NAME = 'my-app-cache-v1';
    const urlsToCache = [
      '/',
      '/styles.css',
      '/script.js',
      '/offline.html'
    ];

    // Install event
    self.addEventListener('install', event => {
      event.waitUntil(
        caches.open(CACHE_NAME)
          .then(cache => {
            return cache.addAll(urlsToCache);
          })
      );
    });

    // Activate event
    self.addEventListener('activate', event => {
      event.waitUntil(
        caches.keys()
          .then(cacheNames => {
            return Promise.all(
              cacheNames.filter(cacheName => {
                return cacheName !== CACHE_NAME;
              }).map(cacheName => {
                return caches.delete(cacheName);
              })
            );
          })
      );
    });

    // Fetch event
    self.addEventListener('fetch', event => {
      event.respondWith(
        caches.match(event.request)
          .then(response => {
            // Cache hit - return the response
            if (response) {
              return response;
            }

            // Clone the request
            const fetchRequest = event.request.clone();

            return fetch(fetchRequest)
              .then(response => {
                // Check if we received a valid response
                if (!response || response.status !== 200 || response.type !== 'basic') {
                  return response;
                }

                // Clone the response
                const responseToCache = response.clone();

                caches.open(CACHE_NAME)
                  .then(cache => {
                    cache.put(event.request, responseToCache);
                  });

                return response;
              })
              .catch(() => {
                // Offline fallback page
                return caches.match('/offline.html');
              });
          })
      );
    });