mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-21 17:26:29 -05:00
feat(notes): redirect note to its primary address
Signed-off-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
parent
c0d4d233da
commit
4b82031464
3 changed files with 53 additions and 3 deletions
|
@ -45,6 +45,7 @@
|
||||||
"success": "Note has been created."
|
"success": "Note has been created."
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
|
"redirecting": "An error occurred while redirecting you to the primary address of this note",
|
||||||
"notFound": {
|
"notFound": {
|
||||||
"title": "Note not found",
|
"title": "Note not found",
|
||||||
"description": "The requested note doesn't exist."
|
"description": "The requested note doesn't exist."
|
||||||
|
|
|
@ -7,11 +7,13 @@ import { ApiError } from '../../../api/common/api-error'
|
||||||
import * as getNoteModule from '../../../api/notes'
|
import * as getNoteModule from '../../../api/notes'
|
||||||
import type { Note } from '../../../api/notes/types'
|
import type { Note } from '../../../api/notes/types'
|
||||||
import * as LoadingScreenModule from '../../../components/application-loader/loading-screen/loading-screen'
|
import * as LoadingScreenModule from '../../../components/application-loader/loading-screen/loading-screen'
|
||||||
|
import * as useApplicationStateModule from '../../../hooks/common/use-application-state'
|
||||||
import * as useSingleStringUrlParameterModule from '../../../hooks/common/use-single-string-url-parameter'
|
import * as useSingleStringUrlParameterModule from '../../../hooks/common/use-single-string-url-parameter'
|
||||||
import * as setNoteDataFromServerModule from '../../../redux/note-details/methods'
|
import * as setNoteDataFromServerModule from '../../../redux/note-details/methods'
|
||||||
import { testId } from '../../../utils/test-id'
|
import { testId } from '../../../utils/test-id'
|
||||||
import * as CommonErrorPageModule from '../../error-pages/common-error-page'
|
import * as CommonErrorPageModule from '../../error-pages/common-error-page'
|
||||||
import { mockI18n } from '../../markdown-renderer/test-utils/mock-i18n'
|
import { mockI18n } from '../../markdown-renderer/test-utils/mock-i18n'
|
||||||
|
import * as useUiNotificationsModule from '../../notifications/ui-notification-boundary'
|
||||||
import * as CreateNonExistingNoteHintModule from './create-non-existing-note-hint'
|
import * as CreateNonExistingNoteHintModule from './create-non-existing-note-hint'
|
||||||
import { NoteLoadingBoundary } from './note-loading-boundary'
|
import { NoteLoadingBoundary } from './note-loading-boundary'
|
||||||
import { render, screen } from '@testing-library/react'
|
import { render, screen } from '@testing-library/react'
|
||||||
|
@ -19,13 +21,20 @@ import { Fragment } from 'react'
|
||||||
import { Mock } from 'ts-mockery'
|
import { Mock } from 'ts-mockery'
|
||||||
|
|
||||||
jest.mock('../../../hooks/common/use-single-string-url-parameter')
|
jest.mock('../../../hooks/common/use-single-string-url-parameter')
|
||||||
|
jest.mock('../../../hooks/common/use-application-state')
|
||||||
jest.mock('../../../api/notes')
|
jest.mock('../../../api/notes')
|
||||||
jest.mock('../../../redux/note-details/methods')
|
jest.mock('../../../redux/note-details/methods')
|
||||||
jest.mock('../../error-pages/common-error-page', () => ({
|
jest.mock('../../error-pages/common-error-page', () => ({
|
||||||
CommonErrorPage: jest.fn()
|
CommonErrorPage: jest.fn()
|
||||||
}))
|
}))
|
||||||
jest.mock('../../../components/application-loader/loading-screen/loading-screen')
|
jest.mock('../../../components/application-loader/loading-screen/loading-screen')
|
||||||
|
jest.mock('../../notifications/ui-notification-boundary')
|
||||||
jest.mock('./create-non-existing-note-hint')
|
jest.mock('./create-non-existing-note-hint')
|
||||||
|
jest.mock('next/router', () => ({
|
||||||
|
useRouter: () => ({
|
||||||
|
push: jest.fn()
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
|
||||||
describe('Note loading boundary', () => {
|
describe('Note loading boundary', () => {
|
||||||
const mockedNoteId = 'mockedNoteId'
|
const mockedNoteId = 'mockedNoteId'
|
||||||
|
@ -37,6 +46,7 @@ describe('Note loading boundary', () => {
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await mockI18n()
|
await mockI18n()
|
||||||
|
jest.spyOn(useApplicationStateModule, 'useApplicationState').mockReturnValue(mockedNoteId)
|
||||||
jest.spyOn(CreateNonExistingNoteHintModule, 'CreateNonExistingNoteHint').mockImplementation(() => {
|
jest.spyOn(CreateNonExistingNoteHintModule, 'CreateNonExistingNoteHint').mockImplementation(() => {
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
|
@ -64,6 +74,11 @@ describe('Note loading boundary', () => {
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
jest.spyOn(useUiNotificationsModule, 'useUiNotifications').mockReturnValue({
|
||||||
|
showErrorNotification: jest.fn(),
|
||||||
|
dismissNotification: jest.fn(),
|
||||||
|
dispatchUiNotification: jest.fn()
|
||||||
|
})
|
||||||
mockGetNoteIdQueryParameter()
|
mockGetNoteIdQueryParameter()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,18 @@
|
||||||
*/
|
*/
|
||||||
import { ApiError } from '../../../api/common/api-error'
|
import { ApiError } from '../../../api/common/api-error'
|
||||||
import { ErrorToI18nKeyMapper } from '../../../api/common/error-to-i18n-key-mapper'
|
import { ErrorToI18nKeyMapper } from '../../../api/common/error-to-i18n-key-mapper'
|
||||||
|
import { useApplicationState } from '../../../hooks/common/use-application-state'
|
||||||
|
import { useSingleStringUrlParameter } from '../../../hooks/common/use-single-string-url-parameter'
|
||||||
import { LoadingScreen } from '../../application-loader/loading-screen/loading-screen'
|
import { LoadingScreen } from '../../application-loader/loading-screen/loading-screen'
|
||||||
import { CommonErrorPage } from '../../error-pages/common-error-page'
|
import { CommonErrorPage } from '../../error-pages/common-error-page'
|
||||||
|
import { useUiNotifications } from '../../notifications/ui-notification-boundary'
|
||||||
import { CustomAsyncLoadingBoundary } from '../async-loading-boundary/custom-async-loading-boundary'
|
import { CustomAsyncLoadingBoundary } from '../async-loading-boundary/custom-async-loading-boundary'
|
||||||
import { ShowIf } from '../show-if/show-if'
|
import { ShowIf } from '../show-if/show-if'
|
||||||
import { CreateNonExistingNoteHint } from './create-non-existing-note-hint'
|
import { CreateNonExistingNoteHint } from './create-non-existing-note-hint'
|
||||||
import { useLoadNoteFromServer } from './hooks/use-load-note-from-server'
|
import { useLoadNoteFromServer } from './hooks/use-load-note-from-server'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
import type { PropsWithChildren } from 'react'
|
import type { PropsWithChildren } from 'react'
|
||||||
import React, { useEffect, useMemo } from 'react'
|
import React, { useEffect, useMemo, useState } from 'react'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the note identified by the note-id in the URL.
|
* Loads the note identified by the note-id in the URL.
|
||||||
|
@ -23,10 +27,40 @@ import React, { useEffect, useMemo } from 'react'
|
||||||
*/
|
*/
|
||||||
export const NoteLoadingBoundary: React.FC<PropsWithChildren> = ({ children }) => {
|
export const NoteLoadingBoundary: React.FC<PropsWithChildren> = ({ children }) => {
|
||||||
const [{ error, loading, value }, loadNoteFromServer] = useLoadNoteFromServer()
|
const [{ error, loading, value }, loadNoteFromServer] = useLoadNoteFromServer()
|
||||||
|
const noteId = useSingleStringUrlParameter('noteId', '')
|
||||||
|
const primaryNoteAddress = useApplicationState((state) => state.noteDetails.primaryAddress)
|
||||||
|
const router = useRouter()
|
||||||
|
const { showErrorNotification } = useUiNotifications()
|
||||||
|
const [primaryAddressChecked, setPrimaryAddressChecked] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (primaryAddressChecked) {
|
||||||
|
return
|
||||||
|
}
|
||||||
loadNoteFromServer()
|
loadNoteFromServer()
|
||||||
}, [loadNoteFromServer])
|
}, [loadNoteFromServer, primaryAddressChecked])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!value || primaryAddressChecked) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (noteId !== primaryNoteAddress) {
|
||||||
|
router
|
||||||
|
.replace(`/n/${primaryNoteAddress}`, undefined, { shallow: true })
|
||||||
|
.then(() => setPrimaryAddressChecked(true))
|
||||||
|
.catch(showErrorNotification('noteLoadingBoundary.error.redirecting'))
|
||||||
|
} else {
|
||||||
|
setPrimaryAddressChecked(true)
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
value,
|
||||||
|
primaryNoteAddress,
|
||||||
|
noteId,
|
||||||
|
router,
|
||||||
|
showErrorNotification,
|
||||||
|
primaryAddressChecked,
|
||||||
|
setPrimaryAddressChecked
|
||||||
|
])
|
||||||
|
|
||||||
const errorComponent = useMemo(() => {
|
const errorComponent = useMemo(() => {
|
||||||
if (error === undefined) {
|
if (error === undefined) {
|
||||||
|
@ -50,7 +84,7 @@ export const NoteLoadingBoundary: React.FC<PropsWithChildren> = ({ children }) =
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CustomAsyncLoadingBoundary
|
<CustomAsyncLoadingBoundary
|
||||||
loading={loading || !value}
|
loading={loading || !value || !primaryAddressChecked}
|
||||||
error={error}
|
error={error}
|
||||||
errorComponent={errorComponent}
|
errorComponent={errorComponent}
|
||||||
loadingComponent={<LoadingScreen />}>
|
loadingComponent={<LoadingScreen />}>
|
||||||
|
|
Loading…
Reference in a new issue