feat: map @username mentions when saving card descriptions

This commit is contained in:
lustsazeus-lab
2026-03-06 09:48:20 +08:00
parent f23ce6e025
commit 2413ddd1cd
2 changed files with 28 additions and 4 deletions
@@ -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(() => {
@@ -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(() => {