Ver código fonte

feat: implement pwa support #1250

0xJacky 3 semanas atrás
pai
commit
f259877665

+ 32 - 9
app/index.html

@@ -1,26 +1,49 @@
 <!DOCTYPE html>
 <html lang="en">
+
 <head>
-	<meta charset="UTF-8"/>
-	<link href="/favicon.ico" rel="icon">
-	<meta content="width=device-width,initial-scale=1.0,user-scalable=0" name="viewport">
-	<style>
+  <meta charset="UTF-8" />
+  <link href="/favicon.ico" rel="icon">
+  <link href="/favicon-32x32.png" rel="icon" type="image/png" sizes="32x32">
+  <meta content="width=device-width,initial-scale=1.0,user-scalable=0" name="viewport">
+
+  <!-- PWA Manifest -->
+  <link rel="manifest" href="/manifest.json">
+
+  <!-- PWA Meta Tags -->
+  <meta name="theme-color" content="#ffffff">
+  <meta name="apple-mobile-web-app-capable" content="yes">
+  <meta name="apple-mobile-web-app-status-bar-style" content="default">
+  <meta name="apple-mobile-web-app-title" content="Nginx UI">
+  <meta name="mobile-web-app-capable" content="yes">
+  <meta name="msapplication-TileColor" content="#ffffff">
+  <meta name="msapplication-config" content="/browserconfig.xml">
+
+  <!-- Apple Touch Icons -->
+  <link rel="apple-touch-icon" sizes="192x192" href="/pwa-192x192.png">
+  <link rel="apple-touch-icon" sizes="512x512" href="/pwa-512x512.png">
+
+  <style>
     body {
       height: auto !important;
       min-height: 100%;
     }
+
     body.dark {
       background-color: #141414;
       color: #fff;
     }
+
     #app {
       height: 100vh;
     }
-	</style>
-	<title>Nginx UI</title>
+  </style>
+  <title>Nginx UI</title>
 </head>
+
 <body>
-<div id="app"></div>
-<script type="module" src="/src/main.ts"></script>
+  <div id="app"></div>
+  <script type="module" src="/src/main.ts"></script>
 </body>
-</html>
+
+</html>

+ 9 - 0
app/public/browserconfig.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<browserconfig>
+    <msapplication>
+        <tile>
+            <square150x150logo src="/pwa-192x192.png"/>
+            <TileColor>#1890ff</TileColor>
+        </tile>
+    </msapplication>
+</browserconfig> 

BIN
app/public/favicon-32x32.png


+ 63 - 0
app/public/manifest.json

@@ -0,0 +1,63 @@
+{
+  "name": "Nginx UI",
+  "short_name": "Nginx UI",
+  "description": "Yet another Nginx Web UI",
+  "theme_color": "#ffffff",
+  "background_color": "#ffffff",
+  "display": "standalone",
+  "orientation": "portrait-primary",
+  "scope": "/",
+  "start_url": "/",
+  "lang": "en",
+  "categories": ["productivity", "utilities"],
+  "icons": [
+    {
+      "src": "/favicon.ico",
+      "sizes": "any",
+      "type": "image/x-icon",
+      "purpose": "any"
+    },
+    {
+      "src": "/favicon-32x32.png",
+      "sizes": "32x32",
+      "type": "image/png",
+      "purpose": "any"
+    },
+    {
+      "src": "/pwa-48x48.png",
+      "sizes": "48x48",
+      "type": "image/png",
+      "purpose": "any"
+    },
+    {
+      "src": "/pwa-72x72.png",
+      "sizes": "72x72",
+      "type": "image/png",
+      "purpose": "any"
+    },
+    {
+      "src": "/pwa-96x96.png",
+      "sizes": "96x96",
+      "type": "image/png",
+      "purpose": "any"
+    },
+    {
+      "src": "/pwa-144x144.png",
+      "sizes": "144x144",
+      "type": "image/png",
+      "purpose": "any"
+    },
+    {
+      "src": "/pwa-192x192.png",
+      "sizes": "192x192",
+      "type": "image/png",
+      "purpose": "any maskable"
+    },
+    {
+      "src": "/pwa-512x512.png",
+      "sizes": "512x512",
+      "type": "image/png",
+      "purpose": "any maskable"
+    }
+  ]
+}

