feat: Add ability to move lists between boards (#1208)

This commit is contained in:
Symon Baikov
2025-09-04 01:07:10 +03:00
committed by GitHub
parent 5f34a737bb
commit 9683227fbc
58 changed files with 950 additions and 263 deletions

View File

@@ -58,10 +58,34 @@ updateList.failure = (id, error) => ({
},
});
const handleListUpdate = (list) => ({
const handleListUpdate = (
list,
isFetched,
users,
cards,
cardMemberships,
cardLabels,
taskLists,
tasks,
attachments,
customFieldGroups,
customFields,
customFieldValues,
) => ({
type: ActionTypes.LIST_UPDATE_HANDLE,
payload: {
list,
isFetched,
users,
cards,
cardMemberships,
cardLabels,
taskLists,
tasks,
attachments,
customFieldGroups,
customFields,
customFieldValues,
},
});

View File

@@ -16,6 +16,7 @@ import { useSteps } from '../../../hooks';
import { ListTypes } from '../../../constants/Enums';
import EditColorStep from './EditColorStep';
import SortStep from './SortStep';
import MoveStep from './MoveStep';
import SelectListTypeStep from '../SelectListTypeStep';
import ConfirmationStep from '../../common/ConfirmationStep';
import ArchiveCardsStep from '../../cards/ArchiveCardsStep';
@@ -26,6 +27,7 @@ const StepTypes = {
EDIT_TYPE: 'EDIT_TYPE',
EDIT_COLOR: 'EDIT_COLOR',
SORT: 'SORT',
MOVE: 'MOVE',
ARCHIVE_CARDS: 'ARCHIVE_CARDS',
DELETE: 'DELETE',
};
@@ -76,6 +78,10 @@ const ActionsStep = React.memo(({ listId, onNameEdit, onCardAdd, onClose }) => {
openStep(StepTypes.SORT);
}, [openStep]);
const handleMoveClick = useCallback(() => {
openStep(StepTypes.MOVE);
}, [openStep]);
const handleArchiveCardsClick = useCallback(() => {
openStep(StepTypes.ARCHIVE_CARDS);
}, [openStep]);
@@ -102,6 +108,8 @@ const ActionsStep = React.memo(({ listId, onNameEdit, onCardAdd, onClose }) => {
return <EditColorStep listId={listId} onBack={handleBack} onClose={onClose} />;
case StepTypes.SORT:
return <SortStep listId={listId} onBack={handleBack} onClose={onClose} />;
case StepTypes.MOVE:
return <MoveStep id={listId} onBack={handleBack} onClose={onClose} />;
case StepTypes.ARCHIVE_CARDS:
return <ArchiveCardsStep listId={listId} onBack={handleBack} onClose={onClose} />;
case StepTypes.DELETE:
@@ -157,6 +165,12 @@ const ActionsStep = React.memo(({ listId, onNameEdit, onCardAdd, onClose }) => {
context: 'title',
})}
</Menu.Item>
<Menu.Item className={styles.menuItem} onClick={handleMoveClick}>
<Icon name="share square outline" className={styles.menuItemIcon} />
{t('action.moveList', {
context: 'title',
})}
</Menu.Item>
{list.type === ListTypes.CLOSED && (
<Menu.Item className={styles.menuItem} onClick={handleArchiveCardsClick}>
<Icon name="folder open outline" className={styles.menuItemIcon} />

View File

@@ -0,0 +1,133 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import React, { useMemo, useCallback } from 'react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import { useTranslation } from 'react-i18next';
import { Button, Dropdown, Form } from 'semantic-ui-react';
import { Popup } from '../../../lib/custom-ui';
import selectors from '../../../selectors';
import entryActions from '../../../entry-actions';
import { useForm } from '../../../hooks';
import styles from './MoveStep.module.scss';
const MoveStep = React.memo(({ id, onBack, onClose }) => {
const selectListById = useMemo(() => selectors.makeSelectListById(), []);
const selectBoardById = useMemo(() => selectors.makeSelectBoardById(), []);
const projectsToBoards = useSelector(
selectors.selectProjectsToBoardsWithEditorRightsForCurrentUser,
);
const list = useSelector((state) => selectListById(state, id));
const projectId = useSelector((state) => selectBoardById(state, list.boardId).projectId);
const dispatch = useDispatch();
const [t] = useTranslation();
const defaultPath = useMemo(
() => ({
projectId,
boardId: list.boardId,
}),
[list.boardId, projectId],
);
const [path, handleFieldChange] = useForm(() => ({
projectId: null,
boardId: null,
...defaultPath,
}));
const selectedProject = useMemo(
() => projectsToBoards.find((project) => project.id === path.projectId) || null,
[projectsToBoards, path.projectId],
);
const selectedBoard = useMemo(
() =>
(selectedProject && selectedProject.boards.find((board) => board.id === path.boardId)) ||
null,
[selectedProject, path.boardId],
);
const handleSubmit = useCallback(() => {
if (selectedBoard.id !== defaultPath.boardId) {
dispatch(entryActions.transferList(id, selectedBoard.id));
}
onClose();
}, [id, onClose, dispatch, defaultPath, selectedBoard]);
return (
<>
<Popup.Header onBack={onBack}>
{t('common.moveList', {
context: 'title',
})}
</Popup.Header>
<Popup.Content>
<Form onSubmit={handleSubmit}>
<div className={styles.text}>{t('common.project')}</div>
<Dropdown
fluid
selection
name="projectId"
options={projectsToBoards.map((project) => ({
text: project.name,
value: project.id,
}))}
value={selectedProject && selectedProject.id}
placeholder={
projectsToBoards.length === 0 ? t('common.noProjects') : t('common.selectProject')
}
disabled={projectsToBoards.length === 0}
className={styles.field}
onChange={handleFieldChange}
/>
{selectedProject && (
<>
<div className={styles.text}>{t('common.board')}</div>
<Dropdown
fluid
selection
name="boardId"
options={selectedProject.boards.map((board) => ({
text: board.name,
value: board.id,
}))}
value={selectedBoard && selectedBoard.id}
placeholder={
selectedProject.boards.length === 0
? t('common.noBoards')
: t('common.selectBoard')
}
disabled={selectedProject.boards.length === 0}
className={styles.field}
onChange={handleFieldChange}
/>
</>
)}
<Button positive content={t('action.move')} disabled={!selectedBoard} />
</Form>
</Popup.Content>
</>
);
});
MoveStep.propTypes = {
id: PropTypes.string.isRequired,
onBack: PropTypes.func,
onClose: PropTypes.func.isRequired,
};
MoveStep.defaultProps = {
onBack: undefined,
};
export default MoveStep;

View File

@@ -0,0 +1,17 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
:global(#app) {
.field {
margin-bottom: 8px;
}
.text {
color: #444444;
font-size: 12px;
font-weight: bold;
padding-bottom: 6px;
}
}

View File

@@ -164,6 +164,7 @@ export default {
LIST_UPDATE: `${PREFIX}/LIST_UPDATE`,
LIST_UPDATE_HANDLE: `${PREFIX}/LIST_UPDATE_HANDLE`,
LIST_MOVE: `${PREFIX}/LIST_MOVE`,
LIST_TRANSFER: `${PREFIX}/LIST_TRANSFER`,
LIST_SORT: `${PREFIX}/LIST_SORT`,
LIST_CARDS_TO_ARCHIVE_LIST_MOVE: `${PREFIX}/LIST_CARDS_TO_ARCHIVE_LIST_MOVE`,
TRASH_LIST_IN_CURRENT_BOARD_CLEAR: `${PREFIX}/TRASH_LIST_IN_CURRENT_BOARD_CLEAR`,

View File

@@ -42,6 +42,15 @@ const moveList = (id, index) => ({
},
});
const transferList = (id, boardId, index = 0) => ({
type: EntryActionTypes.LIST_TRANSFER,
payload: {
id,
boardId,
index,
},
});
const sortList = (id, data) => {
return {
type: EntryActionTypes.LIST_SORT,
@@ -92,6 +101,7 @@ export default {
updateList,
handleListUpdate,
moveList,
transferList,
sortList,
moveListCardsToArchiveList,
clearTrashListInCurrentBoard,

View File

@@ -204,6 +204,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'نقل البطاقة',
moveList_title: null,
myOwn_title: null,
name: 'الاسم',
newEmail: 'بريد إلكتروني جديد',
@@ -408,6 +409,7 @@ export default {
makeProjectShared_title: null,
move: 'نقل',
moveCard_title: 'نقل البطاقة',
moveList_title: null,
remove: 'حذف',
removeAssignee: null,
removeColor: null,

View File

@@ -208,6 +208,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Преместване на карта',
moveList_title: null,
myOwn_title: null,
name: 'Име',
newEmail: 'Нов имейл',
@@ -413,6 +414,7 @@ export default {
makeProjectShared_title: null,
move: 'Преместване',
moveCard_title: 'Преместване на карта',
moveList_title: null,
remove: 'Премахване',
removeAssignee: null,
removeColor: null,

View File

@@ -217,6 +217,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Přesunout kartu',
moveList_title: null,
myOwn_title: 'Moje vlastní',
name: 'Jméno',
newEmail: 'Nový e-mail',
@@ -423,6 +424,7 @@ export default {
makeProjectShared_title: 'Vytvořit sdílený projekt',
move: 'Přesunout',
moveCard_title: 'Přesunout kartu',
moveList_title: null,
remove: 'Odstranit',
removeAssignee: 'Odstranit přiřazení',
removeColor: 'Smazat barvu',

View File

@@ -223,6 +223,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Flyt kort',
moveList_title: null,
myOwn_title: 'Egne',
name: 'Navn',
newEmail: 'Ny e-mail',
@@ -433,6 +434,7 @@ export default {
makeProjectShared_title: 'Del projekt',
move: 'Flyt',
moveCard_title: 'Flyt kort',
moveList_title: null,
remove: 'Fjern',
removeAssignee: 'Fjern ansvarlig',
removeColor: 'Fjern farve',

View File

@@ -232,6 +232,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Karte verschieben',
moveList_title: null,
myOwn_title: 'Meine eigenen',
name: 'Name',
newEmail: 'Neue E-Mail-Adresse',
@@ -438,6 +439,7 @@ export default {
makeProjectShared_title: 'Projekt freigeben',
move: 'Verschieben',
moveCard_title: 'Karte bewegen',
moveList_title: null,
remove: 'Löschen',
removeAssignee: 'Zuständigen entfernen',
removeColor: 'Farbe löschen',

View File

@@ -233,6 +233,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Μετακίνηση κάρτας',
moveList_title: null,
myOwn_title: 'Δικά μου',
name: 'Όνομα',
newEmail: 'Νέο e-mail',
@@ -449,6 +450,7 @@ export default {
makeProjectShared_title: 'Κάντε το έργο κοινόχρηστο',
move: 'Μετακίνηση',
moveCard_title: 'Μετακίνηση κάρτας',
moveList_title: null,
remove: 'Αφαίρεση',
removeAssignee: 'Αφαίρεση υπευθύνου',
removeColor: 'Αφαίρεση χρώματος',

View File

@@ -222,6 +222,7 @@ export default {
moreActions: 'More actions',
moreActions_title: 'More Actions',
moveCard_title: 'Move Card',
moveList_title: 'Move List',
myOwn_title: 'My Own',
name: 'Name',
newEmail: 'New e-mail',
@@ -432,6 +433,7 @@ export default {
makeProjectShared_title: 'Make Project Shared',
move: 'Move',
moveCard_title: 'Move Card',
moveList_title: 'Move List',
remove: 'Remove',
removeAssignee: 'Remove assignee',
removeColor: 'Remove color',

View File

@@ -217,6 +217,7 @@ export default {
moreActions: 'More actions',
moreActions_title: 'More Actions',
moveCard_title: 'Move Card',
moveList_title: 'Move List',
myOwn_title: 'My Own',
name: 'Name',
newEmail: 'New e-mail',
@@ -427,6 +428,7 @@ export default {
makeProjectShared_title: 'Make Project Shared',
move: 'Move',
moveCard_title: 'Move Card',
moveList_title: 'Move List',
remove: 'Remove',
removeAssignee: 'Remove assignee',
removeColor: 'Remove color',

View File

@@ -223,6 +223,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Mover tarjeta',
moveList_title: null,
myOwn_title: 'Propios',
name: 'Nombre',
newEmail: 'Nuevo correo',
@@ -432,6 +433,7 @@ export default {
makeProjectShared_title: 'Hacer proyecto compartido',
move: 'Mover',
moveCard_title: 'Mover Tarjeta',
moveList_title: null,
remove: 'Remover',
removeAssignee: 'Eliminar asignado',
removeColor: 'Eliminar color',

View File

@@ -222,6 +222,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Liiguta kaart',
moveList_title: null,
myOwn_title: 'Minu omanik',
name: 'Nimi',
newEmail: 'Uus e-post',
@@ -431,6 +432,7 @@ export default {
makeProjectShared_title: 'Muuda projekt jagatavaks',
move: 'Liiguta',
moveCard_title: 'Liiguta kaart',
moveList_title: null,
remove: 'Eemalda',
removeAssignee: 'Eemalda vastutaja',
removeColor: 'Eemalda värv',

View File

@@ -205,6 +205,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'انتقال کارت',
moveList_title: null,
myOwn_title: null,
name: 'نام',
newEmail: 'ایمیل جدید',
@@ -410,6 +411,7 @@ export default {
makeProjectShared_title: null,
move: 'انتقال',
moveCard_title: 'انتقال کارت',
moveList_title: null,
remove: 'حذف',
removeAssignee: null,
removeColor: null,

View File

@@ -218,6 +218,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Siirrä kortti',
moveList_title: null,
myOwn_title: 'Omat',
name: 'Nimi',
newEmail: 'Uusi sähköposti',
@@ -431,6 +432,7 @@ export default {
makeProjectShared_title: 'Jaa projekti',
move: 'Siirrä',
moveCard_title: 'Siirrä kortti',
moveList_title: null,
remove: 'Poista',
removeAssignee: 'Poista vastuuhenkilö',
removeColor: 'Poista väri',

View File

@@ -226,6 +226,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Déplacer la carte',
moveList_title: null,
myOwn_title: 'Mes projets privés',
name: 'Nom',
newEmail: 'Nouvel e-mail',
@@ -435,6 +436,7 @@ export default {
makeProjectShared_title: "Transformer le projet en projet d'équipe",
move: 'Déplacer',
moveCard_title: 'Déplacer la carte',
moveList_title: null,
remove: 'Supprimer',
removeAssignee: 'Retirer le responsable',
removeColor: 'Supprimer la couleur',

View File

@@ -215,6 +215,7 @@ export default {
moreActions: 'További műveletek',
moreActions_title: 'További műveletek',
moveCard_title: 'Kártya áthelyezése',
moveList_title: null,
myOwn_title: 'Saját',
name: 'Név',
newEmail: 'Új e-mail',
@@ -432,6 +433,7 @@ export default {
makeProjectShared_title: 'Projekt megosztása',
move: 'Áthelyezés',
moveCard_title: 'Kártya áthelyezése',
moveList_title: null,
remove: 'Eltávolítás',
removeAssignee: 'Felelős eltávolítása',
removeColor: 'Szín eltávolítása',

View File

@@ -207,6 +207,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Pindahkan Kartu',
moveList_title: null,
myOwn_title: null,
name: 'Nama',
newEmail: 'E-mail baru',
@@ -412,6 +413,7 @@ export default {
makeProjectShared_title: null,
move: 'Pindah',
moveCard_title: 'Pindahkan Kartu',
moveList_title: null,
remove: 'Hapus',
removeAssignee: null,
removeColor: null,

View File

@@ -224,6 +224,7 @@ export default {
moreActions: 'Altre azioni',
moreActions_title: 'Altre azioni',
moveCard_title: 'Sposta scheda',
moveList_title: null,
myOwn_title: 'Personali',
name: 'Nome',
newEmail: 'Nuova e-mail',
@@ -436,6 +437,7 @@ export default {
makeProjectShared_title: 'Rendi progetto condiviso',
move: 'Muovi',
moveCard_title: 'Muovi scheda',
moveList_title: null,
remove: 'Rimuovi',
removeAssignee: 'Rimuovi assegnatario',
removeColor: 'Remove colore',

View File

@@ -207,6 +207,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'カードを移動',
moveList_title: null,
myOwn_title: null,
name: '名前',
newEmail: '新しいEメール',
@@ -412,6 +413,7 @@ export default {
makeProjectShared_title: null,
move: '移動',
moveCard_title: 'カードを移動',
moveList_title: null,
remove: '削除',
removeAssignee: null,
removeColor: null,

View File

@@ -205,6 +205,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: '카드 이동',
moveList_title: null,
myOwn_title: null,
name: '이름',
newEmail: '새 이메일',
@@ -411,6 +412,7 @@ export default {
makeProjectShared_title: null,
move: '이동',
moveCard_title: '카드 이동',
moveList_title: null,
remove: '제거',
removeAssignee: null,
removeColor: '색상 제거',

View File

@@ -207,6 +207,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Kaart verplaatsen',
moveList_title: null,
myOwn_title: null,
name: 'Naam',
newEmail: 'Nieuwe e-mail',
@@ -413,6 +414,7 @@ export default {
makeProjectShared_title: null,
move: 'Verplaatsen',
moveCard_title: 'Kaart verplaatsen',
moveList_title: null,
remove: 'Verwijderen',
removeAssignee: null,
removeColor: null,

View File

@@ -214,6 +214,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Przenoszenie Karty',
moveList_title: null,
myOwn_title: 'Moje',
name: 'Nazwa',
newEmail: 'Nowy e-mail',
@@ -423,6 +424,7 @@ export default {
makeProjectShared_title: 'Udostępnij Projekt',
move: 'Przenieś',
moveCard_title: 'Przenieś Kartę',
moveList_title: null,
remove: 'Usuń',
removeAssignee: 'Usuń osobę przypisaną',
removeColor: 'Usuń kolor',

View File

@@ -224,6 +224,7 @@ export default {
moreActions: 'Mais ações',
moreActions_title: 'Mais Ações',
moveCard_title: 'Mover Cartão',
moveList_title: null,
myOwn_title: 'Meus',
name: 'Nome',
newEmail: 'Novo e-mail',
@@ -435,6 +436,7 @@ export default {
makeProjectShared_title: 'Tornar Projeto Compartilhado',
move: 'Mover',
moveCard_title: 'Mover Cartão',
moveList_title: null,
remove: 'Remover',
removeAssignee: 'Remover responsável',
removeColor: 'Remover cor',

View File

@@ -208,6 +208,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Mover Cartão',
moveList_title: null,
myOwn_title: null,
name: 'Nome',
newEmail: 'Novo e-mail',
@@ -414,6 +415,7 @@ export default {
makeProjectShared_title: null,
move: 'Mover',
moveCard_title: 'Mover Cartão',
moveList_title: null,
remove: 'Remover',
removeAssignee: null,
removeColor: null,

View File

@@ -207,6 +207,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Mutați cardul',
moveList_title: null,
myOwn_title: null,
name: 'Nume',
newEmail: 'Email nou',
@@ -413,6 +414,7 @@ export default {
makeProjectShared_title: null,
move: 'Mutați',
moveCard_title: 'Mutați cardul',
moveList_title: null,
remove: 'Eliminați',
removeAssignee: null,
removeColor: null,

View File

@@ -221,6 +221,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Перемещение карточки',
moveList_title: null,
myOwn_title: 'Мой собственный',
name: 'Имя',
newEmail: 'Новый e-mail',
@@ -427,6 +428,7 @@ export default {
makeProjectShared_title: 'Сделать проект общим',
move: 'Переместить',
moveCard_title: 'Переместить карточку',
moveList_title: null,
remove: 'Убрать',
removeAssignee: 'Удалить исполнителя',
removeColor: 'Удалить цвет',

View File

@@ -207,6 +207,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Presunúť kartu',
moveList_title: null,
myOwn_title: null,
name: 'Meno',
newEmail: 'Nový e-mail',
@@ -412,6 +413,7 @@ export default {
makeProjectShared_title: null,
move: 'Presunúť',
moveCard_title: 'Presunúť kartu',
moveList_title: null,
remove: 'Odstrániť',
removeAssignee: null,
removeColor: null,

View File

@@ -207,6 +207,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Премести картицу',
moveList_title: null,
myOwn_title: null,
name: 'Име',
newEmail: 'Нова е-пошта',
@@ -412,6 +413,7 @@ export default {
makeProjectShared_title: null,
move: 'Премести',
moveCard_title: 'Премести картицу',
moveList_title: null,
remove: 'Уклони',
removeAssignee: null,
removeColor: null,

View File

@@ -204,6 +204,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Premesti karticu',
moveList_title: null,
myOwn_title: null,
name: 'Ime',
newEmail: 'Nova e-pošta',
@@ -409,6 +410,7 @@ export default {
makeProjectShared_title: null,
move: 'Premesti',
moveCard_title: 'Premesti karticu',
moveList_title: null,
remove: 'Ukloni',
removeAssignee: null,
removeColor: null,

View File

@@ -206,6 +206,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Flytta kort',
moveList_title: null,
myOwn_title: null,
name: 'Namn',
newEmail: 'Ny e-mail',
@@ -411,6 +412,7 @@ export default {
makeProjectShared_title: null,
move: 'Flytta',
moveCard_title: 'Flytta kort',
moveList_title: null,
remove: 'Ta bort',
removeAssignee: null,
removeColor: null,

View File

@@ -204,6 +204,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: 'Kartı Taşı',
moveList_title: null,
myOwn_title: null,
name: 'isim',
newEmail: 'Yeni e-posta adresi',
@@ -409,6 +410,7 @@ export default {
makeProjectShared_title: null,
move: 'Taşı',
moveCard_title: 'Kartı Taşı',
moveList_title: null,
remove: 'Sil',
removeAssignee: null,
removeColor: null,

View File

@@ -219,6 +219,7 @@ export default {
moreActions: 'Більше дій',
moreActions_title: 'Більше дій',
moveCard_title: 'Перемістити Картку',
moveList_title: null,
myOwn_title: 'Моя власна',
name: 'Назва',
newEmail: 'Нова електронна пошта',
@@ -429,6 +430,7 @@ export default {
makeProjectShared_title: 'Зробити Проект спільним',
move: 'Перемістити',
moveCard_title: 'Перемістити Картку',
moveList_title: null,
remove: 'Видалити',
removeAssignee: 'Видалити правонаступника',
removeColor: 'Видалити колір',

View File

@@ -203,6 +203,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: "Kartani Ko'chirish",
moveList_title: null,
myOwn_title: null,
name: 'Ism',
newEmail: 'Yangi e-mail',
@@ -408,6 +409,7 @@ export default {
makeProjectShared_title: null,
move: "Ko'chirish",
moveCard_title: "Kartani Ko'chirish",
moveList_title: null,
remove: "O'chirish",
removeAssignee: null,
removeColor: null,

View File

@@ -204,6 +204,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: '移动卡片',
moveList_title: null,
myOwn_title: '我的',
name: '姓名',
newEmail: '新邮箱',
@@ -410,6 +411,7 @@ export default {
makeProjectShared_title: '将项目设为共享',
move: '移动',
moveCard_title: '移动卡片',
moveList_title: null,
remove: '删除',
removeAssignee: '移除负责人',
removeColor: '移除颜色',

View File

@@ -201,6 +201,7 @@ export default {
moreActions: null,
moreActions_title: null,
moveCard_title: '移動卡片',
moveList_title: null,
myOwn_title: null,
name: '姓名',
newEmail: '新郵箱',
@@ -405,6 +406,7 @@ export default {
makeProjectShared_title: null,
move: '移動',
moveCard_title: '移動卡片',
moveList_title: null,
remove: '刪除',
removeAssignee: null,
removeColor: null,

View File

@@ -58,6 +58,7 @@ export default class extends BaseModel {
case ActionTypes.PROJECT_UPDATE_HANDLE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
case ActionTypes.LIST_UPDATE_HANDLE:
case ActionTypes.CARD_UPDATE_HANDLE:
if (payload.attachments) {
payload.attachments.forEach((attachment) => {

View File

@@ -88,6 +88,7 @@ export default class extends BaseModel {
case ActionTypes.PROJECT_UPDATE_HANDLE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
case ActionTypes.LIST_UPDATE_HANDLE:
if (payload.cards) {
payload.cards.forEach((card) => {
Card.upsert(card);

View File

@@ -36,6 +36,7 @@ export default class extends BaseModel {
case ActionTypes.PROJECT_UPDATE_HANDLE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
case ActionTypes.LIST_UPDATE_HANDLE:
case ActionTypes.CARD_UPDATE_HANDLE:
if (payload.customFields) {
payload.customFields.forEach((customField) => {

View File

@@ -40,6 +40,7 @@ export default class extends BaseModel {
case ActionTypes.PROJECT_UPDATE_HANDLE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
case ActionTypes.LIST_UPDATE_HANDLE:
case ActionTypes.CARD_UPDATE_HANDLE:
if (payload.customFieldGroups) {
payload.customFieldGroups.forEach((customFieldGroup) => {

View File

@@ -51,6 +51,7 @@ export default class extends BaseModel {
case ActionTypes.PROJECT_UPDATE_HANDLE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
case ActionTypes.LIST_UPDATE_HANDLE:
case ActionTypes.CARD_UPDATE_HANDLE:
if (payload.customFieldValues) {
payload.customFieldValues.forEach((customFieldValue) => {

View File

@@ -135,47 +135,21 @@ export default class extends BaseModel {
case ActionTypes.LIST_UPDATE: {
const listModel = List.withId(payload.id);
let isClosed;
if (payload.data.type) {
const changedTypeState = getChangedTypeState(listModel, payload.data);
if (changedTypeState === ListTypeStates.OPENED) {
isClosed = false;
} else if (changedTypeState === ListTypeStates.CLOSED) {
isClosed = true;
}
}
listModel.update(payload.data);
if (isClosed !== undefined) {
listModel.cards.toModelArray().forEach((cardModel) => {
cardModel.update({
isClosed,
});
cardModel.linkedTasks.update({
isCompleted: isClosed,
});
});
}
break;
}
case ActionTypes.LIST_UPDATE_HANDLE: {
const listModel = List.withId(payload.list.id);
if (listModel) {
const changedTypeState = getChangedTypeState(listModel, payload.list);
if (payload.data.boardId && payload.data.boardId !== listModel.boardId) {
listModel.deleteWithRelated();
} else {
let isClosed;
if (changedTypeState === ListTypeStates.OPENED) {
isClosed = false;
} else if (changedTypeState === ListTypeStates.CLOSED) {
isClosed = true;
if (payload.data.type) {
const changedTypeState = getChangedTypeState(listModel, payload.data);
if (changedTypeState === ListTypeStates.OPENED) {
isClosed = false;
} else if (changedTypeState === ListTypeStates.CLOSED) {
isClosed = true;
}
}
listModel.update(prepareList(payload.list));
listModel.update(payload.data);
if (isClosed !== undefined) {
listModel.cards.toModelArray().forEach((cardModel) => {
@@ -188,7 +162,44 @@ export default class extends BaseModel {
});
});
}
} else {
}
break;
}
// TODO: refactor
case ActionTypes.LIST_UPDATE_HANDLE: {
const listModel = List.withId(payload.list.id);
if (listModel) {
if (payload.list.boardId === null || payload.isFetched) {
listModel.deleteWithRelated();
}
if (payload.list.boardId !== null) {
const changedTypeState = getChangedTypeState(listModel, payload.list);
let isClosed;
if (changedTypeState === ListTypeStates.OPENED) {
isClosed = false;
} else if (changedTypeState === ListTypeStates.CLOSED) {
isClosed = true;
}
listModel.update(prepareList(payload.list));
if (isClosed !== undefined) {
listModel.cards.toModelArray().forEach((cardModel) => {
cardModel.update({
isClosed,
});
cardModel.linkedTasks.update({
isCompleted: isClosed,
});
});
}
}
} else if (payload.list.boardId !== null) {
List.upsert(prepareList(payload.list));
}

View File

@@ -43,6 +43,7 @@ export default class extends BaseModel {
case ActionTypes.PROJECT_UPDATE_HANDLE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
case ActionTypes.LIST_UPDATE_HANDLE:
case ActionTypes.CARD_UPDATE_HANDLE:
if (payload.tasks) {
payload.tasks.forEach((task) => {

View File

@@ -32,6 +32,7 @@ export default class extends BaseModel {
case ActionTypes.PROJECT_UPDATE_HANDLE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
case ActionTypes.LIST_UPDATE_HANDLE:
case ActionTypes.CARD_UPDATE_HANDLE:
if (payload.taskLists) {
payload.taskLists.forEach((taskList) => {

View File

@@ -93,6 +93,7 @@ export default class extends BaseModel {
case ActionTypes.LOCATION_CHANGE_HANDLE:
case ActionTypes.PROJECT_UPDATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
case ActionTypes.LIST_UPDATE_HANDLE:
case ActionTypes.CARD_UPDATE_HANDLE:
if (payload.users) {
payload.users.forEach((user) => {

View File

@@ -6,6 +6,7 @@
import { call, put, select } from 'redux-saga/effects';
import toast from 'react-hot-toast';
import { goToBoard } from './router';
import request from '../request';
import selectors from '../../../selectors';
import actions from '../../../actions';
@@ -65,7 +66,71 @@ export function* updateList(id, data) {
}
export function* handleListUpdate(list) {
yield put(actions.handleListUpdate(list));
const currentCard = yield select(selectors.selectCurrentCard);
let fetch = false;
if (list.boardId) {
const isAvailableForCurrentUser = yield select(
selectors.selectIsListWithIdAvailableForCurrentUser,
list.id,
);
fetch = !isAvailableForCurrentUser;
}
let users;
let cards;
let cardMemberships;
let cardLabels;
let taskLists;
let tasks;
let attachments;
let customFieldGroups;
let customFields;
let customFieldValues;
if (fetch) {
try {
({
item: list, // eslint-disable-line no-param-reassign
included: {
users,
cards,
cardMemberships,
cardLabels,
taskLists,
tasks,
attachments,
customFieldGroups,
customFields,
customFieldValues,
},
} = yield call(request, api.getList, list.id));
} catch {
fetch = false;
}
}
yield put(
actions.handleListUpdate(
list,
fetch,
users,
cards,
cardMemberships,
cardLabels,
taskLists,
tasks,
attachments,
customFieldGroups,
customFields,
customFieldValues,
),
);
if (list.boardId === null && currentCard && list.id === currentCard.listId) {
yield call(goToBoard, currentCard.boardId);
}
}
export function* moveList(id, index) {
@@ -77,6 +142,19 @@ export function* moveList(id, index) {
});
}
export function* transferList(id, boardId) {
const currentCard = yield select(selectors.selectCurrentCard);
// TODO: hack?
if (currentCard && id === currentCard.listId) {
yield call(goToBoard, currentCard.boardId);
}
yield call(updateList, id, {
boardId,
});
}
export function* sortList(id, data) {
yield put(actions.sortList(id, data));
@@ -190,6 +268,7 @@ export default {
updateList,
handleListUpdate,
moveList,
transferList,
sortList,
moveListCardsToArchiveList,
clearTrashListInCurrentBoard,

View File

@@ -28,6 +28,9 @@ export default function* listsWatchers() {
takeEvery(EntryActionTypes.LIST_SORT, ({ payload: { id, data } }) =>
services.sortList(id, data),
),
takeEvery(EntryActionTypes.LIST_TRANSFER, ({ payload: { id, boardId, index } }) =>
services.transferList(id, boardId, index),
),
takeEvery(EntryActionTypes.LIST_CARDS_TO_ARCHIVE_LIST_MOVE, ({ payload: { id } }) =>
services.moveListCardsToArchiveList(id),
),

View File

@@ -7,6 +7,7 @@ import { createSelector } from 'redux-orm';
import orm from '../orm';
import { selectPath } from './router';
import { selectCurrentUserId } from './users';
import { isLocalId } from '../utils/local-id';
import { BoardContexts, ListTypes } from '../constants/Enums';
@@ -64,6 +65,22 @@ export const makeSelectFilteredCardIdsByListId = () =>
export const selectFilteredCardIdsByListId = makeSelectFilteredCardIdsByListId();
export const selectIsListWithIdAvailableForCurrentUser = createSelector(
orm,
(_, id) => id,
(state) => selectCurrentUserId(state),
({ List, User }, id, currentUserId) => {
const listModel = List.withId(id);
if (!listModel) {
return false;
}
const currentUserModel = User.withId(currentUserId);
return listModel.isAvailableForUser(currentUserModel);
},
);
export const selectCurrentListId = createSelector(
orm,
(state) => selectPath(state).boardId,
@@ -154,6 +171,7 @@ export default {
selectCardIdsByListId,
makeSelectFilteredCardIdsByListId,
selectFilteredCardIdsByListId,
selectIsListWithIdAvailableForCurrentUser,
selectCurrentListId,
selectCurrentList,
selectFirstFiniteListId,

View File

@@ -197,6 +197,35 @@ export const selectFavoriteProjectIdsForCurrentUser = createSelector(
},
);
export const selectProjectsToBoardsWithEditorRightsForCurrentUser = createSelector(
orm,
(state) => selectCurrentUserId(state),
({ User }, id) => {
if (!id) {
return id;
}
const userModel = User.withId(id);
if (!userModel) {
return userModel;
}
return userModel.getMembershipProjectsModelArray().map((projectModel) => ({
...projectModel.ref,
boards: projectModel.getBoardsModelArrayForUserWithId(id).flatMap((boardModel) => {
const boardMembersipModel = boardModel.getMembershipModelByUserId(id);
if (boardMembersipModel.role !== BoardMembershipRoles.EDITOR) {
return [];
}
return boardModel.ref;
}),
}));
},
);
export const selectProjectsToListsWithEditorRightsForCurrentUser = createSelector(
orm,
(state) => selectCurrentUserId(state),
@@ -334,6 +363,7 @@ export default {
selectFilteredProjectIdsForCurrentUser,
selectFilteredProjctIdsByGroupForCurrentUser,
selectFavoriteProjectIdsForCurrentUser,
selectProjectsToBoardsWithEditorRightsForCurrentUser,
selectProjectsToListsWithEditorRightsForCurrentUser,
selectBoardIdsForCurrentUser,
selectNotificationIdsForCurrentUser,