feat: Display card name when linking in task (#1234)

This commit is contained in:
Daniel Silvestre
2025-07-08 17:15:48 +02:00
committed by GitHub
parent b76ca9547f
commit 4d40af9c8a
5 changed files with 158 additions and 45 deletions

View File

@@ -16,7 +16,7 @@ import selectors from '../../../../selectors';
import entryActions from '../../../../entry-actions';
import { usePopupInClosableContext } from '../../../../hooks';
import { isListArchiveOrTrash } from '../../../../utils/record-helpers';
import { BoardMembershipRoles } from '../../../../constants/Enums';
import { BoardMembershipRoles, ListTypes } from '../../../../constants/Enums';
import { ClosableContext } from '../../../../contexts';
import EditName from './EditName';
import SelectAssigneeStep from './SelectAssigneeStep';
@@ -28,10 +28,30 @@ import styles from './Task.module.scss';
const Task = React.memo(({ id, index }) => {
const selectTaskById = useMemo(() => selectors.makeSelectTaskById(), []);
const selectCardById = useMemo(() => selectors.makeSelectCardById(), []);
const selectListById = useMemo(() => selectors.makeSelectListById(), []);
const task = useSelector((state) => selectTaskById(state, id));
const isLinkedCardCompleted = useSelector((state) => {
const regex = /\/cards\/([^/]+)/g;
const matches = task.name.matchAll(regex);
// eslint-disable-next-line no-restricted-syntax
for (const [, cardId] of matches) {
const card = selectCardById(state, cardId);
if (card) {
const list = selectListById(state, card.listId);
if (list && list.type === ListTypes.CLOSED) {
return true;
}
}
}
return false;
});
const { canEdit, canToggle } = useSelector((state) => {
const { listId } = selectors.selectCurrentCard(state);
const list = selectListById(state, listId);
@@ -84,6 +104,8 @@ const Task = React.memo(({ id, index }) => {
}, [id, dispatch]);
const isEditable = task.isPersisted && canEdit;
const isCompleted = task.isCompleted || isLinkedCardCompleted;
const isToggleDisabled = !task.isPersisted || !canToggle || isLinkedCardCompleted;
const handleClick = useCallback(() => {
if (isEditable) {
@@ -122,8 +144,8 @@ const Task = React.memo(({ id, index }) => {
>
<span className={styles.checkboxWrapper}>
<Checkbox
checked={task.isCompleted}
disabled={!task.isPersisted || !canToggle}
checked={isCompleted}
disabled={isToggleDisabled}
className={styles.checkbox}
onChange={handleToggleChange}
/>
@@ -138,9 +160,7 @@ const Task = React.memo(({ id, index }) => {
className={classNames(styles.text, canEdit && styles.textEditable)}
onClick={handleClick}
>
<span
className={classNames(styles.task, task.isCompleted && styles.taskCompleted)}
>
<span className={classNames(styles.task, isCompleted && styles.taskCompleted)}>
<Linkify linkStopPropagation>{task.name}</Linkify>
</span>
</span>

View File

@@ -14,7 +14,7 @@ import { useDidUpdate } from '../../../lib/hooks';
import selectors from '../../../selectors';
import { isListArchiveOrTrash } from '../../../utils/record-helpers';
import DroppableTypes from '../../../constants/DroppableTypes';
import { BoardMembershipRoles } from '../../../constants/Enums';
import { BoardMembershipRoles, ListTypes } from '../../../constants/Enums';
import { ClosableContext } from '../../../contexts';
import Task from './Task';
import AddTask from './AddTask';
@@ -23,12 +23,40 @@ import styles from './TaskList.module.scss';
const TaskList = React.memo(({ id }) => {
const selectTaskListById = useMemo(() => selectors.makeSelectTaskListById(), []);
const selectCardById = useMemo(() => selectors.makeSelectCardById(), []);
const selectListById = useMemo(() => selectors.makeSelectListById(), []);
const selectTasksByTaskListId = useMemo(() => selectors.makeSelectTasksByTaskListId(), []);
const taskList = useSelector((state) => selectTaskListById(state, id));
const tasks = useSelector((state) => selectTasksByTaskListId(state, id));
// TODO: move to selector?
const completedTasksTotal = useSelector((state) =>
tasks.reduce((result, task) => {
if (task.isCompleted) {
return result + 1;
}
const regex = /\/cards\/([^/]+)/g;
const matches = task.name.matchAll(regex);
// eslint-disable-next-line no-restricted-syntax
for (const [, cardId] of matches) {
const card = selectCardById(state, cardId);
if (card) {
const list = selectListById(state, card.listId);
if (list && list.type === ListTypes.CLOSED) {
return result + 1;
}
}
}
return result;
}, 0),
);
const canEdit = useSelector((state) => {
const { listId } = selectors.selectCurrentCard(state);
const list = selectListById(state, listId);
@@ -45,12 +73,6 @@ const TaskList = React.memo(({ id }) => {
const [isAddOpened, setIsAddOpened] = useState(false);
const [, , setIsClosableActive] = useContext(ClosableContext);
// TODO: move to selector?
const completedTasksTotal = useMemo(
() => tasks.reduce((result, task) => (task.isCompleted ? result + 1 : result), 0),
[tasks],
);
const handleAddClick = useCallback(() => {
setIsAddOpened(true);
}, []);