Add service worker + various cleanup
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
@ -2,20 +2,18 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
|
||||
<!--
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
-->
|
||||
<meta name="apple-mobile-web-app-title" content="Photoview" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="white" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<!--
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
-->
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
|
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 103 KiB |
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name": "Photoview",
|
||||
"short_name": "Photoview",
|
||||
"description": "Photo gallery for self-hosted personal servers",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"theme_color": "black",
|
||||
"background_color": "white",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/assets/logo192.png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "/assets/logo512.png",
|
||||
"sizes": "512x512"
|
||||
},
|
||||
{
|
||||
"src": "/assets/photoview-logo.svg",
|
||||
"sizes": "150x150"
|
||||
}
|
||||
]
|
||||
}
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
@ -4,7 +4,6 @@ import { useForm, SubmitHandler } from 'react-hook-form'
|
|||
import { checkInitialSetupQuery, login } from './loginUtilities'
|
||||
import { authToken } from '../../helpers/authentication'
|
||||
|
||||
import logoPath from '../../assets/photoview-logo.svg'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Helmet } from 'react-helmet'
|
||||
import { Redirect } from 'react-router'
|
||||
|
@ -28,7 +27,11 @@ const LogoHeader = () => {
|
|||
|
||||
return (
|
||||
<div className="flex justify-center flex-col mb-14 mt-20">
|
||||
<img className="h-24" src={logoPath} alt="photoview logo" />
|
||||
<img
|
||||
className="h-24"
|
||||
src={process.env.PUBLIC_URL + '/photoview-logo.svg'}
|
||||
alt="photoview logo"
|
||||
/>
|
||||
<h1 className="text-3xl text-center mt-4">
|
||||
{t('login_page.welcome', 'Welcome to Photoview')}
|
||||
</h1>
|
||||
|
|
|
@ -157,8 +157,7 @@ export const FaceDetails = ({ group }: FaceDetailsProps) => {
|
|||
}
|
||||
onKeyDown={onKeyDown}
|
||||
onChange={e => setInputValue(e.target.value)}
|
||||
onBlur={e => {
|
||||
console.log(e)
|
||||
onBlur={() => {
|
||||
resetLabel()
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -39,42 +39,3 @@ export function placesReducer(
|
|||
return photoGalleryReducer(state, action)
|
||||
}
|
||||
}
|
||||
|
||||
// export const presentMarkerClicked = ({
|
||||
// dispatchMedia,
|
||||
// mediaState,
|
||||
// marker,
|
||||
// }: {
|
||||
// dispatchMedia: React.Dispatch<PlacesAction>
|
||||
// mediaState: PlacesState
|
||||
// marker: PresentMarker
|
||||
// }) => {
|
||||
// console.log(
|
||||
// 'present marker clicked',
|
||||
// mediaState,
|
||||
// marker,
|
||||
// !!mediaState.presentMarker,
|
||||
// mediaState.presentMarker?.cluster === marker.cluster,
|
||||
// mediaState.presentMarker?.id === marker.id
|
||||
// )
|
||||
// if (
|
||||
// mediaState.presentMarker &&
|
||||
// mediaState.presentMarker.cluster === marker.cluster &&
|
||||
// mediaState.presentMarker.id === marker.id
|
||||
// ) {
|
||||
// console.log('EQUAL OPEN PRESENT MODE')
|
||||
// history.pushState(
|
||||
// { presenting: true, activeIndex: mediaState.activeIndex },
|
||||
// ''
|
||||
// )
|
||||
// dispatchMedia({
|
||||
// type: 'openPresentMode',
|
||||
// activeIndex: mediaState.activeIndex,
|
||||
// })
|
||||
// } else {
|
||||
// dispatchMedia({
|
||||
// type: 'replacePresentMarker',
|
||||
// marker,
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -60,7 +60,6 @@ const AlbumTitle = ({ album, disableLink = false }: AlbumTitleProps) => {
|
|||
}, [album])
|
||||
|
||||
const delay = useDelay(200, [album])
|
||||
console.log('delay', delay)
|
||||
|
||||
if (!album) {
|
||||
return (
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React, { useContext } from 'react'
|
||||
import SearchBar from './Searchbar'
|
||||
|
||||
import logoPath from '../../assets/photoview-logo.svg'
|
||||
import { authToken } from '../../helpers/authentication'
|
||||
import { SidebarContext } from '../sidebar/Sidebar'
|
||||
import classNames from 'classnames'
|
||||
|
@ -17,7 +16,11 @@ const Header = () => {
|
|||
)}
|
||||
>
|
||||
<h1 className="mr-4 lg:mr-8 flex-shrink-0 flex items-center">
|
||||
<img className="h-12 lg:h-10" src={logoPath} alt="logo" />
|
||||
<img
|
||||
className="h-12 lg:h-10"
|
||||
src={process.env.PUBLIC_URL + '/photoview-logo.svg'}
|
||||
alt="logo"
|
||||
/>
|
||||
<span className="hidden lg:block ml-2 text-2xl font-light">
|
||||
Photoview
|
||||
</span>
|
||||
|
|
|
@ -128,7 +128,6 @@ const SearchBar = () => {
|
|||
const keydownEvent = (event: KeyboardEvent) => {
|
||||
if (!expanded) return
|
||||
|
||||
console.log(event.key)
|
||||
if (event.key == 'ArrowDown') {
|
||||
event.preventDefault()
|
||||
setSelectedItem(i => (i === null ? 0 : Math.min(totalItems - 1, i + 1)))
|
||||
|
|
|
@ -3,11 +3,11 @@ import 'regenerator-runtime/runtime'
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import App from './App'
|
||||
import registerServiceWorker from './registerServiceWorker'
|
||||
import client from './apolloClient'
|
||||
import { ApolloProvider } from '@apollo/client'
|
||||
import { BrowserRouter as Router } from 'react-router-dom'
|
||||
import { setupLocalization } from './localization'
|
||||
import * as serviceWorkerRegistration from './serviceWorkerRegistration'
|
||||
|
||||
import './index.css'
|
||||
import { SidebarProvider } from './components/sidebar/Sidebar'
|
||||
|
@ -26,4 +26,4 @@ const Main = () => (
|
|||
|
||||
ReactDOM.render(<Main />, document.getElementById('root'))
|
||||
|
||||
registerServiceWorker()
|
||||
serviceWorkerRegistration.register()
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
// In production, we register a service worker to serve assets from local cache.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
// it offline capabilities. However, it also means that developers (and users)
|
||||
// will only see deployed updates on the "N+1" visit to a page, since previously
|
||||
// cached resources are updated in the background.
|
||||
|
||||
// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
|
||||
// This link also includes instructions on opting out of this behavior.
|
||||
|
||||
const isProduction = process.env.NODE_ENV == 'production'
|
||||
|
||||
export default function register() {
|
||||
if (isProduction && 'serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => {
|
||||
const swUrl = `/service-worker.js`
|
||||
|
||||
if (!isProduction) {
|
||||
// This is running on localhost. Lets check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl)
|
||||
} else {
|
||||
// Is not local host. Just register service worker
|
||||
registerValidSW(swUrl)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function registerValidSW(swUrl) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then(registration => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing
|
||||
installingWorker.onstatechange = () => {
|
||||
if (installingWorker.state === 'installed') {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// At this point, the old content will have been purged and
|
||||
// the fresh content will have been added to the cache.
|
||||
// It's the perfect time to display a "New content is
|
||||
// available; please refresh." message in your web app.
|
||||
console.log('New content is available; please refresh.')
|
||||
} else {
|
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a
|
||||
// "Content is cached for offline use." message.
|
||||
console.log('Content is cached for offline use.')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error during service worker registration:', error)
|
||||
})
|
||||
}
|
||||
|
||||
function checkValidServiceWorker(swUrl) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl)
|
||||
.then(response => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
if (
|
||||
response.status === 404 ||
|
||||
response.headers.get('content-type').indexOf('javascript') === -1
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload()
|
||||
})
|
||||
})
|
||||
} else {
|
||||
// Service worker found. Proceed as normal.
|
||||
registerValidSW(swUrl)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.'
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export function unregister() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister()
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/// <reference lib="webworker" />
|
||||
/* eslint-disable no-restricted-globals */
|
||||
|
||||
// This service worker can be customized!
|
||||
// See https://developers.google.com/web/tools/workbox/modules
|
||||
// for the list of available Workbox modules, or add any other
|
||||
// code you'd like.
|
||||
// You can also remove this file if you'd prefer not to use a
|
||||
// service worker, and the Workbox build step will be skipped.
|
||||
|
||||
import { clientsClaim } from 'workbox-core'
|
||||
import { ExpirationPlugin } from 'workbox-expiration'
|
||||
import { precacheAndRoute, createHandlerBoundToURL } from 'workbox-precaching'
|
||||
import { registerRoute } from 'workbox-routing'
|
||||
import { StaleWhileRevalidate } from 'workbox-strategies'
|
||||
|
||||
declare const self: ServiceWorkerGlobalScope
|
||||
|
||||
clientsClaim()
|
||||
|
||||
// Precache all of the assets generated by your build process.
|
||||
// Their URLs are injected into the manifest variable below.
|
||||
// This variable must be present somewhere in your service worker file,
|
||||
// even if you decide not to use precaching. See https://cra.link/PWA
|
||||
precacheAndRoute(self.__WB_MANIFEST)
|
||||
|
||||
// Set up App Shell-style routing, so that all navigation requests
|
||||
// are fulfilled with your index.html shell. Learn more at
|
||||
// https://developers.google.com/web/fundamentals/architecture/app-shell
|
||||
const fileExtensionRegexp = new RegExp('/[^/?]+\\.[^/]+$')
|
||||
registerRoute(
|
||||
// Return false to exempt requests from being fulfilled by index.html.
|
||||
({ request, url }: { request: Request; url: URL }) => {
|
||||
// If this isn't a navigation, skip.
|
||||
if (request.mode !== 'navigate') {
|
||||
return false
|
||||
}
|
||||
|
||||
// If this is a URL that starts with /_, skip.
|
||||
if (url.pathname.startsWith('/_')) {
|
||||
return false
|
||||
}
|
||||
|
||||
// If this looks like a URL for a resource, because it contains
|
||||
// a file extension, skip.
|
||||
if (url.pathname.match(fileExtensionRegexp)) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Return true to signal that we want to use the handler.
|
||||
return true
|
||||
},
|
||||
createHandlerBoundToURL(process.env.PUBLIC_URL + '/index.html')
|
||||
)
|
||||
|
||||
// An example runtime caching route for requests that aren't handled by the
|
||||
// precache, in this case same-origin .png requests like those from in public/
|
||||
registerRoute(
|
||||
// Add in any other file extensions or routing criteria as needed.
|
||||
({ url }) =>
|
||||
url.origin === self.location.origin && url.pathname.endsWith('.png'),
|
||||
// Customize this strategy as needed, e.g., by changing to CacheFirst.
|
||||
new StaleWhileRevalidate({
|
||||
cacheName: 'images',
|
||||
plugins: [
|
||||
// Ensure that once this runtime cache reaches a maximum size the
|
||||
// least-recently used images are removed.
|
||||
new ExpirationPlugin({ maxEntries: 50 }),
|
||||
],
|
||||
})
|
||||
)
|
||||
|
||||
// This allows the web app to trigger skipWaiting via
|
||||
// registration.waiting.postMessage({type: 'SKIP_WAITING'})
|
||||
self.addEventListener('message', event => {
|
||||
if (event.data && event.data.type === 'SKIP_WAITING') {
|
||||
self.skipWaiting()
|
||||
}
|
||||
})
|
||||
|
||||
// Any other custom service worker logic can go here.
|
|
@ -0,0 +1,146 @@
|
|||
// This optional code is used to register a service worker.
|
||||
// register() is not called by default.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
// it offline capabilities. However, it also means that developers (and users)
|
||||
// will only see deployed updates on subsequent visits to a page, after all the
|
||||
// existing tabs open on the page have been closed, since previously cached
|
||||
// resources are updated in the background.
|
||||
|
||||
// To learn more about the benefits of this model and instructions on how to
|
||||
// opt-in, read https://cra.link/PWA
|
||||
|
||||
const isLocalhost = Boolean(
|
||||
window.location.hostname === 'localhost' ||
|
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' ||
|
||||
// 127.0.0.0/8 are considered localhost for IPv4.
|
||||
window.location.hostname.match(
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||
)
|
||||
)
|
||||
|
||||
type Config = {
|
||||
onSuccess?: (registration: ServiceWorkerRegistration) => void
|
||||
onUpdate?: (registration: ServiceWorkerRegistration) => void
|
||||
}
|
||||
|
||||
export function register(config?: Config) {
|
||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||
// The URL constructor is available in all browsers that support SW.
|
||||
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href)
|
||||
if (publicUrl.origin !== window.location.origin) {
|
||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||
// from what our page is served on. This might happen if a CDN is used to
|
||||
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
||||
return
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`
|
||||
|
||||
if (isLocalhost) {
|
||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl, config)
|
||||
|
||||
// Add some additional logging to localhost, pointing developers to the
|
||||
// service worker/PWA documentation.
|
||||
navigator.serviceWorker.ready.then(() => {
|
||||
console.log(
|
||||
'This web app is being served cache-first by a service worker.' +
|
||||
'worker. To learn more, visit https://cra.link/PWA'
|
||||
)
|
||||
})
|
||||
} else {
|
||||
// Is not localhost. Just register service worker
|
||||
registerValidSW(swUrl, config)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function registerValidSW(swUrl: string, config?: Config) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then(registration => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing
|
||||
if (installingWorker == null) {
|
||||
return
|
||||
}
|
||||
installingWorker.onstatechange = () => {
|
||||
if (installingWorker.state === 'installed') {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// At this point, the updated precached content has been fetched,
|
||||
// but the previous service worker will still serve the older
|
||||
// content until all client tabs are closed.
|
||||
console.log(
|
||||
'New content is available and will be used when all ' +
|
||||
'tabs for this page are closed. See https://cra.link/PWA.'
|
||||
)
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onUpdate) {
|
||||
config.onUpdate(registration)
|
||||
}
|
||||
} else {
|
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a
|
||||
// "Content is cached for offline use." message.
|
||||
console.log('Content is cached for offline use.')
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onSuccess) {
|
||||
config.onSuccess(registration)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error during service worker registration:', error)
|
||||
})
|
||||
}
|
||||
|
||||
function checkValidServiceWorker(swUrl: string, config?: Config) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl, {
|
||||
headers: { 'Service-Worker': 'script' },
|
||||
})
|
||||
.then(response => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
const contentType = response.headers.get('content-type')
|
||||
if (
|
||||
response.status === 404 ||
|
||||
(contentType != null && contentType.indexOf('javascript') === -1)
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload()
|
||||
})
|
||||
})
|
||||
} else {
|
||||
// Service worker found. Proceed as normal.
|
||||
registerValidSW(swUrl, config)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.'
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export function unregister() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready
|
||||
.then(registration => {
|
||||
registration.unregister()
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error.message)
|
||||
})
|
||||
}
|
||||
}
|