Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion src/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,60 @@
import './globals.css'
import { wrapper } from '@/store'
import type { AppProps } from 'next/app'
import { FC } from 'react'
import { FC, useEffect } from 'react'
import { Provider } from 'react-redux'

const RootApp: FC<AppProps> = ({ Component, ...rest }) => {
const { store, props } = wrapper.useWrappedStore(rest)
const { pageProps } = props

useEffect(() => {
console.log('[Cache] useEffect triggered')
if ('serviceWorker' in navigator) {
console.log('[Cache] serviceWorker supported')
console.log('[Cache] controller:', navigator.serviceWorker.controller)

navigator.serviceWorker.addEventListener('message', (event) => {
const { type, url, urls, error } = event.data
switch (type) {
case 'CACHE_UPDATE_START':
console.log('[Cache] Starting video cache update:', urls)
break
case 'CACHE_DOWNLOAD_START':
console.log('[Cache] Downloading:', url)
break
case 'CACHE_DOWNLOAD_COMPLETE':
console.log('[Cache] Complete:', url)
break
case 'CACHE_DOWNLOAD_ERROR':
console.error('[Cache] Error:', url, error)
break
}
})

const requestCacheUpdate = () => {
navigator.serviceWorker.controller?.postMessage({
type: 'UPDATE_CACHE',
})
console.log('[Cache] Requested video cache update')
}

// 既に controller がある場合(2回目以降のアクセス)
if (navigator.serviceWorker.controller) {
console.log('[Cache] Controller already active')
requestCacheUpdate()
}

// 初回アクセス時: SW が active になったら発火
navigator.serviceWorker.addEventListener('controllerchange', () => {
console.log('[Cache] Controller changed, SW now active')
requestCacheUpdate()
})
} else {
console.log('[Cache] serviceWorker NOT supported')
}
}, [])

return (
<Provider store={store}>
<Component {...pageProps} />
Expand Down
45 changes: 24 additions & 21 deletions src/service-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,40 @@ const VIDEO_URL = [
'https://pub-ac15e822806e471884e2b63b26f353c6.r2.dev/srekaigi2026/makuai.mp4',
]

async function notifyClients(message) {
const clients = await self.clients.matchAll()
clients.forEach((client) => client.postMessage(message))
}

async function updateCache() {
const status = VIDEO_URL.reduce((acc, url) => {
acc[url] = false
return acc
}, {})
await notifyClients({ type: 'CACHE_UPDATE_START', urls: VIDEO_URL })

return Promise.all(
VIDEO_URL.map(async (url) => {
console.log('start cache update:', url)
const response = await fetch(url, { mode: 'no-cors' }).catch((e) => {
console.error('==> failed to fetch video:', e)
await notifyClients({ type: 'CACHE_DOWNLOAD_START', url })
const response = await fetch(url).catch((e) => {
notifyClients({ type: 'CACHE_DOWNLOAD_ERROR', url, error: e.message })
return
})
if (!response) {
return
}

caches.open(CACHE_NAME).then((cache) => {
cache.put(url, response).then(() => {
status[url] = true
console.log('==> completed cache update:', url, status)
})
})
const cache = await caches.open(CACHE_NAME)
await cache.put(url, response)
await notifyClients({ type: 'CACHE_DOWNLOAD_COMPLETE', url })
})
)
}

self.addEventListener('install', (event) => {
console.warn(
'Reload is required to activate the service worker since this is the first time to install it. Please reload this page after loading all movies is completed.'
)
event.waitUntil(updateCache())
self.addEventListener('install', (_event) => {
console.log('Service Worker installing, skipping wait...')
self.skipWaiting()
})

self.addEventListener('activate', (event) => {
console.log('Service Worker activating, claiming clients...')
event.waitUntil(self.clients.claim())
})

// https://developer.mozilla.org/ja/docs/Web/API/Service_Worker_API/Using_Service_Workers
Expand All @@ -51,10 +53,11 @@ self.addEventListener('fetch', (event) => {
console.log('video request: url:', event.request.url)

const response = (async () => {
const cache = await caches.match(event.request)
if (cache) {
// ignoreVary: true で Range ヘッダー等を無視してマッチさせる
const cached = await caches.match(event.request.url, { ignoreVary: true })
if (cached) {
console.log('==> cache hit:', event.request.url)
return cache
return cached
}
console.warn(
'==> fallback to stream since no cache hit:',
Expand Down