mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-03-25 05:45:11 +00:00
Update multiple packages (#719)
* Update multiple packages - @typescript-eslint/eslint-plugin - @typescript-eslint/parser - eslint-config-react-app - eslint-config-standard - react-scripts Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de> * fix type Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de> * deduplicate code Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de> * Disable test because it doesn't work Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de> * repair service worker Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de> * Lazy load mermaid Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de> * use show error Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de> * fix tsconfig in cypress Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de> * fix import integration test Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>
This commit is contained in:
parent
0c222fae64
commit
460badb97b
21 changed files with 4468 additions and 5286 deletions
|
@ -2,7 +2,7 @@
|
|||
"parserOptions": {
|
||||
"tsconfigRootDir": "",
|
||||
"project": [
|
||||
"./cypress/tsconfig.json"
|
||||
"./tsconfig.json"
|
||||
]
|
||||
},
|
||||
"plugins": [
|
||||
|
|
|
@ -14,7 +14,7 @@ describe('Import markdown file', () => {
|
|||
cy.get('.import-md-file')
|
||||
.click()
|
||||
cy.get('div[aria-labelledby="editor-menu-import"] > input[type=file]')
|
||||
.attachFile('import.md')
|
||||
.attachFile({ filePath: 'import.md', mimeType: 'text/markdown' })
|
||||
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span')
|
||||
.should('have.text', '# Some short import test file')
|
||||
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span > span')
|
||||
|
@ -29,11 +29,11 @@ describe('Import markdown file', () => {
|
|||
cy.get('.import-md-file')
|
||||
.click()
|
||||
cy.get('div[aria-labelledby="editor-menu-import"] > input[type=file]')
|
||||
.attachFile('import.md')
|
||||
.attachFile({ filePath: 'import.md', mimeType: 'text/markdown' })
|
||||
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span')
|
||||
.should('have.text', 'test')
|
||||
.should('have.text', 'test')
|
||||
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span > span')
|
||||
.should('have.text', 'abc')
|
||||
.should('have.text', 'abc')
|
||||
cy.get('.CodeMirror-code > div:nth-of-type(3) > .CodeMirror-line > span > span')
|
||||
.should('have.text', '# Some short import test file')
|
||||
cy.get('.CodeMirror-code > div:nth-of-type(4) > .CodeMirror-line > span > span')
|
||||
|
|
13
package.json
13
package.json
|
@ -32,8 +32,8 @@
|
|||
"@types/react-router-bootstrap": "0.24.5",
|
||||
"@types/react-router-dom": "5.1.6",
|
||||
"@types/uuid": "8.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "3.10.1",
|
||||
"@typescript-eslint/parser": "3.10.1",
|
||||
"@typescript-eslint/eslint-plugin": "4.5.0",
|
||||
"@typescript-eslint/parser": "4.5.0",
|
||||
"abcjs": "5.12.0",
|
||||
"bootstrap": "4.5.3",
|
||||
"codemirror": "5.58.2",
|
||||
|
@ -42,8 +42,8 @@
|
|||
"diff": "4.0.2",
|
||||
"emoji-picker-element": "1.2.1",
|
||||
"emojibase-data": "5.1.1",
|
||||
"eslint-config-react-app": "5.2.1",
|
||||
"eslint-config-standard": "14.1.1",
|
||||
"eslint-config-react-app": "6.0.0",
|
||||
"eslint-config-standard": "15.0.0",
|
||||
"eslint-plugin-flowtype": "5.2.0",
|
||||
"eslint-plugin-import": "2.22.1",
|
||||
"eslint-plugin-jsx-a11y": "6.3.1",
|
||||
|
@ -93,7 +93,7 @@
|
|||
"react-router": "5.2.0",
|
||||
"react-router-bootstrap": "0.25.0",
|
||||
"react-router-dom": "5.2.0",
|
||||
"react-scripts": "3.4.4",
|
||||
"react-scripts": "4.0.0",
|
||||
"react-use": "15.3.4",
|
||||
"redux": "4.0.5",
|
||||
"ts-mockery": "1.2.0",
|
||||
|
@ -125,6 +125,9 @@
|
|||
"./tsconfig.json"
|
||||
]
|
||||
},
|
||||
"rules": {
|
||||
"no-use-before-define": "off"
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
|
|
|
@ -15,15 +15,15 @@ export const Branding: React.FC<BrandingProps> = ({ inline = false }) => {
|
|||
|
||||
return (
|
||||
<ShowIf condition={showBranding}>
|
||||
<strong className={`mx-1 ${inline ? 'inline-size' : 'regular-size'}`} >@</strong>
|
||||
<strong className={`mx-1 ${inline ? 'inline-size' : 'regular-size'}`}>@</strong>
|
||||
{
|
||||
branding.logo
|
||||
? <img
|
||||
src={branding.logo}
|
||||
alt={branding.name}
|
||||
title={branding.name}
|
||||
className={inline ? 'inline-size' : 'regular-size'}
|
||||
/>
|
||||
src={branding.logo}
|
||||
alt={branding.name}
|
||||
title={branding.name}
|
||||
className={inline ? 'inline-size' : 'regular-size'}
|
||||
/>
|
||||
: branding.name
|
||||
}
|
||||
</ShowIf>
|
||||
|
|
|
@ -2,11 +2,11 @@ import React from 'react'
|
|||
import { Trans } from 'react-i18next'
|
||||
import { IconButton, IconButtonProps } from './icon-button'
|
||||
|
||||
export interface TranslatedIconButton extends IconButtonProps {
|
||||
export interface TranslatedIconButtonProps extends IconButtonProps {
|
||||
i18nKey: string
|
||||
}
|
||||
|
||||
export const TranslatedIconButton: React.FC<TranslatedIconButton> = ({ i18nKey, ...props }) => {
|
||||
export const TranslatedIconButton: React.FC<TranslatedIconButtonProps> = ({ i18nKey, ...props }) => {
|
||||
return (
|
||||
<IconButton {...props}>
|
||||
<Trans i18nKey={i18nKey}/>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react'
|
||||
import { DateTime } from 'luxon'
|
||||
import React from 'react'
|
||||
import { ListGroup } from 'react-bootstrap'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { RevisionListEntry } from '../../../../api/revisions/types'
|
||||
|
@ -32,11 +32,11 @@ export const RevisionModalListEntry: React.FC<RevisionModalListEntryProps> = ({
|
|||
<span className={'d-flex flex-row my-1 align-items-center'}>
|
||||
<ForkAwesomeIcon icon={'user-o'} className={'mx-2'}/>
|
||||
{
|
||||
revisionAuthorListMap.get(revision.timestamp)?.map((user, index) => {
|
||||
return (
|
||||
<UserAvatar name={user.name} photo={user.photo} showName={false} additionalClasses={'mx-1'} key={index}/>
|
||||
)
|
||||
})
|
||||
revisionAuthorListMap.get(revision.timestamp)?.map((user, index) => {
|
||||
return (
|
||||
<UserAvatar name={user.name} photo={user.photo} showName={false} additionalClasses={'mx-1'} key={index}/>
|
||||
)
|
||||
})
|
||||
}
|
||||
</span>
|
||||
</ListGroup.Item>
|
||||
|
|
|
@ -61,32 +61,34 @@ const tab = (editor: Editor) => {
|
|||
}
|
||||
}
|
||||
|
||||
export const defaultKeyMap: KeyMap = !isMac ? {
|
||||
F10: f10,
|
||||
Esc: esc,
|
||||
'Ctrl-S': suppressSave,
|
||||
Enter: 'newlineAndIndentContinueMarkdownList',
|
||||
Tab: tab,
|
||||
Home: 'goLineLeftSmart',
|
||||
End: 'goLineRight',
|
||||
'Ctrl-I': makeSelectionItalic,
|
||||
'Ctrl-B': makeSelectionBold,
|
||||
'Ctrl-U': underlineSelection,
|
||||
'Ctrl-D': strikeThroughSelection,
|
||||
'Ctrl-M': markSelection
|
||||
} : {
|
||||
F10: f10,
|
||||
Esc: esc,
|
||||
'Cmd-S': suppressSave,
|
||||
Enter: 'newlineAndIndentContinueMarkdownList',
|
||||
Tab: tab,
|
||||
'Cmd-Left': 'goLineLeftSmart',
|
||||
'Cmd-Right': 'goLineRight',
|
||||
Home: 'goLineLeftSmart',
|
||||
End: 'goLineRight',
|
||||
'Cmd-I': makeSelectionItalic,
|
||||
'Cmd-B': makeSelectionBold,
|
||||
'Cmd-U': underlineSelection,
|
||||
'Cmd-D': strikeThroughSelection,
|
||||
'Cmd-M': markSelection
|
||||
}
|
||||
export const defaultKeyMap: KeyMap = !isMac
|
||||
? {
|
||||
F10: f10,
|
||||
Esc: esc,
|
||||
'Ctrl-S': suppressSave,
|
||||
Enter: 'newlineAndIndentContinueMarkdownList',
|
||||
Tab: tab,
|
||||
Home: 'goLineLeftSmart',
|
||||
End: 'goLineRight',
|
||||
'Ctrl-I': makeSelectionItalic,
|
||||
'Ctrl-B': makeSelectionBold,
|
||||
'Ctrl-U': underlineSelection,
|
||||
'Ctrl-D': strikeThroughSelection,
|
||||
'Ctrl-M': markSelection
|
||||
}
|
||||
: {
|
||||
F10: f10,
|
||||
Esc: esc,
|
||||
'Cmd-S': suppressSave,
|
||||
Enter: 'newlineAndIndentContinueMarkdownList',
|
||||
Tab: tab,
|
||||
'Cmd-Left': 'goLineLeftSmart',
|
||||
'Cmd-Right': 'goLineRight',
|
||||
Home: 'goLineLeftSmart',
|
||||
End: 'goLineRight',
|
||||
'Cmd-I': makeSelectionItalic,
|
||||
'Cmd-B': makeSelectionBold,
|
||||
'Cmd-U': underlineSelection,
|
||||
'Cmd-D': strikeThroughSelection,
|
||||
'Cmd-M': markSelection
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -14,7 +14,7 @@ export interface RawYAMLMetadata {
|
|||
disqus: string | undefined
|
||||
type: string | undefined
|
||||
slideOptions: unknown
|
||||
opengraph: { [key: string]:string }
|
||||
opengraph: { [key: string]:string } | null
|
||||
}
|
||||
|
||||
export class YAMLMetaData {
|
||||
|
|
|
@ -50,16 +50,16 @@ export const HistoryContent: React.FC<HistoryContentProps> = ({ viewState, entri
|
|||
|
||||
const mapViewStateToComponent = (viewState: ViewStateEnum) => {
|
||||
switch (viewState) {
|
||||
default:
|
||||
case ViewStateEnum.CARD:
|
||||
return <HistoryCardList entries={entries}
|
||||
case ViewStateEnum.TABLE:
|
||||
return <HistoryTable entries={entries}
|
||||
onPinClick={onPinClick}
|
||||
onRemoveClick={onRemoveClick}
|
||||
onDeleteClick={onDeleteClick}
|
||||
pageIndex={pageIndex}
|
||||
onLastPageIndexChange={setLastPageIndex}/>
|
||||
case ViewStateEnum.TABLE:
|
||||
return <HistoryTable entries={entries}
|
||||
case ViewStateEnum.CARD:
|
||||
default:
|
||||
return <HistoryCardList entries={entries}
|
||||
onPinClick={onPinClick}
|
||||
onRemoveClick={onRemoveClick}
|
||||
onDeleteClick={onDeleteClick}
|
||||
|
|
|
@ -11,13 +11,14 @@ export enum SortModeEnum {
|
|||
|
||||
const getIcon = (direction: SortModeEnum): IconName => {
|
||||
switch (direction) {
|
||||
default:
|
||||
case SortModeEnum.no:
|
||||
return 'sort'
|
||||
case SortModeEnum.up:
|
||||
return 'sort-asc'
|
||||
case SortModeEnum.down:
|
||||
return 'sort-desc'
|
||||
default:
|
||||
return 'sort'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,8 +33,8 @@ const toggleDirection = (direction: SortModeEnum) => {
|
|||
return SortModeEnum.up
|
||||
case SortModeEnum.up:
|
||||
return SortModeEnum.down
|
||||
default:
|
||||
case SortModeEnum.down:
|
||||
default:
|
||||
return SortModeEnum.no
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,17 +27,17 @@ const HeaderBar: React.FC = () => {
|
|||
<div className="d-inline-flex">
|
||||
{!userExists
|
||||
? <Fragment>
|
||||
<span className={'mx-1 d-flex'}>
|
||||
<NewGuestNoteButton/>
|
||||
</span>
|
||||
<SignInButton size="sm"/>
|
||||
</Fragment>
|
||||
<span className={'mx-1 d-flex'}>
|
||||
<NewGuestNoteButton/>
|
||||
</span>
|
||||
<SignInButton size="sm"/>
|
||||
</Fragment>
|
||||
: <Fragment>
|
||||
<span className={'mx-1 d-flex'}>
|
||||
<NewUserNoteButton/>
|
||||
</span>
|
||||
<UserDropdown/>
|
||||
</Fragment>
|
||||
<span className={'mx-1 d-flex'}>
|
||||
<NewUserNoteButton/>
|
||||
</span>
|
||||
<UserDropdown/>
|
||||
</Fragment>
|
||||
}
|
||||
</div>
|
||||
</Navbar>
|
||||
|
|
|
@ -39,10 +39,12 @@ export class FullMarkdownItConfigurator extends BasicMarkdownItConfigurator {
|
|||
tasksLists,
|
||||
(markdownIt) => {
|
||||
frontmatterExtract(markdownIt,
|
||||
!this.useFrontmatter ? undefined : {
|
||||
onYamlError: (error: boolean) => this.onYamlError(error),
|
||||
onRawMeta: (rawMeta: RawYAMLMetadata) => this.onRawMeta(rawMeta)
|
||||
})
|
||||
!this.useFrontmatter
|
||||
? undefined
|
||||
: {
|
||||
onYamlError: (error: boolean) => this.onYamlError(error),
|
||||
onRawMeta: (rawMeta: RawYAMLMetadata) => this.onRawMeta(rawMeta)
|
||||
})
|
||||
},
|
||||
headlineAnchors,
|
||||
KatexReplacer.markdownItPlugin,
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import mermaid from 'mermaid'
|
||||
import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { Alert } from 'react-bootstrap'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
@ -22,30 +21,37 @@ export const MermaidChart: React.FC<MermaidChartProps> = ({ code }) => {
|
|||
|
||||
useEffect(() => {
|
||||
if (!mermaidInitialized) {
|
||||
mermaid.initialize({ startOnLoad: false })
|
||||
mermaidInitialized = true
|
||||
import('mermaid').then((mermaid) => {
|
||||
mermaid.default.initialize({ startOnLoad: false })
|
||||
mermaidInitialized = true
|
||||
}).catch(() => { console.error('error while loading mermaid') })
|
||||
}
|
||||
}, [])
|
||||
|
||||
const showError = useCallback((error: string) => {
|
||||
setError(error)
|
||||
console.error(error)
|
||||
if (!diagramContainer.current) {
|
||||
return
|
||||
}
|
||||
setError(error)
|
||||
console.error(error)
|
||||
diagramContainer.current.querySelectorAll('svg').forEach(child => child.remove())
|
||||
}, [])
|
||||
}, [setError])
|
||||
|
||||
useEffect(() => {
|
||||
if (!diagramContainer.current) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
mermaid.parse(code)
|
||||
delete diagramContainer.current.dataset.processed
|
||||
diagramContainer.current.textContent = code
|
||||
mermaid.init(diagramContainer.current)
|
||||
setError(undefined)
|
||||
import('mermaid').then((mermaid) => {
|
||||
if (!diagramContainer.current) {
|
||||
return
|
||||
}
|
||||
mermaid.default.parse(code)
|
||||
delete diagramContainer.current.dataset.processed
|
||||
diagramContainer.current.textContent = code
|
||||
mermaid.default.init(diagramContainer.current)
|
||||
setError(undefined)
|
||||
}).catch(() => showError('Error while loading mermaid'))
|
||||
} catch (error) {
|
||||
const message = (error as MermaidParseError).str
|
||||
showError(message || t('renderer.mermaid.unknownError'))
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
declare module 'markdown-it-mathjax' {
|
||||
import MarkdownIt from 'markdown-it/lib'
|
||||
import { MathJaxOptions } from './interface'
|
||||
const markdownItMathJax: (MathJaxOptions) => MarkdownIt.PluginSimple
|
||||
export = markdownItMathJax
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import { PadViewOnly } from './components/pad-view-only/pad-view-only'
|
|||
import { ProfilePage } from './components/profile-page/profile-page'
|
||||
import { RegisterPage } from './components/register-page/register-page'
|
||||
import { store } from './redux'
|
||||
import * as serviceWorker from './service-worker'
|
||||
import * as serviceWorkerRegistration from './service-worker-registration'
|
||||
import './style/dark.scss'
|
||||
import './style/index.scss'
|
||||
|
||||
|
@ -77,4 +77,4 @@ ReactDOM.render(
|
|||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||
serviceWorker.unregister()
|
||||
serviceWorkerRegistration.unregister()
|
||||
|
|
144
src/service-worker-registration.ts
Normal file
144
src/service-worker-registration.ts
Normal file
|
@ -0,0 +1,144 @@
|
|||
// 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 localhostRegex = /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||
|
||||
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.
|
||||
localhostRegex.exec(window.location.hostname)
|
||||
)
|
||||
|
||||
type Config = {
|
||||
onSuccess?: (registration: ServiceWorkerRegistration) => void;
|
||||
onUpdate?: (registration: ServiceWorkerRegistration) => void;
|
||||
};
|
||||
|
||||
export function register (config?: Config): void {
|
||||
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. To learn more, visit https://cra.link/PWA'
|
||||
)
|
||||
}).catch((error: Error) => console.error(error))
|
||||
} 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()
|
||||
}).catch((error: Error) => console.error(error))
|
||||
}).catch((error: Error) => console.error(error))
|
||||
} 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 (): void {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready
|
||||
.then((registration) => {
|
||||
registration.unregister().catch((error: Error) => console.error(error))
|
||||
})
|
||||
.catch((error:Error) => {
|
||||
console.error(error.message)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,138 +1,81 @@
|
|||
// This optional code is used to register a service worker.
|
||||
// register() is not called by default.
|
||||
/// <reference lib="webworker" />
|
||||
/* eslint-disable no-restricted-globals */
|
||||
|
||||
// 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.
|
||||
// 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.
|
||||
|
||||
// To learn more about the benefits of this model and instructions on how to
|
||||
// opt-in, read https://bit.ly/CRA-PWA
|
||||
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'
|
||||
|
||||
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.
|
||||
new RegExp(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/).exec(window.location.hostname)
|
||||
)
|
||||
declare const self: ServiceWorkerGlobalScope
|
||||
|
||||
type Config = {
|
||||
onSuccess?: (registration: ServiceWorkerRegistration) => void;
|
||||
onUpdate?: (registration: ServiceWorkerRegistration) => void;
|
||||
};
|
||||
clientsClaim()
|
||||
|
||||
export function register (config?: Config):void {
|
||||
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
|
||||
// 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
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`
|
||||
// If this is a URL that starts with /_, skip.
|
||||
if (url.pathname.startsWith('/_')) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (isLocalhost) {
|
||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl, config)
|
||||
} else {
|
||||
// Is not localhost. Just register service worker
|
||||
registerValidSW(swUrl, config)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
// If this looks like a URL for a resource, because it contains
|
||||
// a file extension, skip.
|
||||
if (fileExtensionRegexp.exec(url.pathname)) {
|
||||
return false
|
||||
}
|
||||
|
||||
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://bit.ly/CRA-PWA.'
|
||||
)
|
||||
// Return true to signal that we want to use the handler.
|
||||
return true
|
||||
},
|
||||
createHandlerBoundToURL(process.env.PUBLIC_URL + '/index.html')
|
||||
)
|
||||
|
||||
// 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' }
|
||||
// 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 })
|
||||
]
|
||||
})
|
||||
.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.includes('javascript'))
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
return registration.unregister().then(() => {
|
||||
window.location.reload()
|
||||
})
|
||||
}).catch(() => console.log('Service worker not ready'))
|
||||
} 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 ():void {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready
|
||||
.then(registration => {
|
||||
return registration.unregister()
|
||||
})
|
||||
.catch((error:Error) => {
|
||||
console.error(error.message)
|
||||
})
|
||||
// This allows the web app to trigger skipWaiting via
|
||||
// registration.waiting.postMessage({type: 'SKIP_WAITING'})
|
||||
self.addEventListener('message', (event) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
if (event.data && event.data.type === 'SKIP_WAITING') {
|
||||
self.skipWaiting().catch((e) => console.error(e))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Any other custom service worker logic can go here.
|
||||
|
|
|
@ -1,25 +1,26 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue