diff --git a/frontend/locales/en.json b/frontend/locales/en.json
index a80ac44ba..f3542a663 100644
--- a/frontend/locales/en.json
+++ b/frontend/locales/en.json
@@ -45,6 +45,7 @@
"success": "Note has been created."
},
"error": {
+ "redirecting": "An error occurred while redirecting you to the primary address of this note",
"notFound": {
"title": "Note not found",
"description": "The requested note doesn't exist."
diff --git a/frontend/src/components/common/note-loading-boundary/note-loading-boundary.spec.tsx b/frontend/src/components/common/note-loading-boundary/note-loading-boundary.spec.tsx
index 10c676bec..e297b38b9 100644
--- a/frontend/src/components/common/note-loading-boundary/note-loading-boundary.spec.tsx
+++ b/frontend/src/components/common/note-loading-boundary/note-loading-boundary.spec.tsx
@@ -7,11 +7,13 @@ import { ApiError } from '../../../api/common/api-error'
import * as getNoteModule from '../../../api/notes'
import type { Note } from '../../../api/notes/types'
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 setNoteDataFromServerModule from '../../../redux/note-details/methods'
import { testId } from '../../../utils/test-id'
import * as CommonErrorPageModule from '../../error-pages/common-error-page'
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 { NoteLoadingBoundary } from './note-loading-boundary'
import { render, screen } from '@testing-library/react'
@@ -19,13 +21,20 @@ import { Fragment } from 'react'
import { Mock } from 'ts-mockery'
jest.mock('../../../hooks/common/use-single-string-url-parameter')
+jest.mock('../../../hooks/common/use-application-state')
jest.mock('../../../api/notes')
jest.mock('../../../redux/note-details/methods')
jest.mock('../../error-pages/common-error-page', () => ({
CommonErrorPage: jest.fn()
}))
jest.mock('../../../components/application-loader/loading-screen/loading-screen')
+jest.mock('../../notifications/ui-notification-boundary')
jest.mock('./create-non-existing-note-hint')
+jest.mock('next/router', () => ({
+ useRouter: () => ({
+ push: jest.fn()
+ })
+}))
describe('Note loading boundary', () => {
const mockedNoteId = 'mockedNoteId'
@@ -37,6 +46,7 @@ describe('Note loading boundary', () => {
beforeEach(async () => {
await mockI18n()
+ jest.spyOn(useApplicationStateModule, 'useApplicationState').mockReturnValue(mockedNoteId)
jest.spyOn(CreateNonExistingNoteHintModule, 'CreateNonExistingNoteHint').mockImplementation(() => {
return (
@@ -64,6 +74,11 @@ describe('Note loading boundary', () => {
)
})
+ jest.spyOn(useUiNotificationsModule, 'useUiNotifications').mockReturnValue({
+ showErrorNotification: jest.fn(),
+ dismissNotification: jest.fn(),
+ dispatchUiNotification: jest.fn()
+ })
mockGetNoteIdQueryParameter()
})
diff --git a/frontend/src/components/common/note-loading-boundary/note-loading-boundary.tsx b/frontend/src/components/common/note-loading-boundary/note-loading-boundary.tsx
index 30f17df89..4d1a6fe91 100644
--- a/frontend/src/components/common/note-loading-boundary/note-loading-boundary.tsx
+++ b/frontend/src/components/common/note-loading-boundary/note-loading-boundary.tsx
@@ -5,14 +5,18 @@
*/
import { ApiError } from '../../../api/common/api-error'
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 { 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 { ShowIf } from '../show-if/show-if'
import { CreateNonExistingNoteHint } from './create-non-existing-note-hint'
import { useLoadNoteFromServer } from './hooks/use-load-note-from-server'
+import { useRouter } from 'next/router'
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.
@@ -23,10 +27,40 @@ import React, { useEffect, useMemo } from 'react'
*/
export const NoteLoadingBoundary: React.FC = ({ children }) => {
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(() => {
+ if (primaryAddressChecked) {
+ return
+ }
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(() => {
if (error === undefined) {
@@ -50,7 +84,7 @@ export const NoteLoadingBoundary: React.FC = ({ children }) =
return (
}>