BIN
app/public/pwa-144x144.png


BIN
app/public/pwa-192x192.png


BIN
app/public/pwa-48x48.png


BIN
app/public/pwa-512x512.png


BIN
app/public/pwa-72x72.png


BIN
app/public/pwa-96x96.png


+ 71 - 0
app/src/lib/pwa/index.ts

@@ -0,0 +1,71 @@
+/**
+ * PWA related utilities
+ */
+
+// Theme colors for different modes
+const THEME_COLORS = {
+  light: '#ffffff', // White for light mode
+  dark: '#141414', // Dark background for dark mode
+} as const
+
+/**
+ * Update the theme-color meta tag based on current theme
+ * @param theme - 'light' or 'dark'
+ */
+export function updateThemeColor(theme: 'light' | 'dark') {
+  const themeColorMeta = document.querySelector('meta[name="theme-color"]') as HTMLMetaElement
+  const appleStatusBarMeta = document.querySelector('meta[name="apple-mobile-web-app-status-bar-style"]') as HTMLMetaElement
+  const msApplicationTileColorMeta = document.querySelector('meta[name="msapplication-TileColor"]') as HTMLMetaElement
+
+  if (themeColorMeta) {
+    const color = THEME_COLORS[theme]
+    themeColorMeta.setAttribute('content', color)
+
+    // Also update apple status bar style
+    if (appleStatusBarMeta) {
+      appleStatusBarMeta.setAttribute('content', theme === 'dark' ? 'black-translucent' : 'default')
+    }
+
+    // Update Windows tile color
+    if (msApplicationTileColorMeta) {
+      msApplicationTileColorMeta.setAttribute('content', color)
+    }
+  }
+}
+
+/**
+ * Get the current theme from document body class
+ */
+export function getCurrentTheme(): 'light' | 'dark' {
+  return document.body.classList.contains('dark') ? 'dark' : 'light'
+}
+
+/**
+ * Initialize PWA theme color based on current theme
+ */
+export function initPWAThemeColor() {
+  const currentTheme = getCurrentTheme()
+  updateThemeColor(currentTheme)
+}
+
+/**
+ * Watch for theme changes and update PWA theme color accordingly
+ */
+export function watchThemeChanges() {
+  // Use MutationObserver to watch for class changes on body
+  const observer = new MutationObserver(mutations => {
+    mutations.forEach(mutation => {
+      if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
+        const currentTheme = getCurrentTheme()
+        updateThemeColor(currentTheme)
+      }
+    })
+  })
+
+  observer.observe(document.body, {
+    attributes: true,
+    attributeFilter: ['class'],
+  })
+
+  return observer
+}

+ 7 - 0
app/src/main.ts

@@ -6,6 +6,7 @@ import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
 import { createApp } from 'vue'
 import VueDOMPurifyHTML from 'vue-dompurify-html'
 import { setupInterceptors } from '@/lib/http/interceptors'
+import { initPWAThemeColor, watchThemeChanges } from '@/lib/pwa'
 import { useSettingsStore } from '@/pinia'
 import i18n from '../i18n.json'
 import App from './App.vue'
@@ -79,4 +80,10 @@ gettext.current = settings.language || 'en'
 
 app.use(router).use(autoAnimatePlugin).mount('#app')
 
+// Initialize PWA theme color functionality after app is mounted
+nextTick(() => {
+  initPWAThemeColor()
+  watchThemeChanges()
+})
+
 export default app

+ 1 - 1
app/src/version.json

@@ -1 +1 @@
-{"version":"2.1.14","build_id":1,"total_build":454}
+{"version":"2.1.14","build_id":8,"total_build":461}