mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-14 15:14:56 -04: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": {
|
"parserOptions": {
|
||||||
"tsconfigRootDir": "",
|
"tsconfigRootDir": "",
|
||||||
"project": [
|
"project": [
|
||||||
"./cypress/tsconfig.json"
|
"./tsconfig.json"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
|
|
|
@ -14,7 +14,7 @@ describe('Import markdown file', () => {
|
||||||
cy.get('.import-md-file')
|
cy.get('.import-md-file')
|
||||||
.click()
|
.click()
|
||||||
cy.get('div[aria-labelledby="editor-menu-import"] > input[type=file]')
|
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')
|
cy.get('.CodeMirror-code > div:nth-of-type(1) > .CodeMirror-line > span > span')
|
||||||
.should('have.text', '# Some short import test file')
|
.should('have.text', '# Some short import test file')
|
||||||
cy.get('.CodeMirror-code > div:nth-of-type(2) > .CodeMirror-line > span > span')
|
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')
|
cy.get('.import-md-file')
|
||||||
.click()
|
.click()
|
||||||
cy.get('div[aria-labelledby="editor-menu-import"] > input[type=file]')
|
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')
|
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')
|
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')
|
cy.get('.CodeMirror-code > div:nth-of-type(3) > .CodeMirror-line > span > span')
|
||||||
.should('have.text', '# Some short import test file')
|
.should('have.text', '# Some short import test file')
|
||||||
cy.get('.CodeMirror-code > div:nth-of-type(4) > .CodeMirror-line > span > span')
|
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-bootstrap": "0.24.5",
|
||||||
"@types/react-router-dom": "5.1.6",
|
"@types/react-router-dom": "5.1.6",
|
||||||
"@types/uuid": "8.3.0",
|
"@types/uuid": "8.3.0",
|
||||||
"@typescript-eslint/eslint-plugin": "3.10.1",
|
"@typescript-eslint/eslint-plugin": "4.5.0",
|
||||||
"@typescript-eslint/parser": "3.10.1",
|
"@typescript-eslint/parser": "4.5.0",
|
||||||
"abcjs": "5.12.0",
|
"abcjs": "5.12.0",
|
||||||
"bootstrap": "4.5.3",
|
"bootstrap": "4.5.3",
|
||||||
"codemirror": "5.58.2",
|
"codemirror": "5.58.2",
|
||||||
|
@ -42,8 +42,8 @@
|
||||||
"diff": "4.0.2",
|
"diff": "4.0.2",
|
||||||
"emoji-picker-element": "1.2.1",
|
"emoji-picker-element": "1.2.1",
|
||||||
"emojibase-data": "5.1.1",
|
"emojibase-data": "5.1.1",
|
||||||
"eslint-config-react-app": "5.2.1",
|
"eslint-config-react-app": "6.0.0",
|
||||||
"eslint-config-standard": "14.1.1",
|
"eslint-config-standard": "15.0.0",
|
||||||
"eslint-plugin-flowtype": "5.2.0",
|
"eslint-plugin-flowtype": "5.2.0",
|
||||||
"eslint-plugin-import": "2.22.1",
|
"eslint-plugin-import": "2.22.1",
|
||||||
"eslint-plugin-jsx-a11y": "6.3.1",
|
"eslint-plugin-jsx-a11y": "6.3.1",
|
||||||
|
@ -93,7 +93,7 @@
|
||||||
"react-router": "5.2.0",
|
"react-router": "5.2.0",
|
||||||
"react-router-bootstrap": "0.25.0",
|
"react-router-bootstrap": "0.25.0",
|
||||||
"react-router-dom": "5.2.0",
|
"react-router-dom": "5.2.0",
|
||||||
"react-scripts": "3.4.4",
|
"react-scripts": "4.0.0",
|
||||||
"react-use": "15.3.4",
|
"react-use": "15.3.4",
|
||||||
"redux": "4.0.5",
|
"redux": "4.0.5",
|
||||||
"ts-mockery": "1.2.0",
|
"ts-mockery": "1.2.0",
|
||||||
|
@ -125,6 +125,9 @@
|
||||||
"./tsconfig.json"
|
"./tsconfig.json"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"rules": {
|
||||||
|
"no-use-before-define": "off"
|
||||||
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"@typescript-eslint"
|
"@typescript-eslint"
|
||||||
],
|
],
|
||||||
|
|
|
@ -15,15 +15,15 @@ export const Branding: React.FC<BrandingProps> = ({ inline = false }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ShowIf condition={showBranding}>
|
<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
|
branding.logo
|
||||||
? <img
|
? <img
|
||||||
src={branding.logo}
|
src={branding.logo}
|
||||||
alt={branding.name}
|
alt={branding.name}
|
||||||
title={branding.name}
|
title={branding.name}
|
||||||
className={inline ? 'inline-size' : 'regular-size'}
|
className={inline ? 'inline-size' : 'regular-size'}
|
||||||
/>
|
/>
|
||||||
: branding.name
|
: branding.name
|
||||||
}
|
}
|
||||||
</ShowIf>
|
</ShowIf>
|
||||||
|
|
|
@ -2,11 +2,11 @@ import React from 'react'
|
||||||
import { Trans } from 'react-i18next'
|
import { Trans } from 'react-i18next'
|
||||||
import { IconButton, IconButtonProps } from './icon-button'
|
import { IconButton, IconButtonProps } from './icon-button'
|
||||||
|
|
||||||
export interface TranslatedIconButton extends IconButtonProps {
|
export interface TranslatedIconButtonProps extends IconButtonProps {
|
||||||
i18nKey: string
|
i18nKey: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TranslatedIconButton: React.FC<TranslatedIconButton> = ({ i18nKey, ...props }) => {
|
export const TranslatedIconButton: React.FC<TranslatedIconButtonProps> = ({ i18nKey, ...props }) => {
|
||||||
return (
|
return (
|
||||||
<IconButton {...props}>
|
<IconButton {...props}>
|
||||||
<Trans i18nKey={i18nKey}/>
|
<Trans i18nKey={i18nKey}/>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react'
|
|
||||||
import { DateTime } from 'luxon'
|
import { DateTime } from 'luxon'
|
||||||
|
import React from 'react'
|
||||||
import { ListGroup } from 'react-bootstrap'
|
import { ListGroup } from 'react-bootstrap'
|
||||||
import { Trans } from 'react-i18next'
|
import { Trans } from 'react-i18next'
|
||||||
import { RevisionListEntry } from '../../../../api/revisions/types'
|
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'}>
|
<span className={'d-flex flex-row my-1 align-items-center'}>
|
||||||
<ForkAwesomeIcon icon={'user-o'} className={'mx-2'}/>
|
<ForkAwesomeIcon icon={'user-o'} className={'mx-2'}/>
|
||||||
{
|
{
|
||||||
revisionAuthorListMap.get(revision.timestamp)?.map((user, index) => {
|
revisionAuthorListMap.get(revision.timestamp)?.map((user, index) => {
|
||||||
return (
|
return (
|
||||||
<UserAvatar name={user.name} photo={user.photo} showName={false} additionalClasses={'mx-1'} key={index}/>
|
<UserAvatar name={user.name} photo={user.photo} showName={false} additionalClasses={'mx-1'} key={index}/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
</ListGroup.Item>
|
</ListGroup.Item>
|
||||||
|
|
|
@ -61,32 +61,34 @@ const tab = (editor: Editor) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultKeyMap: KeyMap = !isMac ? {
|
export const defaultKeyMap: KeyMap = !isMac
|
||||||
F10: f10,
|
? {
|
||||||
Esc: esc,
|
F10: f10,
|
||||||
'Ctrl-S': suppressSave,
|
Esc: esc,
|
||||||
Enter: 'newlineAndIndentContinueMarkdownList',
|
'Ctrl-S': suppressSave,
|
||||||
Tab: tab,
|
Enter: 'newlineAndIndentContinueMarkdownList',
|
||||||
Home: 'goLineLeftSmart',
|
Tab: tab,
|
||||||
End: 'goLineRight',
|
Home: 'goLineLeftSmart',
|
||||||
'Ctrl-I': makeSelectionItalic,
|
End: 'goLineRight',
|
||||||
'Ctrl-B': makeSelectionBold,
|
'Ctrl-I': makeSelectionItalic,
|
||||||
'Ctrl-U': underlineSelection,
|
'Ctrl-B': makeSelectionBold,
|
||||||
'Ctrl-D': strikeThroughSelection,
|
'Ctrl-U': underlineSelection,
|
||||||
'Ctrl-M': markSelection
|
'Ctrl-D': strikeThroughSelection,
|
||||||
} : {
|
'Ctrl-M': markSelection
|
||||||
F10: f10,
|
}
|
||||||
Esc: esc,
|
: {
|
||||||
'Cmd-S': suppressSave,
|
F10: f10,
|
||||||
Enter: 'newlineAndIndentContinueMarkdownList',
|
Esc: esc,
|
||||||
Tab: tab,
|
'Cmd-S': suppressSave,
|
||||||
'Cmd-Left': 'goLineLeftSmart',
|
Enter: 'newlineAndIndentContinueMarkdownList',
|
||||||
'Cmd-Right': 'goLineRight',
|
Tab: tab,
|
||||||
Home: 'goLineLeftSmart',
|
'Cmd-Left': 'goLineLeftSmart',
|
||||||
End: 'goLineRight',
|
'Cmd-Right': 'goLineRight',
|
||||||
'Cmd-I': makeSelectionItalic,
|
Home: 'goLineLeftSmart',
|
||||||
'Cmd-B': makeSelectionBold,
|
End: 'goLineRight',
|
||||||
'Cmd-U': underlineSelection,
|
'Cmd-I': makeSelectionItalic,
|
||||||
'Cmd-D': strikeThroughSelection,
|
'Cmd-B': makeSelectionBold,
|
||||||
'Cmd-M': markSelection
|
'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
|
disqus: string | undefined
|
||||||
type: string | undefined
|
type: string | undefined
|
||||||
slideOptions: unknown
|
slideOptions: unknown
|
||||||
opengraph: { [key: string]:string }
|
opengraph: { [key: string]:string } | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export class YAMLMetaData {
|
export class YAMLMetaData {
|
||||||
|
|
|
@ -50,16 +50,16 @@ export const HistoryContent: React.FC<HistoryContentProps> = ({ viewState, entri
|
||||||
|
|
||||||
const mapViewStateToComponent = (viewState: ViewStateEnum) => {
|
const mapViewStateToComponent = (viewState: ViewStateEnum) => {
|
||||||
switch (viewState) {
|
switch (viewState) {
|
||||||
default:
|
case ViewStateEnum.TABLE:
|
||||||
case ViewStateEnum.CARD:
|
return <HistoryTable entries={entries}
|
||||||
return <HistoryCardList entries={entries}
|
|
||||||
onPinClick={onPinClick}
|
onPinClick={onPinClick}
|
||||||
onRemoveClick={onRemoveClick}
|
onRemoveClick={onRemoveClick}
|
||||||
onDeleteClick={onDeleteClick}
|
onDeleteClick={onDeleteClick}
|
||||||
pageIndex={pageIndex}
|
pageIndex={pageIndex}
|
||||||
onLastPageIndexChange={setLastPageIndex}/>
|
onLastPageIndexChange={setLastPageIndex}/>
|
||||||
case ViewStateEnum.TABLE:
|
case ViewStateEnum.CARD:
|
||||||
return <HistoryTable entries={entries}
|
default:
|
||||||
|
return <HistoryCardList entries={entries}
|
||||||
onPinClick={onPinClick}
|
onPinClick={onPinClick}
|
||||||
onRemoveClick={onRemoveClick}
|
onRemoveClick={onRemoveClick}
|
||||||
onDeleteClick={onDeleteClick}
|
onDeleteClick={onDeleteClick}
|
||||||
|
|
|
@ -11,13 +11,14 @@ export enum SortModeEnum {
|
||||||
|
|
||||||
const getIcon = (direction: SortModeEnum): IconName => {
|
const getIcon = (direction: SortModeEnum): IconName => {
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
default:
|
|
||||||
case SortModeEnum.no:
|
case SortModeEnum.no:
|
||||||
return 'sort'
|
return 'sort'
|
||||||
case SortModeEnum.up:
|
case SortModeEnum.up:
|
||||||
return 'sort-asc'
|
return 'sort-asc'
|
||||||
case SortModeEnum.down:
|
case SortModeEnum.down:
|
||||||
return 'sort-desc'
|
return 'sort-desc'
|
||||||
|
default:
|
||||||
|
return 'sort'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,8 +33,8 @@ const toggleDirection = (direction: SortModeEnum) => {
|
||||||
return SortModeEnum.up
|
return SortModeEnum.up
|
||||||
case SortModeEnum.up:
|
case SortModeEnum.up:
|
||||||
return SortModeEnum.down
|
return SortModeEnum.down
|
||||||
default:
|
|
||||||
case SortModeEnum.down:
|
case SortModeEnum.down:
|
||||||
|
default:
|
||||||
return SortModeEnum.no
|
return SortModeEnum.no
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,17 +27,17 @@ const HeaderBar: React.FC = () => {
|
||||||
<div className="d-inline-flex">
|
<div className="d-inline-flex">
|
||||||
{!userExists
|
{!userExists
|
||||||
? <Fragment>
|
? <Fragment>
|
||||||
<span className={'mx-1 d-flex'}>
|
<span className={'mx-1 d-flex'}>
|
||||||
<NewGuestNoteButton/>
|
<NewGuestNoteButton/>
|
||||||
</span>
|
</span>
|
||||||
<SignInButton size="sm"/>
|
<SignInButton size="sm"/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
: <Fragment>
|
: <Fragment>
|
||||||
<span className={'mx-1 d-flex'}>
|
<span className={'mx-1 d-flex'}>
|
||||||
<NewUserNoteButton/>
|
<NewUserNoteButton/>
|
||||||
</span>
|
</span>
|
||||||
<UserDropdown/>
|
<UserDropdown/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</Navbar>
|
</Navbar>
|
||||||
|
|
|
@ -39,10 +39,12 @@ export class FullMarkdownItConfigurator extends BasicMarkdownItConfigurator {
|
||||||
tasksLists,
|
tasksLists,
|
||||||
(markdownIt) => {
|
(markdownIt) => {
|
||||||
frontmatterExtract(markdownIt,
|
frontmatterExtract(markdownIt,
|
||||||
!this.useFrontmatter ? undefined : {
|
!this.useFrontmatter
|
||||||
onYamlError: (error: boolean) => this.onYamlError(error),
|
? undefined
|
||||||
onRawMeta: (rawMeta: RawYAMLMetadata) => this.onRawMeta(rawMeta)
|
: {
|
||||||
})
|
onYamlError: (error: boolean) => this.onYamlError(error),
|
||||||
|
onRawMeta: (rawMeta: RawYAMLMetadata) => this.onRawMeta(rawMeta)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
headlineAnchors,
|
headlineAnchors,
|
||||||
KatexReplacer.markdownItPlugin,
|
KatexReplacer.markdownItPlugin,
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import mermaid from 'mermaid'
|
|
||||||
import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'
|
import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import { Alert } from 'react-bootstrap'
|
import { Alert } from 'react-bootstrap'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
@ -22,30 +21,37 @@ export const MermaidChart: React.FC<MermaidChartProps> = ({ code }) => {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!mermaidInitialized) {
|
if (!mermaidInitialized) {
|
||||||
mermaid.initialize({ startOnLoad: false })
|
import('mermaid').then((mermaid) => {
|
||||||
mermaidInitialized = true
|
mermaid.default.initialize({ startOnLoad: false })
|
||||||
|
mermaidInitialized = true
|
||||||
|
}).catch(() => { console.error('error while loading mermaid') })
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const showError = useCallback((error: string) => {
|
const showError = useCallback((error: string) => {
|
||||||
|
setError(error)
|
||||||
|
console.error(error)
|
||||||
if (!diagramContainer.current) {
|
if (!diagramContainer.current) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setError(error)
|
|
||||||
console.error(error)
|
|
||||||
diagramContainer.current.querySelectorAll('svg').forEach(child => child.remove())
|
diagramContainer.current.querySelectorAll('svg').forEach(child => child.remove())
|
||||||
}, [])
|
}, [setError])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!diagramContainer.current) {
|
if (!diagramContainer.current) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
mermaid.parse(code)
|
import('mermaid').then((mermaid) => {
|
||||||
delete diagramContainer.current.dataset.processed
|
if (!diagramContainer.current) {
|
||||||
diagramContainer.current.textContent = code
|
return
|
||||||
mermaid.init(diagramContainer.current)
|
}
|
||||||
setError(undefined)
|
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) {
|
} catch (error) {
|
||||||
const message = (error as MermaidParseError).str
|
const message = (error as MermaidParseError).str
|
||||||
showError(message || t('renderer.mermaid.unknownError'))
|
showError(message || t('renderer.mermaid.unknownError'))
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
declare module 'markdown-it-mathjax' {
|
declare module 'markdown-it-mathjax' {
|
||||||
import MarkdownIt from 'markdown-it/lib'
|
import MarkdownIt from 'markdown-it/lib'
|
||||||
import { MathJaxOptions } from './interface'
|
|
||||||
const markdownItMathJax: (MathJaxOptions) => MarkdownIt.PluginSimple
|
const markdownItMathJax: (MathJaxOptions) => MarkdownIt.PluginSimple
|
||||||
export = markdownItMathJax
|
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 { ProfilePage } from './components/profile-page/profile-page'
|
||||||
import { RegisterPage } from './components/register-page/register-page'
|
import { RegisterPage } from './components/register-page/register-page'
|
||||||
import { store } from './redux'
|
import { store } from './redux'
|
||||||
import * as serviceWorker from './service-worker'
|
import * as serviceWorkerRegistration from './service-worker-registration'
|
||||||
import './style/dark.scss'
|
import './style/dark.scss'
|
||||||
import './style/index.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
|
// If you want your app to work offline and load faster, you can change
|
||||||
// unregister() to register() below. Note this comes with some pitfalls.
|
// unregister() to register() below. Note this comes with some pitfalls.
|
||||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
// 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.
|
/// <reference lib="webworker" />
|
||||||
// register() is not called by default.
|
/* eslint-disable no-restricted-globals */
|
||||||
|
|
||||||
// This lets the app load faster on subsequent visits in production, and gives
|
// This service worker can be customized!
|
||||||
// it offline capabilities. However, it also means that developers (and users)
|
// See https://developers.google.com/web/tools/workbox/modules
|
||||||
// will only see deployed updates on subsequent visits to a page, after all the
|
// for the list of available Workbox modules, or add any other
|
||||||
// existing tabs open on the page have been closed, since previously cached
|
// code you'd like.
|
||||||
// resources are updated in the background.
|
// 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
|
import { clientsClaim } from 'workbox-core'
|
||||||
// opt-in, read https://bit.ly/CRA-PWA
|
import { ExpirationPlugin } from 'workbox-expiration'
|
||||||
|
import { precacheAndRoute, createHandlerBoundToURL } from 'workbox-precaching'
|
||||||
|
import { registerRoute } from 'workbox-routing'
|
||||||
|
import { StaleWhileRevalidate } from 'workbox-strategies'
|
||||||
|
|
||||||
const isLocalhost = Boolean(
|
declare const self: ServiceWorkerGlobalScope
|
||||||
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)
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config = {
|
clientsClaim()
|
||||||
onSuccess?: (registration: ServiceWorkerRegistration) => void;
|
|
||||||
onUpdate?: (registration: ServiceWorkerRegistration) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function register (config?: Config):void {
|
// Precache all of the assets generated by your build process.
|
||||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
// Their URLs are injected into the manifest variable below.
|
||||||
// The URL constructor is available in all browsers that support SW.
|
// This variable must be present somewhere in your service worker file,
|
||||||
const publicUrl = new URL(
|
// even if you decide not to use precaching. See https://cra.link/PWA
|
||||||
process.env.PUBLIC_URL,
|
precacheAndRoute(self.__WB_MANIFEST)
|
||||||
window.location.href
|
|
||||||
)
|
// Set up App Shell-style routing, so that all navigation requests
|
||||||
if (publicUrl.origin !== window.location.origin) {
|
// are fulfilled with your index.html shell. Learn more at
|
||||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
// https://developers.google.com/web/fundamentals/architecture/app-shell
|
||||||
// from what our page is served on. This might happen if a CDN is used to
|
const fileExtensionRegexp = new RegExp('/[^/?]+\\.[^/]+$')
|
||||||
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
registerRoute(
|
||||||
return
|
// 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', () => {
|
// If this is a URL that starts with /_, skip.
|
||||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`
|
if (url.pathname.startsWith('/_')) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if (isLocalhost) {
|
// If this looks like a URL for a resource, because it contains
|
||||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
// a file extension, skip.
|
||||||
checkValidServiceWorker(swUrl, config)
|
if (fileExtensionRegexp.exec(url.pathname)) {
|
||||||
} else {
|
return false
|
||||||
// Is not localhost. Just register service worker
|
}
|
||||||
registerValidSW(swUrl, config)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function registerValidSW (swUrl: string, config?: Config) {
|
// Return true to signal that we want to use the handler.
|
||||||
navigator.serviceWorker
|
return true
|
||||||
.register(swUrl)
|
},
|
||||||
.then(registration => {
|
createHandlerBoundToURL(process.env.PUBLIC_URL + '/index.html')
|
||||||
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.'
|
|
||||||
)
|
|
||||||
|
|
||||||
// Execute callback
|
// An example runtime caching route for requests that aren't handled by the
|
||||||
if (config && config.onUpdate) {
|
// precache, in this case same-origin .png requests like those from in public/
|
||||||
config.onUpdate(registration)
|
registerRoute(
|
||||||
}
|
// Add in any other file extensions or routing criteria as needed.
|
||||||
} else {
|
({ url }) => url.origin === self.location.origin && url.pathname.endsWith('.png'),
|
||||||
// At this point, everything has been precached.
|
// Customize this strategy as needed, e.g., by changing to CacheFirst.
|
||||||
// It's the perfect time to display a
|
new StaleWhileRevalidate({
|
||||||
// "Content is cached for offline use." message.
|
cacheName: 'images',
|
||||||
console.log('Content is cached for offline use.')
|
plugins: [
|
||||||
|
// Ensure that once this runtime cache reaches a maximum size the
|
||||||
// Execute callback
|
// least-recently used images are removed.
|
||||||
if (config && config.onSuccess) {
|
new ExpirationPlugin({ maxEntries: 50 })
|
||||||
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.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 {
|
// This allows the web app to trigger skipWaiting via
|
||||||
if ('serviceWorker' in navigator) {
|
// registration.waiting.postMessage({type: 'SKIP_WAITING'})
|
||||||
navigator.serviceWorker.ready
|
self.addEventListener('message', (event) => {
|
||||||
.then(registration => {
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||||
return registration.unregister()
|
if (event.data && event.data.type === 'SKIP_WAITING') {
|
||||||
})
|
self.skipWaiting().catch((e) => console.error(e))
|
||||||
.catch((error:Error) => {
|
|
||||||
console.error(error.message)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
|
// Any other custom service worker logic can go here.
|
||||||
|
|
|
@ -1,25 +1,26 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es5",
|
"target": "es5",
|
||||||
"lib": [
|
"lib": [
|
||||||
"dom",
|
"dom",
|
||||||
"dom.iterable",
|
"dom.iterable",
|
||||||
"esnext"
|
"esnext"
|
||||||
],
|
],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"module": "esnext",
|
"noFallthroughCasesInSwitch": true,
|
||||||
"moduleResolution": "node",
|
"module": "esnext",
|
||||||
"resolveJsonModule": true,
|
"moduleResolution": "node",
|
||||||
"isolatedModules": true,
|
"resolveJsonModule": true,
|
||||||
"noEmit": true,
|
"isolatedModules": true,
|
||||||
"jsx": "react"
|
"noEmit": true,
|
||||||
},
|
"jsx": "react"
|
||||||
"include": [
|
},
|
||||||
"src"
|
"include": [
|
||||||
]
|
"src"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue