diff --git a/services/web/frontend/js/features/file-view/components/file-view-icons.tsx b/services/web/frontend/js/features/file-view/components/file-view-icons.tsx index 168f7be5e8..e19e06e22f 100644 --- a/services/web/frontend/js/features/file-view/components/file-view-icons.tsx +++ b/services/web/frontend/js/features/file-view/components/file-view-icons.tsx @@ -1,7 +1,7 @@ import MaterialIcon, { IconProps } from '@/shared/components/material-icon' export const LinkedFileIcon = ( - props: Omit + props: Omit ) => { return ( ) -} +}) function Hotkey({ combination, diff --git a/services/web/frontend/js/features/ide-react/components/editor-survey.tsx b/services/web/frontend/js/features/ide-react/components/editor-survey.tsx index edde0dd8da..c12cd0b95d 100644 --- a/services/web/frontend/js/features/ide-react/components/editor-survey.tsx +++ b/services/web/frontend/js/features/ide-react/components/editor-survey.tsx @@ -7,20 +7,20 @@ import { OLToast } from '@/shared/components/ol/ol-toast' import { OLToastContainer } from '@/shared/components/ol/ol-toast-container' import { useEditorContext } from '@/shared/context/editor-context' import useTutorial from '@/shared/hooks/promotions/use-tutorial' -import { useCallback, useEffect, useMemo, useState } from 'react' +import { memo, useCallback, useEffect, useMemo, useState } from 'react' import { sendMB } from '@/infrastructure/event-tracking' import { useIsNewEditorEnabled } from '@/features/ide-redesign/utils/new-editor-utils' import { useTranslation } from 'react-i18next' type EditorSurveyPage = 'ease-of-use' | 'meets-my-needs' | 'thank-you' -export default function EditorSurvey() { +export default memo(function EditorSurvey() { return ( ) -} +}) const TUTORIAL_KEY = 'editor-popup-ux-survey' diff --git a/services/web/frontend/js/features/ide-react/components/global-toasts.tsx b/services/web/frontend/js/features/ide-react/components/global-toasts.tsx index 8b2262aeaa..eedd88596d 100644 --- a/services/web/frontend/js/features/ide-react/components/global-toasts.tsx +++ b/services/web/frontend/js/features/ide-react/components/global-toasts.tsx @@ -1,6 +1,6 @@ import { OLToast, OLToastProps } from '@/shared/components/ol/ol-toast' import useEventListener from '@/shared/hooks/use-event-listener' -import { Fragment, ReactElement, useCallback, useState } from 'react' +import { Fragment, memo, ReactElement, useCallback, useState } from 'react' import { debugConsole } from '@/utils/debugging' import importOverleafModules from '../../../../macros/import-overleaf-module.macro' @@ -30,7 +30,7 @@ const GENERATOR_MAP: Map = new Map( let toastCounter = 1 -export const GlobalToasts = () => { +export const GlobalToasts = memo(function GlobalToasts() { const [toasts, setToasts] = useState< { component: ReactElement; id: string }[] >([]) @@ -96,4 +96,4 @@ export const GlobalToasts = () => { ))} ) -} +}) diff --git a/services/web/frontend/js/features/review-panel/components/review-tooltip-menu.tsx b/services/web/frontend/js/features/review-panel/components/review-tooltip-menu.tsx index 654f090061..5cade9c670 100644 --- a/services/web/frontend/js/features/review-panel/components/review-tooltip-menu.tsx +++ b/services/web/frontend/js/features/review-panel/components/review-tooltip-menu.tsx @@ -1,6 +1,7 @@ import { CSSProperties, FC, + memo, useCallback, useEffect, useMemo, @@ -94,166 +95,168 @@ const ReviewTooltipMenu: FC = () => { ) } -const ReviewTooltipMenuContent: FC<{ onAddComment: () => void }> = ({ - onAddComment, -}) => { - const { t } = useTranslation() - const view = useCodeMirrorViewContext() - const state = useCodeMirrorStateContext() - const { reviewPanelOpen } = useLayoutContext() - const ranges = useRangesContext() - const { acceptChanges, rejectChanges } = useRangesActionsContext() - const { showGenericConfirmModal } = useModalsContext() - const { wantTrackChanges } = useEditorPropertiesContext() - const [tooltipStyle, setTooltipStyle] = useState() - const [visible, setVisible] = useState(false) +const ReviewTooltipMenuContent = memo<{ onAddComment: () => void }>( + function ReviewTooltipMenuContent({ onAddComment }) { + const { t } = useTranslation() + const view = useCodeMirrorViewContext() + const state = useCodeMirrorStateContext() + const { reviewPanelOpen } = useLayoutContext() + const ranges = useRangesContext() + const { acceptChanges, rejectChanges } = useRangesActionsContext() + const { showGenericConfirmModal } = useModalsContext() + const { wantTrackChanges } = useEditorPropertiesContext() + const [tooltipStyle, setTooltipStyle] = useState< + CSSProperties | undefined + >() + const [visible, setVisible] = useState(false) - const changesInSelection = useMemo(() => { - return (ranges?.changes ?? []).filter(({ op }) => { - const opFrom = op.p - const opLength = isInsertOperation(op) ? op.i.length : 0 - const opTo = opFrom + opLength - const selection = state.selection.main - return opFrom >= selection.from && opTo <= selection.to - }) - }, [ranges, state.selection.main]) + const changesInSelection = useMemo(() => { + return (ranges?.changes ?? []).filter(({ op }) => { + const opFrom = op.p + const opLength = isInsertOperation(op) ? op.i.length : 0 + const opTo = opFrom + opLength + const selection = state.selection.main + return opFrom >= selection.from && opTo <= selection.to + }) + }, [ranges, state.selection.main]) - const acceptChangesHandler = useCallback(() => { - const nChanges = numberOfChangesInSelection( + const acceptChangesHandler = useCallback(() => { + const nChanges = numberOfChangesInSelection( + ranges, + view.state.selection.main + ) + showGenericConfirmModal({ + message: t('confirm_accept_selected_changes', { count: nChanges }), + title: t('accept_selected_changes'), + onConfirm: async () => { + await acceptChanges(...changesInSelection) + }, + primaryVariant: 'danger', + }) + }, [ + acceptChanges, + changesInSelection, ranges, - view.state.selection.main - ) - showGenericConfirmModal({ - message: t('confirm_accept_selected_changes', { count: nChanges }), - title: t('accept_selected_changes'), - onConfirm: async () => { - await acceptChanges(...changesInSelection) - }, - primaryVariant: 'danger', - }) - }, [ - acceptChanges, - changesInSelection, - ranges, - showGenericConfirmModal, - view, - t, - ]) + showGenericConfirmModal, + view, + t, + ]) - const rejectChangesHandler = useCallback(() => { - const nChanges = numberOfChangesInSelection( + const rejectChangesHandler = useCallback(() => { + const nChanges = numberOfChangesInSelection( + ranges, + view.state.selection.main + ) + showGenericConfirmModal({ + message: t('confirm_reject_selected_changes', { count: nChanges }), + title: t('reject_selected_changes'), + onConfirm: async () => { + await rejectChanges(...changesInSelection) + }, + primaryVariant: 'danger', + }) + }, [ + showGenericConfirmModal, + t, ranges, - view.state.selection.main - ) - showGenericConfirmModal({ - message: t('confirm_reject_selected_changes', { count: nChanges }), - title: t('reject_selected_changes'), - onConfirm: async () => { - await rejectChanges(...changesInSelection) - }, - primaryVariant: 'danger', - }) - }, [ - showGenericConfirmModal, - t, - ranges, - view, - rejectChanges, - changesInSelection, - ]) + view, + rejectChanges, + changesInSelection, + ]) - const showChangesButtons = changesInSelection.length > 0 + const showChangesButtons = changesInSelection.length > 0 - useEffect(() => { - view.requestMeasure({ - key: 'review-tooltip-outside-viewport', - read(view) { - const cursorCoords = view.coordsAtPos(view.state.selection.main.head) + useEffect(() => { + view.requestMeasure({ + key: 'review-tooltip-outside-viewport', + read(view) { + const cursorCoords = view.coordsAtPos(view.state.selection.main.head) - if (!cursorCoords) { - return - } + if (!cursorCoords) { + return + } - const scrollDomRect = view.scrollDOM.getBoundingClientRect() - const contentDomRect = view.contentDOM.getBoundingClientRect() - const editorRightPos = contentDomRect.right - CM_LINE_RIGHT_PADDING + const scrollDomRect = view.scrollDOM.getBoundingClientRect() + const contentDomRect = view.contentDOM.getBoundingClientRect() + const editorRightPos = contentDomRect.right - CM_LINE_RIGHT_PADDING - if ( - cursorCoords.top > scrollDomRect.top && - cursorCoords.top < scrollDomRect.bottom - ) { - return - } + if ( + cursorCoords.top > scrollDomRect.top && + cursorCoords.top < scrollDomRect.bottom + ) { + return + } - return { - position: 'fixed' as const, - top: scrollDomRect.top + EDIT_MODE_SWITCH_WIDGET_HEIGHT, - right: window.innerWidth - editorRightPos, - } - }, - write(res) { - setTooltipStyle(res) - }, - }) - }, [view, reviewPanelOpen, wantTrackChanges]) + return { + position: 'fixed' as const, + top: scrollDomRect.top + EDIT_MODE_SWITCH_WIDGET_HEIGHT, + right: window.innerWidth - editorRightPos, + } + }, + write(res) { + setTooltipStyle(res) + }, + }) + }, [view, reviewPanelOpen, wantTrackChanges]) - useEffect(() => { - setVisible(false) - const timeout = setTimeout(() => { - setVisible(true) - }, TOOLTIP_SHOW_DELAY) + useEffect(() => { + setVisible(false) + const timeout = setTimeout(() => { + setVisible(true) + }, TOOLTIP_SHOW_DELAY) - return () => { - clearTimeout(timeout) - } - }, []) + return () => { + clearTimeout(timeout) + } + }, []) - return ( -
- - {showChangesButtons && ( - <> -
- - + {showChangesButtons && ( + <> +
+ - - - + + - - - - - )} -
- ) -} + +
+ + )} +
+ ) + } +) export default ReviewTooltipMenu diff --git a/services/web/frontend/js/shared/components/material-icon.tsx b/services/web/frontend/js/shared/components/material-icon.tsx index a5bd97e83c..81792bb474 100644 --- a/services/web/frontend/js/shared/components/material-icon.tsx +++ b/services/web/frontend/js/shared/components/material-icon.tsx @@ -1,5 +1,5 @@ import classNames from 'classnames' -import React from 'react' +import React, { memo } from 'react' import unfilledIconTypes from '../../../fonts/material-symbols/unfilled-symbols.mjs' export type AvailableUnfilledIcon = (typeof unfilledIconTypes)[number] @@ -53,4 +53,4 @@ function MaterialIcon({ ) } -export default MaterialIcon +export default memo(MaterialIcon)