diff --git a/client/src/components/cards/CardModal/ProjectContent.jsx b/client/src/components/cards/CardModal/ProjectContent.jsx index 99f1f033..a85b3cac 100644 --- a/client/src/components/cards/CardModal/ProjectContent.jsx +++ b/client/src/components/cards/CardModal/ProjectContent.jsx @@ -3,6 +3,7 @@ * Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md */ +import keyBy from 'lodash/keyBy'; import React, { useCallback, useContext, useMemo, useState } from 'react'; import classNames from 'classnames'; import { shallowEqual, useDispatch, useSelector } from 'react-redux'; @@ -15,6 +16,7 @@ import entryActions from '../../../entry-actions'; import { usePopupInClosableContext } from '../../../hooks'; import { startStopwatch, stopStopwatch } from '../../../utils/stopwatch'; import { isUsableMarkdownElement } from '../../../utils/element-helpers'; +import { mentionTextToMarkup } from '../../../utils/mentions'; import { BoardMembershipRoles, CardTypes, ListTypes } from '../../../constants/Enums'; import { CardTypeIcons } from '../../../constants/Icons'; import { ClosableContext } from '../../../contexts'; @@ -49,6 +51,7 @@ const ProjectContent = React.memo(() => { const card = useSelector(selectors.selectCurrentCard); const board = useSelector(selectors.selectCurrentBoard); + const boardMemberships = useSelector(selectors.selectMembershipsForCurrentBoard); const userIds = useSelector(selectors.selectUserIdsForCurrentCard); const labelIds = useSelector(selectors.selectLabelIdsForCurrentCard); const attachmentIds = useSelector(selectors.selectAttachmentIdsForCurrentCard); @@ -141,6 +144,15 @@ const ProjectContent = React.memo(() => { }, shallowEqual); const dispatch = useDispatch(); + const userByUsername = useMemo( + () => + keyBy( + boardMemberships.flatMap(({ user }) => (user.username ? user : [])), + ({ username }) => username.toLowerCase(), + ), + [boardMemberships], + ); + const [t] = useTranslation(); const [descriptionDraft, setDescriptionDraft] = useState(null); const [isEditDescriptionOpened, setIsEditDescriptionOpened] = useState(false); @@ -168,11 +180,11 @@ const ProjectContent = React.memo(() => { (description) => { dispatch( entryActions.updateCurrentCard({ - description, + description: description && mentionTextToMarkup(description, userByUsername), }), ); }, - [dispatch], + [dispatch, userByUsername], ); const handleDueCompletionChange = useCallback(() => { diff --git a/client/src/components/cards/CardModal/StoryContent.jsx b/client/src/components/cards/CardModal/StoryContent.jsx index a7150e83..02ee6437 100644 --- a/client/src/components/cards/CardModal/StoryContent.jsx +++ b/client/src/components/cards/CardModal/StoryContent.jsx @@ -3,6 +3,7 @@ * Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md */ +import keyBy from 'lodash/keyBy'; import React, { useCallback, useContext, useMemo, useState } from 'react'; import classNames from 'classnames'; import { shallowEqual, useDispatch, useSelector } from 'react-redux'; @@ -15,6 +16,7 @@ import selectors from '../../../selectors'; import entryActions from '../../../entry-actions'; import { usePopupInClosableContext } from '../../../hooks'; import { isUsableMarkdownElement } from '../../../utils/element-helpers'; +import { mentionTextToMarkup } from '../../../utils/mentions'; import { BoardMembershipRoles, CardTypes, ListTypes } from '../../../constants/Enums'; import { CardTypeIcons } from '../../../constants/Icons'; import { ClosableContext } from '../../../contexts'; @@ -45,6 +47,7 @@ const StoryContent = React.memo(() => { const card = useSelector(selectors.selectCurrentCard); const board = useSelector(selectors.selectCurrentBoard); + const boardMemberships = useSelector(selectors.selectMembershipsForCurrentBoard); const userIds = useSelector(selectors.selectUserIdsForCurrentCard); const labelIds = useSelector(selectors.selectLabelIdsForCurrentCard); const attachmentIds = useSelector(selectors.selectAttachmentIdsForCurrentCard); @@ -136,6 +139,15 @@ const StoryContent = React.memo(() => { }, shallowEqual); const dispatch = useDispatch(); + const userByUsername = useMemo( + () => + keyBy( + boardMemberships.flatMap(({ user }) => (user.username ? user : [])), + ({ username }) => username.toLowerCase(), + ), + [boardMemberships], + ); + const [t] = useTranslation(); const [descriptionDraft, setDescriptionDraft] = useState(null); const [isEditDescriptionOpened, setIsEditDescriptionOpened] = useState(false); @@ -163,11 +175,11 @@ const StoryContent = React.memo(() => { (description) => { dispatch( entryActions.updateCurrentCard({ - description, + description: description && mentionTextToMarkup(description, userByUsername), }), ); }, - [dispatch], + [dispatch, userByUsername], ); const handleRestoreClick = useCallback(() => {