mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-12 22:26:08 -04:00
fix(frontend): improve performance by caching translated texts
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
ced4cd953c
commit
76242330fd
81 changed files with 341 additions and 292 deletions
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { useTranslatedText } from '../../../../hooks/common/use-translated-text'
|
||||
import type { PropsWithDataCypressId } from '../../../../utils/cypress-attribute'
|
||||
import { cypressId } from '../../../../utils/cypress-attribute'
|
||||
import { UiIcon } from '../../icons/ui-icon'
|
||||
|
@ -11,7 +12,6 @@ import React, { Fragment, useRef } from 'react'
|
|||
import { Button } from 'react-bootstrap'
|
||||
import { Files as IconFiles } from 'react-bootstrap-icons'
|
||||
import type { Variant } from 'react-bootstrap/types'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export interface CopyToClipboardButtonProps extends PropsWithDataCypressId {
|
||||
content: string
|
||||
|
@ -33,10 +33,9 @@ export const CopyToClipboardButton: React.FC<CopyToClipboardButtonProps> = ({
|
|||
variant = 'dark',
|
||||
...props
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const button = useRef<HTMLButtonElement>(null)
|
||||
|
||||
const [copyToClipboard, overlayElement] = useCopyOverlay(button, content)
|
||||
const buttonTitle = useTranslatedText('renderer.highlightCode.copyCode')
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
|
@ -44,7 +43,7 @@ export const CopyToClipboardButton: React.FC<CopyToClipboardButtonProps> = ({
|
|||
ref={button}
|
||||
size={size}
|
||||
variant={variant}
|
||||
title={t('renderer.highlightCode.copyCode') ?? undefined}
|
||||
title={buttonTitle}
|
||||
onClick={copyToClipboard}
|
||||
{...cypressId(props)}>
|
||||
<UiIcon icon={IconFiles} />
|
||||
|
|
|
@ -3,10 +3,11 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { useTranslatedText } from '../../../hooks/common/use-translated-text'
|
||||
import type { CommonFieldProps } from './fields'
|
||||
import React from 'react'
|
||||
import { Form } from 'react-bootstrap'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { Trans } from 'react-i18next'
|
||||
|
||||
/**
|
||||
* Renders an input field for the current password when changing passwords.
|
||||
|
@ -14,7 +15,7 @@ import { Trans, useTranslation } from 'react-i18next'
|
|||
* @param onChange Hook that is called when the entered password changes.
|
||||
*/
|
||||
export const CurrentPasswordField: React.FC<CommonFieldProps> = ({ onChange }) => {
|
||||
const { t } = useTranslation()
|
||||
const placeholderText = useTranslatedText('login.auth.password')
|
||||
|
||||
return (
|
||||
<Form.Group>
|
||||
|
@ -25,7 +26,7 @@ export const CurrentPasswordField: React.FC<CommonFieldProps> = ({ onChange }) =
|
|||
type='password'
|
||||
size='sm'
|
||||
onChange={onChange}
|
||||
placeholder={t('login.auth.password') ?? undefined}
|
||||
placeholder={placeholderText}
|
||||
autoComplete='current-password'
|
||||
required
|
||||
/>
|
||||
|
|
|
@ -3,10 +3,11 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { useTranslatedText } from '../../../hooks/common/use-translated-text'
|
||||
import type { CommonFieldProps } from './fields'
|
||||
import React, { useMemo } from 'react'
|
||||
import { Form } from 'react-bootstrap'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { Trans } from 'react-i18next'
|
||||
|
||||
interface DisplayNameFieldProps extends CommonFieldProps {
|
||||
initialValue?: string
|
||||
|
@ -20,11 +21,8 @@ interface DisplayNameFieldProps extends CommonFieldProps {
|
|||
* @param initialValue The initial input field value.
|
||||
*/
|
||||
export const DisplayNameField: React.FC<DisplayNameFieldProps> = ({ onChange, value, initialValue }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const isValid = useMemo(() => {
|
||||
return value.trim() !== '' && value !== initialValue
|
||||
}, [value, initialValue])
|
||||
const isValid = useMemo(() => value.trim() !== '' && value !== initialValue, [value, initialValue])
|
||||
const placeholderText = useTranslatedText('profile.displayName')
|
||||
|
||||
return (
|
||||
<Form.Group>
|
||||
|
@ -37,7 +35,7 @@ export const DisplayNameField: React.FC<DisplayNameFieldProps> = ({ onChange, va
|
|||
value={value}
|
||||
isValid={isValid}
|
||||
onChange={onChange}
|
||||
placeholder={t('profile.displayName') ?? undefined}
|
||||
placeholder={placeholderText}
|
||||
autoComplete='name'
|
||||
required
|
||||
/>
|
||||
|
|
|
@ -3,10 +3,11 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { useTranslatedText } from '../../../hooks/common/use-translated-text'
|
||||
import type { CommonFieldProps } from './fields'
|
||||
import React, { useMemo } from 'react'
|
||||
import { Form } from 'react-bootstrap'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { Trans } from 'react-i18next'
|
||||
|
||||
/**
|
||||
* Renders an input field for the new password when registering.
|
||||
|
@ -15,11 +16,9 @@ import { Trans, useTranslation } from 'react-i18next'
|
|||
* @param value The currently entered password.
|
||||
*/
|
||||
export const NewPasswordField: React.FC<CommonFieldProps> = ({ onChange, value, hasError = false }) => {
|
||||
const { t } = useTranslation()
|
||||
const isValid = useMemo(() => value.trim() !== '', [value])
|
||||
|
||||
const isValid = useMemo(() => {
|
||||
return value.trim() !== ''
|
||||
}, [value])
|
||||
const placeholderText = useTranslatedText('login.auth.password')
|
||||
|
||||
return (
|
||||
<Form.Group>
|
||||
|
@ -32,7 +31,7 @@ export const NewPasswordField: React.FC<CommonFieldProps> = ({ onChange, value,
|
|||
isValid={isValid}
|
||||
isInvalid={hasError}
|
||||
onChange={onChange}
|
||||
placeholder={t('login.auth.password') ?? undefined}
|
||||
placeholder={placeholderText}
|
||||
autoComplete='new-password'
|
||||
required
|
||||
/>
|
||||
|
|
|
@ -3,10 +3,11 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { useTranslatedText } from '../../../hooks/common/use-translated-text'
|
||||
import type { CommonFieldProps } from './fields'
|
||||
import React, { useMemo } from 'react'
|
||||
import { Form } from 'react-bootstrap'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { Trans } from 'react-i18next'
|
||||
|
||||
interface PasswordAgainFieldProps extends CommonFieldProps {
|
||||
password: string
|
||||
|
@ -15,9 +16,10 @@ interface PasswordAgainFieldProps extends CommonFieldProps {
|
|||
/**
|
||||
* Renders an input field for typing the new password again when registering.
|
||||
*
|
||||
* @param onChange Hook that is called when the entered retype of the password changes.
|
||||
* @param value The currently entered retype of the password.
|
||||
* @param password The password entered into the password input field.
|
||||
* @param onChange Hook that is called when the entered retype of the password changes
|
||||
* @param value The currently entered retype of the password
|
||||
* @param password The password entered into the password input field
|
||||
* @param hasError Defines if the password should be shown as invalid
|
||||
*/
|
||||
export const PasswordAgainField: React.FC<PasswordAgainFieldProps> = ({
|
||||
onChange,
|
||||
|
@ -25,15 +27,9 @@ export const PasswordAgainField: React.FC<PasswordAgainFieldProps> = ({
|
|||
password,
|
||||
hasError = false
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const isInvalid = useMemo(() => {
|
||||
return value !== '' && password !== value && hasError
|
||||
}, [password, value, hasError])
|
||||
|
||||
const isValid = useMemo(() => {
|
||||
return password !== '' && password === value && !hasError
|
||||
}, [password, value, hasError])
|
||||
const isInvalid = useMemo(() => value !== '' && password !== value && hasError, [password, value, hasError])
|
||||
const isValid = useMemo(() => password !== '' && password === value && !hasError, [password, value, hasError])
|
||||
const placeholderText = useTranslatedText('login.register.passwordAgain')
|
||||
|
||||
return (
|
||||
<Form.Group>
|
||||
|
@ -46,7 +42,7 @@ export const PasswordAgainField: React.FC<PasswordAgainFieldProps> = ({
|
|||
isInvalid={isInvalid}
|
||||
isValid={isValid}
|
||||
onChange={onChange}
|
||||
placeholder={t('login.register.passwordAgain') ?? undefined}
|
||||
placeholder={placeholderText}
|
||||
autoComplete='new-password'
|
||||
required
|
||||
/>
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { useTranslatedText } from '../../../hooks/common/use-translated-text'
|
||||
import type { CommonFieldProps } from './fields'
|
||||
import React from 'react'
|
||||
import { Form } from 'react-bootstrap'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export interface UsernameFieldProps extends CommonFieldProps {
|
||||
isInvalid?: boolean
|
||||
|
@ -22,7 +22,7 @@ export interface UsernameFieldProps extends CommonFieldProps {
|
|||
* @param isInvalid Adds error style to label
|
||||
*/
|
||||
export const UsernameField: React.FC<UsernameFieldProps> = ({ onChange, value, isValid, isInvalid }) => {
|
||||
const { t } = useTranslation()
|
||||
const placeholderText = useTranslatedText('login.auth.username')
|
||||
|
||||
return (
|
||||
<Form.Control
|
||||
|
@ -32,7 +32,7 @@ export const UsernameField: React.FC<UsernameFieldProps> = ({ onChange, value, i
|
|||
isValid={isValid}
|
||||
isInvalid={isInvalid}
|
||||
onChange={onChange}
|
||||
placeholder={t('login.auth.username') ?? undefined}
|
||||
placeholder={placeholderText}
|
||||
autoComplete='username'
|
||||
autoFocus={true}
|
||||
required
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { UsernameField } from './username-field'
|
||||
import type { UsernameFieldProps } from './username-field'
|
||||
import { UsernameField } from './username-field'
|
||||
import React from 'react'
|
||||
import { Form } from 'react-bootstrap'
|
||||
import { Trans } from 'react-i18next'
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { testId } from '../../../utils/test-id'
|
||||
import { BootstrapLazyIcons } from './bootstrap-icons'
|
||||
import type { BootstrapIconName } from './bootstrap-icons'
|
||||
import { BootstrapLazyIcons } from './bootstrap-icons'
|
||||
import React, { Suspense, useMemo } from 'react'
|
||||
|
||||
export interface LazyBootstrapIconProps {
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { useTranslatedText } from '../../../hooks/common/use-translated-text'
|
||||
import { ExternalLink } from './external-link'
|
||||
import type { TranslatedLinkProps } from './types'
|
||||
import React, { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import React from 'react'
|
||||
|
||||
/**
|
||||
* An {@link ExternalLink external link} with translated text.
|
||||
|
@ -16,7 +16,6 @@ import { useTranslation } from 'react-i18next'
|
|||
* @param props Additional props directly given to the {@link ExternalLink}
|
||||
*/
|
||||
export const TranslatedExternalLink: React.FC<TranslatedLinkProps> = ({ i18nKey, i18nOption, ...props }) => {
|
||||
const { t } = useTranslation()
|
||||
const text = useMemo(() => (i18nOption ? t(i18nKey, i18nOption) : t(i18nKey)), [i18nKey, i18nOption, t])
|
||||
const text = useTranslatedText(i18nKey, i18nOption)
|
||||
return <ExternalLink text={text} {...props} />
|
||||
}
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { useTranslatedText } from '../../../hooks/common/use-translated-text'
|
||||
import { InternalLink } from './internal-link'
|
||||
import type { TranslatedLinkProps } from './types'
|
||||
import React, { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import React from 'react'
|
||||
|
||||
/**
|
||||
* An {@link InternalLink internal link} with translated text.
|
||||
|
@ -16,7 +16,7 @@ import { useTranslation } from 'react-i18next'
|
|||
* @param props Additional props directly given to the {@link InternalLink}
|
||||
*/
|
||||
export const TranslatedInternalLink: React.FC<TranslatedLinkProps> = ({ i18nKey, i18nOption, ...props }) => {
|
||||
const { t } = useTranslation()
|
||||
const text = useMemo(() => (i18nOption ? t(i18nKey, i18nOption) : t(i18nKey)), [i18nKey, i18nOption, t])
|
||||
const text = useTranslatedText(i18nKey, i18nOption)
|
||||
|
||||
return <InternalLink text={text} {...props} />
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import { RendererType } from '../../render-page/window-post-message-communicator
|
|||
import { CommonModal } from '../modals/common-modal'
|
||||
import { RendererIframe } from '../renderer-iframe/renderer-iframe'
|
||||
import { fetchMotd, MOTD_LOCAL_STORAGE_KEY } from './fetch-motd'
|
||||
import React, { useCallback, useMemo, useEffect, useState } from 'react'
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { Button, Modal } from 'react-bootstrap'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { useAsync } from 'react-use'
|
||||
|
|
|
@ -10,9 +10,11 @@ import { UiIcon } from '../icons/ui-icon'
|
|||
import { ShowIf } from '../show-if/show-if'
|
||||
import React, { useCallback, useEffect } from 'react'
|
||||
import { Alert, Button } from 'react-bootstrap'
|
||||
import { ArrowRepeat as IconArrowRepeat } from 'react-bootstrap-icons'
|
||||
import { CheckCircle as IconCheckCircle } from 'react-bootstrap-icons'
|
||||
import { ExclamationTriangle as IconExclamationTriangle } from 'react-bootstrap-icons'
|
||||
import {
|
||||
ArrowRepeat as IconArrowRepeat,
|
||||
CheckCircle as IconCheckCircle,
|
||||
ExclamationTriangle as IconExclamationTriangle
|
||||
} from 'react-bootstrap-icons'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { useAsyncFn } from 'react-use'
|
||||
|
||||
|
|
|
@ -15,9 +15,9 @@ import { useEditorReceiveHandler } from '../../render-page/window-post-message-c
|
|||
import type {
|
||||
ExtensionEvent,
|
||||
OnHeightChangeMessage,
|
||||
RendererType,
|
||||
SetScrollStateMessage
|
||||
} from '../../render-page/window-post-message-communicator/rendering-message'
|
||||
import type { RendererType } from '../../render-page/window-post-message-communicator/rendering-message'
|
||||
import { CommunicationMessageType } from '../../render-page/window-post-message-communicator/rendering-message'
|
||||
import { ShowIf } from '../show-if/show-if'
|
||||
import { WaitSpinner } from '../wait-spinner/wait-spinner'
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { useTranslatedText } from '../../../hooks/common/use-translated-text'
|
||||
import { ShowIf } from '../show-if/show-if'
|
||||
import defaultAvatar from './default-avatar.png'
|
||||
import styles from './user-avatar.module.scss'
|
||||
import React, { useCallback, useMemo } from 'react'
|
||||
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
|
||||
import type { OverlayInjectedProps } from 'react-bootstrap/Overlay'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export interface UserAvatarProps {
|
||||
size?: 'sm' | 'lg'
|
||||
|
@ -34,8 +34,6 @@ export const UserAvatar: React.FC<UserAvatarProps> = ({
|
|||
additionalClasses = '',
|
||||
showName = true
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const imageSize = useMemo(() => {
|
||||
switch (size) {
|
||||
case 'sm':
|
||||
|
@ -51,7 +49,13 @@ export const UserAvatar: React.FC<UserAvatarProps> = ({
|
|||
return photoUrl || defaultAvatar.src
|
||||
}, [photoUrl])
|
||||
|
||||
const imgDescription = useMemo(() => t('common.avatarOf', { name: displayName }), [t, displayName])
|
||||
const imageTranslateOptions = useMemo(
|
||||
() => ({
|
||||
name: displayName
|
||||
}),
|
||||
[displayName]
|
||||
)
|
||||
const imgDescription = useTranslatedText('common.avatarOf', imageTranslateOptions)
|
||||
|
||||
const tooltip = useCallback(
|
||||
(overlayInjectedProps: OverlayInjectedProps) => (
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue