mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-05-06 20:43:03 +08:00
add service worker
This commit is contained in:
@@ -1 +1,2 @@
|
||||
VITE_API_BASE_URL=http://localhost:3001/api/v1/
|
||||
VITE_PUBLIC_VAPID_KEY=BH3w49sZA6jXUnE-yt4jO6VKh73lsdsvwoJ6Hx7fmPIDKoqGiUl2GEoZzy-iJfn4SfQQcx7yQdHf9RknwrL_lSM
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
VITE_API_BASE_URL=api/v1/
|
||||
VITE_PUBLIC_VAPID_KEY=BH3w49sZA6jXUnE-yt4jO6VKh73lsdsvwoJ6Hx7fmPIDKoqGiUl2GEoZzy-iJfn4SfQQcx7yQdHf9RknwrL_lSM
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,6 +11,7 @@ node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
dev-dist
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||
<link rel="apple-touch-startup-image" href="/splash/apple-splash.jpg" />
|
||||
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
|
||||
<link rel="manifest" href="manifest.json" crossorigin="use-credentials" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "moviepilot",
|
||||
"version": "1.9.3-4",
|
||||
"version": "1.9.4-beta",
|
||||
"private": true,
|
||||
"bin": "dist/service.js",
|
||||
"scripts": {
|
||||
@@ -42,7 +42,6 @@
|
||||
"sass": "^1.59.3",
|
||||
"tailwindcss": "^3.3.2",
|
||||
"unplugin-vue-define-options": "^1.3.5",
|
||||
"vite-plugin-pwa": "^0.19.8",
|
||||
"vue": "^3.3.2",
|
||||
"vue-router": "^4.2.0",
|
||||
"vue-toast-notification": "^3",
|
||||
@@ -92,6 +91,7 @@
|
||||
"unplugin-vue-components": "^0.26.0",
|
||||
"vite": "^5.2.8",
|
||||
"vite-plugin-pages": "^0.32.1",
|
||||
"vite-plugin-pwa": "^0.20.0",
|
||||
"vite-plugin-vue-layouts": "^0.11.0",
|
||||
"vite-plugin-vuetify": "2.0.3",
|
||||
"vue-shepherd": "^3.0.0",
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
{
|
||||
"name": "MoviePilot",
|
||||
"short_name": "MoviePilot",
|
||||
"start_url": "./",
|
||||
"display": "standalone",
|
||||
"icons": [
|
||||
{
|
||||
"src": "./android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "./android-chrome-192x192_maskable.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "./android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "./android-chrome-512x512_maskable.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
],
|
||||
"theme_color": "#28243D",
|
||||
"background_color": "#28243D",
|
||||
"shortcuts": [
|
||||
{
|
||||
"name": "推荐",
|
||||
"url": "./ranking",
|
||||
"icons": [
|
||||
{
|
||||
"src": "./sparkles-icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "电影订阅",
|
||||
"url": "./subscribe-movie",
|
||||
"icons": [
|
||||
{
|
||||
"src": "./clock-icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "电视剧订阅",
|
||||
"url": "./subscribe-tv",
|
||||
"icons": [
|
||||
{
|
||||
"src": "./clock-icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "设置",
|
||||
"url": "./setting",
|
||||
"icons": [
|
||||
{
|
||||
"src": "./cog-icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
2
public/robots.txt
Normal file
2
public/robots.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow: /
|
||||
@@ -5,14 +5,14 @@
|
||||
/**
|
||||
* 修复低版本Safari等浏览器数组不支持at函数的问题
|
||||
*/
|
||||
export function fixArrayAt() {
|
||||
if (!Array.prototype.at) {
|
||||
Array.prototype.at = function(index: number) {
|
||||
if (index >= 0) {
|
||||
return this[index]
|
||||
} else {
|
||||
return this[this.length + index]
|
||||
}
|
||||
}
|
||||
;(function fixArrayAt() {
|
||||
if (!Array.prototype.at) {
|
||||
Array.prototype.at = function (index: number) {
|
||||
if (index >= 0) {
|
||||
return this[index]
|
||||
} else {
|
||||
return this[this.length + index]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})()
|
||||
|
||||
@@ -28,3 +28,17 @@ export async function copyToClipboard(content: string) {
|
||||
document.body.removeChild(input)
|
||||
}
|
||||
}
|
||||
|
||||
// VAPID公钥转Uint8Array
|
||||
export function urlBase64ToUint8Array(base64String: string) {
|
||||
const padding = '='.repeat((4 - (base64String.length % 4)) % 4)
|
||||
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/')
|
||||
|
||||
const rawData = window.atob(base64)
|
||||
const outputArray = new Uint8Array(rawData.length)
|
||||
|
||||
for (let i = 0; i < rawData.length; ++i) {
|
||||
outputArray[i] = rawData.charCodeAt(i)
|
||||
}
|
||||
return outputArray
|
||||
}
|
||||
|
||||
34
src/main.ts
34
src/main.ts
@@ -1,23 +1,19 @@
|
||||
import { VAceEditor } from 'vue3-ace-editor'
|
||||
import { createApp } from 'vue'
|
||||
import '@/@iconify/icons-bundle'
|
||||
import ToastPlugin from 'vue-toast-notification'
|
||||
import VuetifyUseDialog from 'vuetify-use-dialog'
|
||||
import '@/@core/utils/compatibility'
|
||||
import './ace-config'
|
||||
import VueApexCharts from 'vue3-apexcharts'
|
||||
import { removeEl } from './@core/utils/dom'
|
||||
import '@/@iconify/icons-bundle'
|
||||
import '@/plugins/webfontloader'
|
||||
import App from '@/App.vue'
|
||||
import vuetify from '@/plugins/vuetify'
|
||||
import { loadFonts } from '@/plugins/webfontloader'
|
||||
import router from '@/router'
|
||||
import store from '@/store'
|
||||
import '@core/scss/template/index.scss'
|
||||
import '@layouts/styles/index.scss'
|
||||
import '@styles/styles.scss'
|
||||
import 'vue-toast-notification/dist/theme-bootstrap.css'
|
||||
import { VAceEditor } from 'vue3-ace-editor'
|
||||
import { createApp } from 'vue'
|
||||
import { removeEl } from './@core/utils/dom'
|
||||
import { PerfectScrollbarPlugin } from 'vue3-perfect-scrollbar'
|
||||
import 'vue3-perfect-scrollbar/style.css'
|
||||
import { VTreeview } from 'vuetify/labs/VTreeview'
|
||||
import ToastPlugin from 'vue-toast-notification'
|
||||
import VuetifyUseDialog from 'vuetify-use-dialog'
|
||||
import VueApexCharts from 'vue3-apexcharts'
|
||||
import DialogCloseBtn from '@/@core/components/DialogCloseBtn.vue'
|
||||
import MediaCard from './components/cards/MediaCard.vue'
|
||||
import PosterCard from './components/cards/PosterCard.vue'
|
||||
@@ -27,13 +23,11 @@ import MediaInfoCard from './components/cards/MediaInfoCard.vue'
|
||||
import TorrentCard from './components/cards/TorrentCard.vue'
|
||||
import MediaIdSelector from './components/misc/MediaIdSelector.vue'
|
||||
import PathField from './components/input/PathField.vue'
|
||||
import { fixArrayAt } from '@/@core/utils/compatibility'
|
||||
|
||||
// 修复低版本Safari等浏览器数组不支持at函数的问题
|
||||
fixArrayAt()
|
||||
|
||||
// 加载字体
|
||||
loadFonts()
|
||||
import '@core/scss/template/index.scss'
|
||||
import '@layouts/styles/index.scss'
|
||||
import '@styles/styles.scss'
|
||||
import 'vue-toast-notification/dist/theme-bootstrap.css'
|
||||
import 'vue3-perfect-scrollbar/style.css'
|
||||
|
||||
// 创建Vue实例
|
||||
const app = createApp(App)
|
||||
|
||||
@@ -8,6 +8,7 @@ import router from '@/router'
|
||||
import logo from '@images/logo.png'
|
||||
import { useTheme } from 'vuetify'
|
||||
import { checkPrefersColorSchemeIsDark } from '@/@core/utils'
|
||||
import { urlBase64ToUint8Array } from '@/@core/utils/navigator'
|
||||
|
||||
const { global: globalTheme } = useTheme()
|
||||
|
||||
@@ -89,9 +90,33 @@ async function setTheme() {
|
||||
localStorage.setItem('materio-initial-loader-bg', globalTheme.current.value.colors.background)
|
||||
}
|
||||
|
||||
// 订阅推送通知
|
||||
async function subscribeForPushNotifications() {
|
||||
if ('serviceWorker' in navigator && 'PushManager' in window) {
|
||||
const registration = await navigator.serviceWorker.ready
|
||||
// 获取订阅信息
|
||||
const subscription = await registration.pushManager.getSubscription().then(function (subscription) {
|
||||
if (subscription === null) {
|
||||
const convertedVapidKey = urlBase64ToUint8Array(import.meta.env.VITE_PUBLIC_VAPID_KEY)
|
||||
return registration.pushManager.subscribe({
|
||||
userVisibleOnly: true,
|
||||
applicationServerKey: convertedVapidKey,
|
||||
})
|
||||
} else {
|
||||
return subscription
|
||||
}
|
||||
})
|
||||
// 发送订阅请求
|
||||
await api.post('/message/subscribe', subscription)
|
||||
}
|
||||
}
|
||||
|
||||
// 登录后处理
|
||||
async function afterLogin() {
|
||||
// 生效主题配置
|
||||
await setTheme()
|
||||
// 订阅推送通知
|
||||
await subscribeForPushNotifications()
|
||||
// 跳转到首页或回原始页面
|
||||
router.push(store.state.auth.originalPath ?? '/')
|
||||
}
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
* webfontloader documentation: https://github.com/typekit/webfontloader
|
||||
*/
|
||||
|
||||
export async function loadFonts() {
|
||||
const webFontLoader = await import(/* webpackChunkName: "webfontloader" */'webfontloader')
|
||||
;(async function loadFonts() {
|
||||
const webFontLoader = await import(/* webpackChunkName: "webfontloader" */ 'webfontloader')
|
||||
|
||||
webFontLoader.load({
|
||||
google: {
|
||||
families: ['Inter:100,200,300,400,500,600,700&display=swap'],
|
||||
},
|
||||
})
|
||||
}
|
||||
})()
|
||||
|
||||
39
src/service-worker.ts
Normal file
39
src/service-worker.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { createHandlerBoundToURL, cleanupOutdatedCaches, precacheAndRoute } from 'workbox-precaching'
|
||||
import { NavigationRoute, registerRoute } from 'workbox-routing'
|
||||
import { clientsClaim } from 'workbox-core'
|
||||
|
||||
declare let self: ServiceWorkerGlobalScope
|
||||
|
||||
cleanupOutdatedCaches()
|
||||
|
||||
// self.__WB_MANIFEST is default injection point
|
||||
precacheAndRoute(self.__WB_MANIFEST)
|
||||
|
||||
// to allow work offline
|
||||
registerRoute(new NavigationRoute(createHandlerBoundToURL('index.html'), { denylist: [/^\/api/] }))
|
||||
|
||||
// 通知选项
|
||||
const options = {
|
||||
icon: '/logo.png',
|
||||
}
|
||||
|
||||
// 监听 push 事件,显示通知
|
||||
self.addEventListener('push', function (e) {
|
||||
if (!e.data) {
|
||||
return
|
||||
}
|
||||
// 解析获取推送消息
|
||||
let payload = e.data.json()
|
||||
// 根据推送消息生成桌面通知并展现出来
|
||||
let promise = self.registration.showNotification(payload.title, {
|
||||
body: payload.body,
|
||||
icon: payload.icon ?? options.icon,
|
||||
data: {
|
||||
url: payload.url,
|
||||
},
|
||||
})
|
||||
e.waitUntil(promise)
|
||||
})
|
||||
|
||||
self.skipWaiting()
|
||||
clientsClaim()
|
||||
@@ -46,7 +46,8 @@
|
||||
"esnext",
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost"
|
||||
"scripthost",
|
||||
"WebWorker"
|
||||
],
|
||||
"skipLibCheck": true,
|
||||
"types": [
|
||||
@@ -63,11 +64,13 @@
|
||||
"src/**/*.vue",
|
||||
"themeConfig.ts",
|
||||
"auto-imports.d.ts",
|
||||
"components.d.ts"
|
||||
"components.d.ts",
|
||||
"src/service-worker.ts",
|
||||
"public/service.js"
|
||||
],
|
||||
"exclude": [
|
||||
"dist",
|
||||
"node_modules",
|
||||
"src/@iconify/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
104
vite.config.ts
104
vite.config.ts
@@ -4,8 +4,8 @@ import vueJsx from '@vitejs/plugin-vue-jsx'
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import { defineConfig } from 'vite'
|
||||
import { VitePWA } from 'vite-plugin-pwa'
|
||||
import vuetify from 'vite-plugin-vuetify'
|
||||
import { VitePWA } from 'vite-plugin-pwa'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
@@ -13,8 +13,6 @@ export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
vueJsx(),
|
||||
|
||||
// https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vite-plugin
|
||||
vuetify({
|
||||
styles: {
|
||||
configFile: 'src/styles/variables/_vuetify.scss',
|
||||
@@ -29,12 +27,100 @@ export default defineConfig({
|
||||
vueTemplate: true,
|
||||
}),
|
||||
VitePWA({
|
||||
registerType: 'autoUpdate',
|
||||
injectRegister: 'script',
|
||||
manifest: false,
|
||||
registerType: 'autoUpdate',
|
||||
strategies: 'injectManifest',
|
||||
srcDir: 'src',
|
||||
filename: 'service-worker.ts',
|
||||
workbox: {
|
||||
navigateFallbackDenylist: [
|
||||
/.*\/api\/v\d+\/system\/logging.*/,
|
||||
globPatterns: ['**/*.{js,css,html,ico,png,svg,jpg,jpeg}'],
|
||||
navigateFallbackDenylist: [/.*\/api\/v\d+\/system\/logging.*/],
|
||||
},
|
||||
injectManifest: {
|
||||
rollupFormat: 'iife',
|
||||
},
|
||||
devOptions: {
|
||||
enabled: true,
|
||||
type: 'module',
|
||||
},
|
||||
manifest: {
|
||||
'name': 'MoviePilot',
|
||||
'short_name': 'MoviePilot',
|
||||
'start_url': './',
|
||||
'display': 'standalone',
|
||||
'icons': [
|
||||
{
|
||||
'src': './android-chrome-192x192.png',
|
||||
'sizes': '192x192',
|
||||
'type': 'image/png',
|
||||
'purpose': 'any',
|
||||
},
|
||||
{
|
||||
'src': './android-chrome-192x192_maskable.png',
|
||||
'sizes': '192x192',
|
||||
'type': 'image/png',
|
||||
'purpose': 'maskable',
|
||||
},
|
||||
{
|
||||
'src': './android-chrome-512x512.png',
|
||||
'sizes': '512x512',
|
||||
'type': 'image/png',
|
||||
'purpose': 'any',
|
||||
},
|
||||
{
|
||||
'src': './android-chrome-512x512_maskable.png',
|
||||
'sizes': '512x512',
|
||||
'type': 'image/png',
|
||||
'purpose': 'maskable',
|
||||
},
|
||||
],
|
||||
'theme_color': '#28243D',
|
||||
'background_color': '#28243D',
|
||||
'shortcuts': [
|
||||
{
|
||||
'name': '推荐',
|
||||
'url': './ranking',
|
||||
'icons': [
|
||||
{
|
||||
'src': './sparkles-icon-192x192.png',
|
||||
'sizes': '192x192',
|
||||
'type': 'image/png',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': '电影订阅',
|
||||
'url': './subscribe-movie?tab=mysub',
|
||||
'icons': [
|
||||
{
|
||||
'src': './clock-icon-192x192.png',
|
||||
'sizes': '192x192',
|
||||
'type': 'image/png',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': '电视剧订阅',
|
||||
'url': './subscribe-tv?tab=mysub',
|
||||
'icons': [
|
||||
{
|
||||
'src': './clock-icon-192x192.png',
|
||||
'sizes': '192x192',
|
||||
'type': 'image/png',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': '设置',
|
||||
'url': './setting',
|
||||
'icons': [
|
||||
{
|
||||
'src': './cog-icon-192x192.png',
|
||||
'sizes': '192x192',
|
||||
'type': 'image/png',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
@@ -63,8 +149,6 @@ export default defineConfig({
|
||||
},
|
||||
optimizeDeps: {
|
||||
exclude: ['vuetify'],
|
||||
entries: [
|
||||
'./src/**/*.vue',
|
||||
],
|
||||
entries: ['./src/**/*.vue'],
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user