From 194183a7eb05e3241c3084d3405d33dec91e3828 Mon Sep 17 00:00:00 2001 From: Tilman Vatteroth Date: Sun, 2 Jan 2022 19:35:39 +0100 Subject: [PATCH] Add browser meta tags for favicon and title (#1746) Signed-off-by: Tilman Vatteroth --- public/index.html | 25 ---------------- .../landing-layout/landing-layout.tsx | 3 -- src/components/layout/base-head.tsx | 25 ++++++++++++++++ src/components/layout/fav-icon.tsx | 29 +++++++++++++++++++ .../layout/note-and-app-title-head.tsx | 28 ++++++++++++++++++ ...use-document-title.ts => use-app-title.ts} | 13 +++++---- ...e-with-note-title.ts => use-note-title.ts} | 8 +++-- src/pages/_app.tsx | 2 ++ src/pages/n/[id].tsx | 4 +-- src/pages/p/[id].tsx | 18 +++++++----- src/pages/s/[id].tsx | 4 +-- 11 files changed, 111 insertions(+), 48 deletions(-) delete mode 100644 public/index.html create mode 100644 src/components/layout/base-head.tsx create mode 100644 src/components/layout/fav-icon.tsx create mode 100644 src/components/layout/note-and-app-title-head.tsx rename src/hooks/common/{use-document-title.ts => use-app-title.ts} (52%) rename src/hooks/common/{use-document-title-with-note-title.ts => use-note-title.ts} (68%) diff --git a/public/index.html b/public/index.html deleted file mode 100644 index 18874c0e9..000000000 --- a/public/index.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - HedgeDoc - - - - - - - - - - - - - - - - - -
- - diff --git a/src/components/landing-layout/landing-layout.tsx b/src/components/landing-layout/landing-layout.tsx index 03ca265f4..2e7bd7eea 100644 --- a/src/components/landing-layout/landing-layout.tsx +++ b/src/components/landing-layout/landing-layout.tsx @@ -6,15 +6,12 @@ import React, { Fragment } from 'react' import { Container } from 'react-bootstrap' -import { useDocumentTitle } from '../../hooks/common/use-document-title' import { MotdModal } from '../common/motd-modal/motd-modal' import { Footer } from './footer/footer' import { HeaderBar } from './navigation/header-bar/header-bar' import { UiNotifications } from '../notifications/ui-notifications' export const LandingLayout: React.FC = ({ children }) => { - useDocumentTitle() - return ( diff --git a/src/components/layout/base-head.tsx b/src/components/layout/base-head.tsx new file mode 100644 index 000000000..f4fe66f46 --- /dev/null +++ b/src/components/layout/base-head.tsx @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import React from 'react' +import Head from 'next/head' +import { useAppTitle } from '../../hooks/common/use-app-title' +import { FavIcon } from './fav-icon' + +/** + * Sets basic browser meta tags. + */ +export const BaseHead: React.FC = () => { + const appTitle = useAppTitle() + + return ( + + {appTitle} + + + + ) +} diff --git a/src/components/layout/fav-icon.tsx b/src/components/layout/fav-icon.tsx new file mode 100644 index 000000000..a6123bf89 --- /dev/null +++ b/src/components/layout/fav-icon.tsx @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import React, { Fragment } from 'react' + +/** + * Sets meta tags for the favicon. + */ +export const FavIcon: React.FC = () => { + return ( + + + + + + + + + + + + + + + ) +} diff --git a/src/components/layout/note-and-app-title-head.tsx b/src/components/layout/note-and-app-title-head.tsx new file mode 100644 index 000000000..5dee63694 --- /dev/null +++ b/src/components/layout/note-and-app-title-head.tsx @@ -0,0 +1,28 @@ +/* + * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import Head from 'next/head' +import React, { useMemo } from 'react' +import { useNoteTitle } from '../../hooks/common/use-note-title' +import { useAppTitle } from '../../hooks/common/use-app-title' + +/** + * Sets the note and app title for the browser window + */ +export const NoteAndAppTitleHead: React.FC = () => { + const noteTitle = useNoteTitle() + const appTitle = useAppTitle() + + const noteAndAppTitle = useMemo(() => { + return noteTitle + ' - ' + appTitle + }, [appTitle, noteTitle]) + + return ( + + {noteAndAppTitle} + + ) +} diff --git a/src/hooks/common/use-document-title.ts b/src/hooks/common/use-app-title.ts similarity index 52% rename from src/hooks/common/use-document-title.ts rename to src/hooks/common/use-app-title.ts index 8f759c843..a95ec09e7 100644 --- a/src/hooks/common/use-document-title.ts +++ b/src/hooks/common/use-app-title.ts @@ -4,13 +4,16 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { useEffect } from 'react' +import { useMemo } from 'react' import { useApplicationState } from './use-application-state' -export const useDocumentTitle = (title?: string): void => { +/** + * Returns the app title with branding if set. + */ +export const useAppTitle = (): string => { const brandingName = useApplicationState((state) => state.config.branding.name) - useEffect(() => { - document.title = `${title ? title + ' - ' : ''}HedgeDoc ${brandingName ? ` @ ${brandingName}` : ''}` - }, [brandingName, title]) + return useMemo(() => { + return 'HedgeDoc' + (brandingName ? ` @ ${brandingName}` : '') + }, [brandingName]) } diff --git a/src/hooks/common/use-document-title-with-note-title.ts b/src/hooks/common/use-note-title.ts similarity index 68% rename from src/hooks/common/use-document-title-with-note-title.ts rename to src/hooks/common/use-note-title.ts index c793209a8..252156e7c 100644 --- a/src/hooks/common/use-document-title-with-note-title.ts +++ b/src/hooks/common/use-note-title.ts @@ -6,13 +6,15 @@ import { useTranslation } from 'react-i18next' import { useApplicationState } from './use-application-state' -import { useDocumentTitle } from './use-document-title' import { useMemo } from 'react' -export const useDocumentTitleWithNoteTitle = (): void => { +/** + * Returns the title of the note or a placeholder text. + */ +export const useNoteTitle = (): string => { const { t } = useTranslation() const untitledNote = useMemo(() => t('editor.untitledNote'), [t]) const noteTitle = useApplicationState((state) => state.noteDetails.noteTitle) - useDocumentTitle(noteTitle === '' ? untitledNote : noteTitle) + return useMemo(() => (noteTitle === '' ? untitledNote : noteTitle), [noteTitle, untitledNote]) } diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index d4febd9e5..8ec38a1a3 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -11,6 +11,7 @@ import { ApplicationLoader } from '../components/application-loader/application- import '../../global-styles/dark.scss' import '../../global-styles/index.scss' import type { NextPage } from 'next' +import { BaseHead } from '../components/layout/base-head' /** * The actual hedgedoc next js app. @@ -19,6 +20,7 @@ import type { NextPage } from 'next' const HedgeDocApp: NextPage = ({ Component, pageProps }: AppProps) => { return ( + diff --git a/src/pages/n/[id].tsx b/src/pages/n/[id].tsx index 08872d9b1..c1de2e545 100644 --- a/src/pages/n/[id].tsx +++ b/src/pages/n/[id].tsx @@ -7,7 +7,6 @@ import React, { Suspense, useCallback, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { useApplyDarkMode } from '../../hooks/common/use-apply-dark-mode' -import { useDocumentTitleWithNoteTitle } from '../../hooks/common/use-document-title-with-note-title' import { setCheckboxInMarkdownContent, updateNoteTitleByFirstHeading } from '../../redux/note-details/methods' import { MotdModal } from '../../components/common/motd-modal/motd-modal' import { ShowIf } from '../../components/common/show-if/show-if' @@ -32,6 +31,7 @@ import { NoteType } from '../../redux/note-details/types/note-details' import type { NextPage } from 'next' import { isClientSideRendering } from '../../utils/is-client-side-rendering' import { LoadingScreen } from '../../components/application-loader/loading-screen' +import { NoteAndAppTitleHead } from '../../components/layout/note-and-app-title-head' const EditorPane = React.lazy(() => import('../../components/editor-page/editor-pane/editor-pane')) @@ -88,7 +88,6 @@ export const EditorPage: NextPage = () => { useViewModeShortcuts() useApplyDarkMode() - useDocumentTitleWithNoteTitle() useEditorModeFromUrl() const [error, loading] = useLoadNoteFromServer() @@ -137,6 +136,7 @@ export const EditorPage: NextPage = () => { return ( +
diff --git a/src/pages/p/[id].tsx b/src/pages/p/[id].tsx index a3a910cf1..66aa990df 100644 --- a/src/pages/p/[id].tsx +++ b/src/pages/p/[id].tsx @@ -4,23 +4,25 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import React from 'react' +import React, { Fragment } from 'react' import { useLoadNoteFromServer } from '../../components/editor-page/hooks/useLoadNoteFromServer' import { ShowIf } from '../../components/common/show-if/show-if' import { EditorToRendererCommunicatorContextProvider } from '../../components/editor-page/render-context/editor-to-renderer-communicator-context-provider' import { SlideShowPageContent } from '../../components/slide-show-page/slide-show-page-content' -import { useDocumentTitleWithNoteTitle } from '../../hooks/common/use-document-title-with-note-title' +import { NoteAndAppTitleHead } from '../../components/layout/note-and-app-title-head' export const SlideShowPage: React.FC = () => { const [error, loading] = useLoadNoteFromServer() - useDocumentTitleWithNoteTitle() return ( - - - - - + + + + + + + + ) } diff --git a/src/pages/s/[id].tsx b/src/pages/s/[id].tsx index 807697ba4..70296583e 100644 --- a/src/pages/s/[id].tsx +++ b/src/pages/s/[id].tsx @@ -6,7 +6,6 @@ import React from 'react' import { useApplyDarkMode } from '../../hooks/common/use-apply-dark-mode' -import { useDocumentTitleWithNoteTitle } from '../../hooks/common/use-document-title-with-note-title' import { MotdModal } from '../../components/common/motd-modal/motd-modal' import { ShowIf } from '../../components/common/show-if/show-if' import { AppBar, AppBarMode } from '../../components/editor-page/app-bar/app-bar' @@ -16,16 +15,17 @@ import { LoadingNoteAlert } from '../../components/document-read-only-page/Loadi import { EditorToRendererCommunicatorContextProvider } from '../../components/editor-page/render-context/editor-to-renderer-communicator-context-provider' import { UiNotifications } from '../../components/notifications/ui-notifications' import { DocumentReadOnlyPageContent } from '../../components/document-read-only-page/document-read-only-page-content' +import { NoteAndAppTitleHead } from '../../components/layout/note-and-app-title-head' /** * Renders a page that contains only the rendered document without an editor or realtime updates. */ export const DocumentReadOnlyPage: React.FC = () => { useApplyDarkMode() - useDocumentTitleWithNoteTitle() const [error, loading] = useLoadNoteFromServer() return ( +