pull/705/merge
Jens Frost 2 years ago committed by GitHub
commit aae1a21c2c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -121,6 +121,33 @@ const handleCardDelete = (card) => ({
},
});
const copyCard = (id) => ({
type: ActionTypes.CARD_COPY,
payload: {
id,
},
});
copyCard.success = (card) => ({
type: ActionTypes.CARD_COPY__SUCCESS,
payload: {
card,
},
});
copyCard.failure = (id, error) => ({
type: ActionTypes.CARD_COPY__FAILURE,
payload: {
id,
error,
},
});
const handleCardCopy = (card) => ({
type: ActionTypes.CARD_COPY_HANDLE,
payload: {
card,
const filterText = (boardId, text) => ({
type: ActionTypes.TEXT_FILTER_IN_CURRENT_BOARD,
payload: {
@ -137,5 +164,7 @@ export default {
duplicateCard,
deleteCard,
handleCardDelete,
copyCard,
handleCardCopy,
filterText,
};

@ -121,6 +121,35 @@ const handleListDelete = (list) => ({
},
});
const sortList = (id) => ({
type: ActionTypes.LIST_SORT,
payload: {
id,
},
});
sortList.success = (list) => ({
type: ActionTypes.LIST_SORT__SUCCESS,
payload: {
list,
},
});
sortList.failure = (id, error) => ({
type: ActionTypes.LIST_SORT__FAILURE,
payload: {
id,
error,
},
});
const handleListSort = (list) => ({
type: ActionTypes.LIST_SORT_HANDLE,
payload: {
list,
},
});
export default {
createList,
handleListCreate,
@ -130,4 +159,6 @@ export default {
handleListSort,
deleteList,
handleListDelete,
sortList,
handleListSort,
};

@ -41,6 +41,12 @@ const createCard = (listId, data, headers) =>
item: transformCard(body.item),
}));
const copyCard = (listId, data, headers) =>
socket.post(`/lists/${listId}/cards`, transformCardData(data), headers).then((body) => ({
...body,
item: transformCard(body.item),
}));
const getCard = (id, headers) =>
socket.get(`/cards/${id}`, undefined, headers).then((body) => ({
...body,
@ -91,4 +97,5 @@ export default {
makeHandleCardCreate,
makeHandleCardUpdate,
makeHandleCardDelete,
copyCard,
};

@ -12,6 +12,7 @@ import DueDateEditStep from '../DueDateEditStep';
import StopwatchEditStep from '../StopwatchEditStep';
import CardMoveStep from '../CardMoveStep';
import DeleteStep from '../DeleteStep';
import CardCopyStep from '../CardCopyStep';
import styles from './ActionsStep.module.scss';
@ -22,6 +23,7 @@ const StepTypes = {
EDIT_STOPWATCH: 'EDIT_STOPWATCH',
MOVE: 'MOVE',
DELETE: 'DELETE',
COPY: 'COPY',
};
const ActionsStep = React.memo(
@ -48,9 +50,35 @@ const ActionsStep = React.memo(
onLabelMove,
onLabelDelete,
onClose,
onCopyCard,
}) => {
const [t] = useTranslation();
const [step, openStep, handleBack] = useSteps();
// prepare defaultPath data for copying card
const defaultPath = {};
defaultPath.id = card.id;
defaultPath.boardId = card.boardId;
defaultPath.projectId = card.projectId;
defaultPath.listId = card.listId;
defaultPath.cardId = card.id;
defaultPath.name = card.name;
if (card.dueDate !== null) {
defaultPath.dueDate = card.dueDate;
}
if (card.dueDate === null || card.dueDate === undefined) {
// eslint-disable-next-line no-param-reassign
if (defaultPath.dueDate) {
delete defaultPath.dueDate;
}
}
defaultPath.stopwatch = card.stopwatch;
defaultPath.labels = card.labels;
defaultPath.boardMemberships = boardMemberships;
defaultPath.currentLabelIds = currentLabelIds;
defaultPath.currentUserIds = currentUserIds;
defaultPath.tasks = card.tasks;
defaultPath.users = card.users;
defaultPath.description = card.description;
const handleEditNameClick = useCallback(() => {
onNameEdit();
@ -77,11 +105,16 @@ const ActionsStep = React.memo(
openStep(StepTypes.MOVE);
}, [openStep]);
const handleCopyClick = useCallback(() => {
openStep(StepTypes.COPY);
}, [openStep]);
const handleDuplicateClick = useCallback(() => {
onDuplicate();
onClose();
}, [onDuplicate, onClose]);
const handleDeleteClick = useCallback(() => {
openStep(StepTypes.DELETE);
}, [openStep]);
@ -170,6 +203,18 @@ const ActionsStep = React.memo(
onBack={handleBack}
/>
);
case StepTypes.COPY:
return (
<CardCopyStep
projectsToLists={projectsToLists}
defaultPath={defaultPath}
onCopyCard={onCopyCard}
onTransfer={onTransfer}
onBoardFetch={onBoardFetch}
onBack={handleBack}
onClose={onClose}
/>
);
default:
}
}
@ -223,6 +268,11 @@ const ActionsStep = React.memo(
context: 'title',
})}
</Menu.Item>
<Menu.Item className={styles.menuItem} onClick={handleCopyClick}>
{t('action.copyCard', {
context: 'title',
})}
</Menu.Item>
</Menu>
</Popup.Content>
</>
@ -255,6 +305,7 @@ ActionsStep.propTypes = {
onLabelMove: PropTypes.func.isRequired,
onLabelDelete: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
onCopyCard: PropTypes.func.isRequired,
};
export default ActionsStep;

@ -21,6 +21,7 @@ import styles from './Card.module.scss';
const Card = React.memo(
({
id,
description,
index,
name,
dueDate,
@ -52,6 +53,7 @@ const Card = React.memo(
onLabelUpdate,
onLabelMove,
onLabelDelete,
onCopyCard,
}) => {
const nameEdit = useRef(null);
@ -171,6 +173,13 @@ const Card = React.memo(
{canEdit && (
<ActionsPopup
card={{
id,
description,
index,
name,
tasks,
labels,
users,
dueDate,
stopwatch,
boardId,
@ -197,6 +206,7 @@ const Card = React.memo(
onLabelUpdate={onLabelUpdate}
onLabelMove={onLabelMove}
onLabelDelete={onLabelDelete}
onCopyCard={onCopyCard}
>
<Button className={classNames(styles.actionsButton, styles.target)}>
<Icon fitted name="pencil" size="small" />
@ -218,6 +228,7 @@ const Card = React.memo(
Card.propTypes = {
id: PropTypes.string.isRequired,
description: PropTypes.string,
index: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
dueDate: PropTypes.instanceOf(Date),
@ -251,12 +262,16 @@ Card.propTypes = {
onLabelUpdate: PropTypes.func.isRequired,
onLabelMove: PropTypes.func.isRequired,
onLabelDelete: PropTypes.func.isRequired,
// onSortTitleAsc: PropTypes.func.isRequired,
// onSortTitleDesc: PropTypes.func.isRequired,
onCopyCard: PropTypes.func.isRequired,
};
Card.defaultProps = {
dueDate: undefined,
stopwatch: undefined,
coverUrl: undefined,
description: undefined,
};
export default Card;

@ -0,0 +1,5 @@
import { withPopup } from '../lib/popup';
import CardCopyStep from './CardCopyStep';
export default withPopup(CardCopyStep);

@ -0,0 +1,184 @@
import React, { useMemo, useCallback } from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { Button, Dropdown, Form } from 'semantic-ui-react';
import { Popup } from '../../lib/custom-ui';
import { useForm } from '../../hooks';
import styles from './CardCopyStep.module.scss';
import store from '../../store';
const CardCopyStep = React.memo(
({ projectsToLists, defaultPath, onBoardFetch, onBack, onClose, onCopyCard }) => {
const [t] = useTranslation();
// Get store to get value for description string
const st = store.getState();
const keys = Object.keys(st.orm.Card.itemsById);
if (defaultPath.description === undefined) {
keys.forEach((key) => {
if (key === defaultPath.id) {
// eslint-disable-next-line no-param-reassign
defaultPath.description = st.orm.Card.itemsById[key].description;
}
});
}
if (defaultPath.dueDate === null || defaultPath.dueDate === undefined) {
// eslint-disable-next-line no-param-reassign
delete defaultPath.dueDate;
}
const [path, handleFieldChange] = useForm(() => ({
projectId: null,
boardId: null,
listId: null,
name: defaultPath.name,
description: defaultPath.description,
tasks: defaultPath.tasks,
attachments: defaultPath.attachments,
labels: defaultPath.labels,
users: defaultPath.users,
...defaultPath,
}));
const selectedProject = useMemo(
() => projectsToLists.find((project) => project.id === path.projectId) || null,
[projectsToLists, path.projectId],
);
const selectedBoard = useMemo(
() =>
(selectedProject && selectedProject.boards.find((board) => board.id === path.boardId)) ||
null,
[selectedProject, path.boardId],
);
const selectedList = useMemo(
() => (selectedBoard && selectedBoard.lists.find((list) => list.id === path.listId)) || null,
[selectedBoard, path.listId],
);
const handleBoardIdChange = useCallback(
(event, data) => {
if (selectedProject.boards.find((board) => board.id === data.value).isFetching === null) {
onBoardFetch(data.value);
}
handleFieldChange(event, data);
},
[onBoardFetch, handleFieldChange, selectedProject],
);
const handleSubmit = useCallback(() => {
onCopyCard(selectedList.id, path);
onClose();
}, [onCopyCard, selectedList.id, path, onClose]);
return (
<>
<Popup.Header onBack={onBack}>
{t('action.copyCard', {
context: 'title',
})}
</Popup.Header>
<Popup.Content>
<Form onSubmit={handleSubmit}>
<div className={styles.text}>{t('common.project')}</div>
<Dropdown
fluid
selection
name="projectId"
options={projectsToLists.map((project) => ({
text: project.name,
value: project.id,
}))}
value={selectedProject && selectedProject.id}
placeholder={
projectsToLists.length === 0 ? t('common.noProjects') : t('common.selectProject')
}
disabled={projectsToLists.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={handleBoardIdChange}
/>
</>
)}
{selectedBoard && (
<>
<div className={styles.text}>{t('common.list')}</div>
<Dropdown
fluid
selection
name="listId"
options={selectedBoard.lists.map((list) => ({
text: list.name,
value: list.id,
}))}
value={selectedList && selectedList.id}
placeholder={
selectedBoard.isFetching === false && selectedBoard.lists.length === 0
? t('common.noLists')
: t('common.selectList')
}
loading={selectedBoard.isFetching !== false}
disabled={selectedBoard.isFetching !== false || selectedBoard.lists.length === 0}
className={styles.field}
onChange={handleFieldChange}
/>
</>
)}
<Button
positive
content={t('action.copy')} // change this action.copy
disabled={(selectedBoard && selectedBoard.isFetching !== false) || !selectedList}
/>
</Form>
</Popup.Content>
</>
);
},
);
CardCopyStep.propTypes = {
// eslint-disable-next-line react/forbid-prop-types
description: PropTypes.string,
/* eslint-disable react/forbid-prop-types */
projectsToLists: PropTypes.array.isRequired,
defaultPath: PropTypes.object.isRequired,
/* eslint-enable react/forbid-prop-types */
// onMove: PropTypes.func.isRequired,
// onTransfer: PropTypes.func.isRequired,
onBoardFetch: PropTypes.func.isRequired,
onBack: PropTypes.func,
onClose: PropTypes.func.isRequired,
onCopyCard: PropTypes.func.isRequired,
};
CardCopyStep.defaultProps = {
onBack: undefined,
description: undefined,
};
export default CardCopyStep;

@ -0,0 +1,12 @@
:global(#app) {
.field {
margin-bottom: 8px;
}
.text {
color: #444444;
font-size: 12px;
font-weight: bold;
padding-bottom: 6px;
}
}

@ -0,0 +1,3 @@
import CardCopyStep from './CardCopyStep';
export default CardCopyStep;

@ -25,10 +25,13 @@ import StopwatchEditStep from '../StopwatchEditStep';
import CardMoveStep from '../CardMoveStep';
import DeleteStep from '../DeleteStep';
import CardCopyPopup from '../CardCopyPopup';
import styles from './CardModal.module.scss';
const CardModal = React.memo(
({
id,
name,
description,
dueDate,
@ -79,9 +82,63 @@ const CardModal = React.memo(
onCommentActivityUpdate,
onCommentActivityDelete,
onClose,
onCopyCard,
}) => {
const [t] = useTranslation();
const card = {};
card.id = id;
card.name = name;
card.description = description;
card.dueDate = dueDate;
card.stopwatch = stopwatch;
card.isSubscribed = isSubscribed;
card.isActivitiesFetching = isActivitiesFetching;
card.isAllActivitiesFetched = isAllActivitiesFetched;
card.isActivitiesDetailsVisible = isActivitiesDetailsVisible;
card.isActivitiesDetailsFetching = isActivitiesDetailsFetching;
card.listId = listId;
card.boardId = boardId;
card.projectId = projectId;
card.users = users;
card.labels = labels;
card.tasks = tasks;
card.attachments = attachments;
card.activities = activities;
card.allProjectsToLists = allProjectsToLists;
card.allBoardMemberships = allBoardMemberships;
card.allLabels = allLabels;
card.canEdit = canEdit;
card.canEditCommentActivities = canEditCommentActivities;
card.canEditAllCommentActivities = canEditAllCommentActivities;
card.onUpdate = onUpdate;
card.onMove = onMove;
card.onTransfer = onTransfer;
card.onDelete = onDelete;
card.onUserAdd = onUserAdd;
card.onUserRemove = onUserRemove;
card.onBoardFetch = onBoardFetch;
card.onLabelAdd = onLabelAdd;
card.onLabelRemove = onLabelRemove;
card.onLabelCreate = onLabelCreate;
card.onLabelUpdate = onLabelUpdate;
card.onLabelMove = onLabelMove;
card.onLabelDelete = onLabelDelete;
card.onTaskCreate = onTaskCreate;
card.onTaskUpdate = onTaskUpdate;
card.onTaskMove = onTaskMove;
card.onTaskDelete = onTaskDelete;
card.onAttachmentCreate = onAttachmentCreate;
card.onAttachmentUpdate = onAttachmentUpdate;
card.onAttachmentDelete = onAttachmentDelete;
card.onActivitiesFetch = onActivitiesFetch;
card.onActivitiesDetailsToggle = onActivitiesDetailsToggle;
card.onCommentActivityCreate = onCommentActivityCreate;
card.onCommentActivityUpdate = onCommentActivityUpdate;
card.onCommentActivityDelete = onCommentActivityDelete;
card.onClose = onClose;
card.onCopyCard = onCopyCard;
const isGalleryOpened = useRef(false);
const handleToggleStopwatchClick = useCallback(() => {
@ -502,10 +559,30 @@ const CardModal = React.memo(
{t('action.move')}
</Button>
</CardMovePopup>
<CardCopyPopup
projectsToLists={allProjectsToLists}
defaultPath={card}
onMove={onMove}
onTransfer={onTransfer}
onBoardFetch={onBoardFetch}
onCopyCard={onCopyCard}
>
<Button
fluid
className={styles.actionButton}
onClick={handleToggleSubscriptionClick}
>
<Icon name="copy outline" className={styles.actionIcon} />
{t('action.copy')}
</Button>
</CardCopyPopup>
<Button fluid className={styles.actionButton} onClick={handleDuplicateClick}>
<Icon name="copy outline" className={styles.actionIcon} />
{t('action.duplicate')}
</Button>
<DeletePopup
title="common.deleteCard"
content="common.areYouSureYouWantToDeleteThisCard"
@ -537,6 +614,7 @@ const CardModal = React.memo(
);
CardModal.propTypes = {
id: PropTypes.string,
name: PropTypes.string.isRequired,
description: PropTypes.string,
dueDate: PropTypes.instanceOf(Date),
@ -589,12 +667,14 @@ CardModal.propTypes = {
onCommentActivityUpdate: PropTypes.func.isRequired,
onCommentActivityDelete: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
onCopyCard: PropTypes.func.isRequired,
};
CardModal.defaultProps = {
description: undefined,
dueDate: undefined,
stopwatch: undefined,
id: undefined,
};
export default CardModal;

@ -11,7 +11,6 @@ import styles from './CardMoveStep.module.scss';
const CardMoveStep = React.memo(
({ projectsToLists, defaultPath, onMove, onTransfer, onBoardFetch, onBack, onClose }) => {
const [t] = useTranslation();
const [path, handleFieldChange] = useForm(() => ({
projectId: null,
boardId: null,

@ -7,6 +7,7 @@ import { Popup } from '../../lib/custom-ui';
import { useSteps } from '../../hooks';
import ListSortStep from '../ListSortStep';
import DeleteStep from '../DeleteStep';
import SortStep from '../SortStep';
import styles from './ActionsStep.module.scss';
@ -15,19 +16,90 @@ const StepTypes = {
SORT: 'SORT',
};
const ActionsStep = React.memo(({ onNameEdit, onCardAdd, onSort, onDelete, onClose }) => {
const [t] = useTranslation();
const [step, openStep, handleBack] = useSteps();
const ActionsStep = React.memo(
({ onNameEdit, onCardAdd, onDelete, onClose, onSort, selectedOption, setSelectedOption }) => {
const [t] = useTranslation();
const [step, openStep, handleBack] = useSteps();
const handleEditNameClick = useCallback(() => {
onNameEdit();
onClose();
}, [onNameEdit, onClose]);
const handleEditNameClick = useCallback(() => {
onNameEdit();
onClose();
}, [onNameEdit, onClose]);
const handleAddCardClick = useCallback(() => {
onCardAdd();
onClose();
}, [onCardAdd, onClose]);
const handleDeleteClick = useCallback(() => {
openStep(StepTypes.DELETE);
}, [openStep]);
const handleSortClick = useCallback(() => {
openStep(StepTypes.SORT);
}, [openStep]);
const handleAddCardClick = useCallback(() => {
onCardAdd();
onClose();
}, [onCardAdd, onClose]);
if (step && step.type === StepTypes.DELETE) {
return (
<DeleteStep
title="common.deleteList"
content="common.areYouSureYouWantToDeleteThisList"
buttonContent="action.deleteList"
onConfirm={onDelete}
onBack={handleBack}
/>
);
}
if (step && step.type === StepTypes.SORT) {
return (
<SortStep
title="common.sortList"
content="common.areYouSureYouWantToSortThisList"
buttonContent="action.sortList"
onConfirm={onSort}
onBack={handleBack}
selectedOption={selectedOption}
setSelectedOption={setSelectedOption}
/>
);
}
return (
<>
<Popup.Header>
{t('common.listActions', {
context: 'title',
})}
</Popup.Header>
<Popup.Content>
<Menu secondary vertical className={styles.menu}>
<Menu.Item className={styles.menuItem} onClick={handleEditNameClick}>
{t('action.editTitle', {
context: 'title',
})}
</Menu.Item>
<Menu.Item className={styles.menuItem} onClick={handleAddCardClick}>
{t('action.addCard', {
context: 'title',
})}
</Menu.Item>
<Menu.Item className={styles.menuItem} onClick={handleDeleteClick}>
{t('action.deleteList', {
context: 'title',
})}
</Menu.Item>
<Menu.Item className={styles.menuItem} onClick={handleSortClick}>
{t('action.sortList', {
context: 'title',
})}
</Menu.Item>
</Menu>
</Popup.Content>
</>
);
},
);
const handleSortClick = useCallback(() => {
openStep(StepTypes.SORT);
@ -107,6 +179,9 @@ ActionsStep.propTypes = {
onDelete: PropTypes.func.isRequired,
onSort: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
onSort: PropTypes.func.isRequired,
selectedOption: PropTypes.string.isRequired,
setSelectedOption: PropTypes.func.isRequired,
};
export default ActionsStep;

@ -4,7 +4,7 @@ import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
import { Draggable, Droppable } from 'react-beautiful-dnd';
import { Button, Icon } from 'semantic-ui-react';
import { usePopup } from '../../lib/popup';
import { usePopup, closePopup } from '../../lib/popup';
import DroppableTypes from '../../constants/DroppableTypes';
import CardContainer from '../../containers/CardContainer';
@ -30,6 +30,7 @@ const List = React.memo(
}) => {
const [t] = useTranslation();
const [isAddCardOpened, setIsAddCardOpened] = useState(false);
const [selectedOption, setSelectedOption] = useState('name');
const nameEdit = useRef(null);
const listWrapper = useRef(null);
@ -51,11 +52,11 @@ const List = React.memo(
const handleAddCardClick = useCallback(() => {
setIsAddCardOpened(true);
}, []);
}, [setIsAddCardOpened]);
const handleAddCardClose = useCallback(() => {
setIsAddCardOpened(false);
}, []);
}, [setIsAddCardOpened]);
const handleNameEdit = useCallback(() => {
nameEdit.current.open();
@ -63,7 +64,12 @@ const List = React.memo(
const handleCardAdd = useCallback(() => {
setIsAddCardOpened(true);
}, []);
}, [setIsAddCardOpened]);
const onSort = useCallback(() => {
onUpdate({ selectedOption: document.querySelector('input[name="sort"]:checked').value });
closePopup();
}, [onUpdate]);
useEffect(() => {
if (isAddCardOpened) {
@ -126,6 +132,9 @@ const List = React.memo(
onCardAdd={handleCardAdd}
onDelete={onDelete}
onSort={onSort}
selectedOption={selectedOption}
setSelectedOption={setSelectedOption}
>
<Button className={classNames(styles.headerButton, styles.target)}>
<Icon fitted name="pencil" size="small" />

@ -0,0 +1,164 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { Button } from 'semantic-ui-react';
import { Popup } from '../../lib/custom-ui';
import styles from './SortStep.module.scss';
const SortStep = React.memo(
({ title, content, buttonContent, onBack, onConfirm, selectedOption, setSelectedOption }) => {
const [t] = useTranslation();
const [checked, setChecked] = useState(false);
function handleChange(event) {
setSelectedOption(event.target.id);
}
const updateState = () => {
if (checked) {
setChecked(false);
} else {
setChecked(true);
}
};
function changeEvent(event) {
handleChange(event);
updateState();
}
return (
<>
<Popup.Header onBack={onBack}>
{t(title, {
context: 'title',
})}
</Popup.Header>
<Popup.Content>
<div className={styles.content}>{t(content)}</div>
<div>
<label htmlFor="name">
<input
type="radio"
id="name"
name="sort"
value="name"
onChange={changeEvent}
checked={selectedOption === 'name'}
/>
{t('action.sortByTitleList', {
context: 'title',
})}
</label>
<br />
<label htmlFor="id">
<input
type="radio"
id="id"
name="sort"
value="id"
onChange={changeEvent}
checked={selectedOption === 'id'}
/>
{t('action.sortByIdList', {
context: 'title',
})}
</label>
<br />
<label htmlFor="position">
<input
type="radio"
id="position"
name="sort"
value="position"
onChange={changeEvent}
checked={selectedOption === 'position'}
/>
{t('action.sortByPositionList', {
context: 'title',
})}
</label>
<br />
<label htmlFor="createdAt">
<input
type="radio"
id="createdAt"
name="sort"
value="createdAt"
onChange={changeEvent}
checked={selectedOption === 'createdAt'}
/>
{t('action.sortByCreatedAt', {
context: 'title',
})}
</label>
<br />
<label htmlFor="updatedAt">
<input
type="radio"
id="updatedAt"
name="sort"
value="updatedAt"
onChange={changeEvent}
checked={selectedOption === 'updatedAt'}
/>
{t('action.sortByUpdatedAtList', {
context: 'title',
})}
</label>
<br />
<label htmlFor="dueDate">
<input
type="radio"
id="dueDate"
name="sort"
value="dueDate"
onChange={changeEvent}
checked={selectedOption === 'dueDate'}
/>
{t('action.sortByDueDateList', {
context: 'title',
})}
</label>
<br />
<label htmlFor="creatorUserId">
<input
type="radio"
id="creatorUserId"
name="sort"
value="creatorUserId"
onChange={changeEvent}
checked={selectedOption === 'creatorUserId'}
/>
{t('action.sortByCreatorUserIdList', {
context: 'title',
})}
</label>
<br />
</div>
<Button fluid negative content={t(buttonContent)} onClick={onConfirm} />
</Popup.Content>
</>
);
},
);
SortStep.propTypes = {
title: PropTypes.string.isRequired,
content: PropTypes.string.isRequired,
buttonContent: PropTypes.string.isRequired,
onConfirm: PropTypes.func.isRequired,
onBack: PropTypes.func,
selectedOption: PropTypes.string.isRequired,
setSelectedOption: PropTypes.func.isRequired,
};
SortStep.defaultProps = {
onBack: undefined,
};
export default SortStep;

@ -0,0 +1,7 @@
:global(#app) {
.content {
color: #212121;
padding-bottom: 6px;
padding-left: 2px;
}
}

@ -0,0 +1,3 @@
import SortStep from './SortStep';
export default SortStep;

@ -181,6 +181,10 @@ export default {
LIST_DELETE__SUCCESS: 'LIST_DELETE__SUCCESS',
LIST_DELETE__FAILURE: 'LIST_DELETE__FAILURE',
LIST_DELETE_HANDLE: 'LIST_DELETE_HANDLE',
LIST_SORT: 'LIST_SORT',
LIST_SORT__SUCCESS: 'LIST_SORT__SUCCESS',
LIST_SORT__FAILURE: 'LIST_SORT__FAILURE',
LIST_SORT_HANDLE: 'LIST_SORT_HANDLE',
/* Cards */
@ -205,8 +209,15 @@ export default {
CARD_DELETE__SUCCESS: 'CARD_DELETE__SUCCESS',
CARD_DELETE__FAILURE: 'CARD_DELETE__FAILURE',
CARD_DELETE_HANDLE: 'CARD_DELETE_HANDLE',
CARD_COPY_HANDLE: 'CARD_COPY_HANDLE',
CARD_COPY: 'CARD_COPY',
CARD_COPY__SUCCESS: 'CARD_COPY__SUCCESS',
CARD_COPY__FAILURE: 'CARD_COPY__FAILURE',
TEXT_FILTER_IN_CURRENT_BOARD: 'TEXT_FILTER_IN_CURRENT_BOARD',
/* Tasks */
TASK_CREATE: 'TASK_CREATE',

@ -139,6 +139,8 @@ export default {
CARD_DELETE: `${PREFIX}/CARD_DELETE`,
CURRENT_CARD_DELETE: `${PREFIX}/CURRENT_CARD_DELETE`,
CARD_DELETE_HANDLE: `${PREFIX}/CARD_DELETE_HANDLE`,
CARD_COPY: `${PREFIX}/CARD_COPY`,
CARD_COPY_HANDLE: `${PREFIX}/CARD_COPY_HANDLE`,
TEXT_FILTER_IN_CURRENT_BOARD: `${PREFIX}/FILTER_TEXT_HANDLE`,
/* Tasks */

@ -19,4 +19,5 @@ export const ActivityTypes = {
CREATE_CARD: 'createCard',
MOVE_CARD: 'moveCard',
COMMENT_CARD: 'commentCard',
COPY_CARD: 'copyCard',
};

@ -27,6 +27,7 @@ const mapDispatchToProps = (dispatch) =>
onListCreate: entryActions.createListInCurrentBoard,
onListMove: entryActions.moveList,
onCardMove: entryActions.moveCard,
onCardCopy: entryActions.copyCard,
},
dispatch,
);

@ -24,6 +24,7 @@ const mapDispatchToProps = (dispatch) =>
onUpdate: entryActions.updateBoard,
onMove: entryActions.moveBoard,
onDelete: entryActions.deleteBoard,
onSort: entryActions.sortBoard,
},
dispatch,
);

@ -73,6 +73,7 @@ const mapDispatchToProps = (dispatch, { id }) =>
onLabelUpdate: (labelId, data) => entryActions.updateLabel(labelId, data),
onLabelMove: (labelId, index) => entryActions.moveLabel(labelId, index),
onLabelDelete: (labelId) => entryActions.deleteLabel(labelId),
onCopyCard: (listId, data) => entryActions.copyCard(listId, data, false),
},
dispatch,
);

@ -18,6 +18,7 @@ const mapStateToProps = (state) => {
const currentUserMembership = selectors.selectCurrentUserMembershipForCurrentBoard(state);
const {
id,
name,
description,
dueDate,
@ -46,6 +47,7 @@ const mapStateToProps = (state) => {
}
return {
id,
name,
description,
dueDate,
@ -101,6 +103,7 @@ const mapDispatchToProps = (dispatch) =>
onCommentActivityCreate: entryActions.createCommentActivityInCurrentCard,
onCommentActivityUpdate: entryActions.updateCommentActivity,
onCommentActivityDelete: entryActions.deleteCommentActivity,
onCopyCard: entryActions.copyCard,
push,
},
dispatch,

@ -36,6 +36,7 @@ const mapDispatchToProps = (dispatch, { id }) =>
onSort: (data) => entryActions.sortList(id, data),
onDelete: () => entryActions.deleteList(id),
onCardCreate: (data, autoOpen) => entryActions.createCard(id, data, autoOpen),
onSort: (data) => entryActions.sortList(id, data),
},
dispatch,
);

@ -47,7 +47,16 @@ const moveCard = (id, listId, index) => ({
},
});
const moveCurrentCard = (listId, index) => ({
const copyCard = (listId, data, autoOpen) => ({
type: EntryActionTypes.CARD_COPY,
payload: {
listId,
data,
autoOpen,
},
});
const moveCurrentCard = (listId, index = 0) => ({
type: EntryActionTypes.CURRENT_CARD_MOVE,
payload: {
listId,
@ -127,5 +136,6 @@ export default {
deleteCard,
deleteCurrentCard,
handleCardDelete,
copyCard,
filterText,
};

@ -69,6 +69,13 @@ const handleListDelete = (list) => ({
},
});
const handleListSort = (list) => ({
type: EntryActionTypes.LIST_SORT_HANDLE,
payload: {
list,
},
});
export default {
createListInCurrentBoard,
handleListCreate,
@ -79,4 +86,5 @@ export default {
handleListSort,
deleteList,
handleListDelete,
handleListSort,
};

@ -1,4 +1,5 @@
import usePopup from './use-popup';
import closePopup from './close-popup';
import withPopup from './with-popup';
export { usePopup, closePopup };
export { usePopup, closePopup, withPopup };

@ -0,0 +1,109 @@
import { ResizeObserver } from '@juggle/resize-observer';
import React, { useCallback, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import { Button, Popup as SemanticUIPopup } from 'semantic-ui-react';
import styles from './Popup.module.css';
export default (WrappedComponent, defaultProps) => {
const Popup = React.memo(({ children, ...props }) => {
const [isOpened, setIsOpened] = useState(false);
const wrapper = useRef(null);
const resizeObserver = useRef(null);
const handleOpen = useCallback(() => {
setIsOpened(true);
}, []);
const handleClose = useCallback(() => {
setIsOpened(false);
}, []);
const handleMouseDown = useCallback((event) => {
event.stopPropagation();
}, []);
const handleClick = useCallback((event) => {
event.stopPropagation();
}, []);
const handleTriggerClick = useCallback(
(event) => {
event.stopPropagation();
const { onClick } = children;
if (onClick) {
onClick(event);
}
},
[children],
);
const handleContentRef = useCallback((element) => {
if (resizeObserver.current) {
resizeObserver.current.disconnect();
}
if (!element) {
resizeObserver.current = null;
return;
}
resizeObserver.current = new ResizeObserver(() => {
if (resizeObserver.current.isInitial) {
resizeObserver.current.isInitial = false;
return;
}
wrapper.current.positionUpdate();
});
resizeObserver.current.isInitial = true;
resizeObserver.current.observe(element);
}, []);
const tigger = React.cloneElement(children, {
onClick: handleTriggerClick,
});
return (
<SemanticUIPopup
basic
wide
ref={wrapper}
trigger={tigger}
on="click"
open={isOpened}
position="bottom left"
popperModifiers={[
{
name: 'preventOverflow',
options: {
boundariesElement: 'window',
},
},
]}
className={styles.wrapper}
onOpen={handleOpen}
onClose={handleClose}
onMouseDown={handleMouseDown}
onClick={handleClick}
{...defaultProps} // eslint-disable-line react/jsx-props-no-spreading
>
<div ref={handleContentRef}>
<Button icon="close" onClick={handleClose} className={styles.closeButton} />
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
<WrappedComponent {...props} onClose={handleClose} />
</div>
</SemanticUIPopup>
);
});
Popup.propTypes = {
children: PropTypes.node.isRequired,
};
return Popup;
};

@ -22,7 +22,8 @@ export default {
administrator: 'Administrator',
all: 'All',
allChangesWillBeAutomaticallySavedAfterConnectionRestored:
'All changes will be automatically saved<br />after connection restored.',
'All changes will be automatically saved<br />after connection restored',
areYouSureYouWantToCopyThisCard: 'Are you sure you want to copy this card',
areYouSureYouWantToDeleteThisAttachment: 'Are you sure you want to delete this attachment?',
areYouSureYouWantToDeleteThisBoard: 'Are you sure you want to delete this board?',
areYouSureYouWantToDeleteThisCard: 'Are you sure you want to delete this card?',
@ -38,6 +39,7 @@ export default {
'Are you sure you want to remove this manager from the project?',
areYouSureYouWantToRemoveThisMemberFromBoard:
'Are you sure you want to remove this member from the board?',
areYouSureYouWantToSortThisList: 'Are you sure you want to sort this list?',
attachment: 'Attachment',
attachments: 'Attachments',
authentication: 'Authentication',
@ -51,6 +53,7 @@ export default {
cardNotFound_title: 'Card Not Found',
cardOrActionAreDeleted: 'Card or action are deleted.',
color: 'Color',
copyCard: 'Copy card',
copy_inline: 'copy',
createBoard_title: 'Create Board',
createLabel_title: 'Create Label',
@ -147,6 +150,7 @@ export default {
selectPermissions_title: 'Select Permissions',
selectProject: 'Select project',
settings: 'Settings',
sortList: 'Sort list',
sortList_title: 'Sort List',
stopwatch: 'Stopwatch',
subscribeToMyOwnCardsByDefault: 'Subscribe to my own cards by default',
@ -183,6 +187,8 @@ export default {
addTask: 'Add task',
addToCard: 'Add to card',
addUser: 'Add user',
copy: 'Copy',
copyCard: 'Copy card',
createBoard: 'Create board',
createFile: 'Create file',
createLabel: 'Create label',
@ -235,6 +241,14 @@ export default {
showAllAttachments: 'Show all attachments ({{hidden}} hidden)',
showDetails: 'Show details',
showFewerAttachments: 'Show fewer attachments',
sortByCreatedAt: ' Sort by created at',
sortByCreatorUserIdList: ' Sort by creator',
sortByDueDateList: ' Sort by due date',
sortByIdList: ' Sort by id',
sortByPositionList: ' Sort by position',
sortByTitleList: ' Sort by title',
sortByUpdatedAtList: ' Sort by updated at',
sortList: 'Sort list',
sortList_title: 'Sort List',
start: 'Start',
stop: 'Stop',

@ -15,6 +15,7 @@ export default {
translation: {
common: {
aboutPlanka: 'Om Planka',
account: 'Konto',
actions: 'Åtgärder',
addAttachment_title: 'Bifoga',
@ -25,7 +26,8 @@ export default {
administrator: 'Administratör',
all: 'Alla',
allChangesWillBeAutomaticallySavedAfterConnectionRestored:
'Alla ändringar kommer att sparas automatiskt<br />så fort anslutningen är återställd.',
'Alla ändringar kommer att sparas automatiskt<br />så fort anslutningen är återställd',
areYouSureYouWantToCopyThisCard: 'Är du säker på att du vill kopiera det här kortet',
areYouSureYouWantToDeleteThisAttachment:
'Är du säker på att du vill ta bort den här bilagan?',
areYouSureYouWantToDeleteThisBoard: 'Är du säker på att du vill ta bort den här tavlan?',
@ -34,6 +36,7 @@ export default {
'Är du säker på att du vill ta bort den här kommentaren?',
areYouSureYouWantToDeleteThisLabel: 'Är du säker på att du vill ta bort den här etiketten?',
areYouSureYouWantToDeleteThisList: 'Är du säker på att du vill ta bort den här listan?',
areYouSureYouWantToSortThisList: 'Är du säker på att du vill sortera den här listan?',
areYouSureYouWantToDeleteThisProject: 'Är du säker på att du vill ta bort det här projektet?',
areYouSureYouWantToDeleteThisTask: 'Är du säker på att du vill ta bort den här uppgiften?',
areYouSureYouWantToDeleteThisUser: 'Är du säker på att du vill ta bort den här användaren?',
@ -49,10 +52,14 @@ export default {
background: 'Bakgrund',
board: 'Tavla',
boardNotFound_title: 'Tavla Ej Funnen',
canComment: 'Kan kommentera',
canEditContentOfBoard: 'Kan ändra innehåll för tavla',
canOnlyViewBoard: 'Kan bara titta på tavla',
cardActions_title: 'Kortåtgärder',
cardNotFound_title: 'Kort Ej Funnet',
cardOrActionAreDeleted: 'Kort eller åtgärd är borttagen.',
color: 'Färg',
copyCard: 'Kopiera kort',
createBoard_title: 'Skapa Tavla',
createLabel_title: 'Skapa Etikett',
createNewOneOrSelectExistingOne: 'Skapa en ny eller välj<br />en redan existerande.',
@ -72,14 +79,18 @@ export default {
deleteTask_title: 'Ta Bort Uppgift',
deleteUser_title: 'Ta Bort Användare',
description: 'Beskrivning',
detectAutomatically: 'Upptäck automatiskt',
dropFileToUpload: 'Släpp en fil för att ladda upp',
editor: 'Fileditor',
editAttachment_title: 'Redigera Bilaga',
editAvatar_title: 'Redigera Avatar',
editBoard_title: 'Redigera Tavla',
editDueDate_title: 'Redigera Förfallodatum',
editEmail_title: 'Redigera E-mail',
editInformation_title: 'Ändra Information',
editLabel_title: 'Redigera Etikett',
editPassword_title: 'Redigera Lösenord',
editPermissions_title: 'Ändra behörigheter',
editStopwatch_title: 'Redigera Timer',
editUsername_title: 'Redigera Användarnamn',
email: 'E-mail',
@ -93,10 +104,13 @@ export default {
filterByLabels_title: 'Filtrera Efter Etiketter',
filterByMembers_title: 'Filtrera Efter Medlemmar',
fromComputer_title: 'Från dator',
fromTrello: 'Från Trello',
general: 'Allmänt',
hours: 'Timmar',
importBoard_title: 'Importera Tavla',
invalidCurrentPassword: 'Ogiltigt nuvarande lösenord',
labels: 'Etiketter',
language: 'Språk',
leaveBoard_title: 'Lämna Tavla',
leaveProject_title: 'Lämna Projekt',
list: 'Lista',
@ -126,11 +140,16 @@ export default {
projectNotFound_title: 'Projekt Ej Funnet',
removeManager_title: 'Ta Bort Projektledare',
removeMember_title: 'Ta Bort Medlem',
searchLabels: 'Sök etiketter...',
searchMembers: 'Sök medlemmar...',
searchUsers: 'Sök användare...',
seconds: 'Sekunder',
selectBoard: 'Välj tavla',
selectList: 'Välj lista',
selectPermissions_title: 'Välj behörigheter',
selectProject: 'Välj projekt',
settings: 'Inställningar',
sortList: 'Sortera listan',
stopwatch: 'Timer',
subscribeToMyOwnCardsByDefault: 'Prenumerera på mina egna kort som standard',
taskActions_title: 'Uppgiftsåtgärder',
@ -147,6 +166,8 @@ export default {
username: 'Användarnamn',
usernameAlreadyInUse: 'Användarnamnet används redan',
users: 'Användare',
version: 'Version',
viewer: 'Visare',
writeComment: 'Skriv en kommentar...',
},
@ -158,10 +179,13 @@ export default {
addCard_title: 'Lägg Till Kort',
addComment: 'Lägg till kommentar',
addList: 'Lägg till lista',
addMember: 'Lääg till medlem',
addMoreDetailedDescription: 'Lägg till en mer detaljerad beskrivning',
addTask: 'Lägg till uppgift',
addToCard: 'Lägg till i kort',
addUser: 'Lägg till användare',
copy: 'Kopiera',
copyCard: 'Kopiera kort',
createBoard: 'Skapa tavla',
createFile: 'Skapa fil',
createLabel: 'Skapa etikett',
@ -188,9 +212,12 @@ export default {
editDescription_title: 'Redigera Beskrivning',
editEmail_title: 'Redigera E-mail',
editPassword_title: 'Redigera Lösenord',
editPermissions: 'Ändra behörigheter',
editStopwatch_title: 'Redigera Timer',
editTitle_title: 'Redigera Titel',
editUsername_title: 'Redigera Användarnamn',
hideDetails: 'Dölj detaljer',
import: 'Importera',
leaveBoard: 'Lämna tavla',
leaveProject: 'Lämna projekt',
logOut_title: 'Logga ut',
@ -206,7 +233,16 @@ export default {
removeMember: 'Ta bort medlem',
save: 'Spara',
showAllAttachments: 'Visa alla bilagor ({{hidden}} dolda)',
showDetails: 'Visa detaljer',
showFewerAttachments: 'Visa färre bilagor',
sortByCreatedAt: ' Sortera på skapad',
sortByCreatorUserIdList: ' Sortera på skapad av',
sortByDueDateList: ' Sortera på förfallodatum',
sortByIdList: ' Sortera på id',
sortByPositionList: ' Sortera på position',
sortByTitleList: ' Sortera på titel',
sortByUpdatedAtList: ' Sortera på uppdaterad',
sortList: 'Sortera listan',
start: 'Starta',
stop: 'Stoppa',
subscribe: 'Prenumerera',

@ -189,6 +189,13 @@ export default class extends BaseModel {
Card.upsert(payload.card);
break;
case ActionTypes.CARD_COPY__SUCCESS:
Card.withId(payload.localId).delete();
Card.upsert(payload.card);
break;
case ActionTypes.CARD_CREATE_HANDLE: {
const cardModel = Card.upsert(payload.card);
@ -202,6 +209,7 @@ export default class extends BaseModel {
break;
}
case ActionTypes.CARD_UPDATE:
Card.withId(payload.id).update(payload.data);

@ -79,16 +79,77 @@ export default class extends BaseModel {
break;
}
case ActionTypes.LIST_SORT:
List.withId(payload.id).sortList();
break;
case ActionTypes.LIST_SORT__SUCCESS:
case ActionTypes.LIST_SORT_HANDLE: {
const listModel = List.withId(payload.list.id);
if (listModel) {
listModel.sortList();
}
break;
}
default:
}
}
getOrderedCardsQuerySet() {
getOrderedByIdCardsQuerySet() {
return this.cards.orderBy('id');
}
getOrderedByPositionCardsQuerySet() {
return this.cards.orderBy('position');
}
getOrderedByTitelCardsQuerySet() {
return this.cards.orderBy('name');
}
getOrderedByCreatedAtCardsQuerySet() {
return this.cards.orderBy('createdAt');
}
getOrderedByUpdatedAtCardsQuerySet() {
return this.cards.orderBy('updatedAt');
}
getOrderedByDueDateCardsQuerySet() {
return this.cards.orderBy('dueDate');
}
getFilteredOrderedCardsModelArray() {
let cardModels = this.getOrderedCardsQuerySet().toModelArray();
const sortby = this.selectedOption;
let cardModels = null;
switch (sortby) {
case 'name':
cardModels = this.getOrderedByTitelCardsQuerySet().toModelArray();
break;
case 'id':
cardModels = this.getOrderedByIdCardsQuerySet().toModelArray();
break;
case 'position':
cardModels = this.getOrderedByPositionCardsQuerySet().toModelArray();
break;
case 'createdAt':
cardModels = this.getOrderedByCreatedAtCardsQuerySet().toModelArray();
break;
case 'updatedAt':
cardModels = this.getOrderedByUpdatedAtCardsQuerySet().toModelArray();
break;
case 'dueDate':
cardModels = this.getOrderedByDueDateCardsQuerySet().toModelArray();
break;
case 'creatorUserId':
cardModels = this.getOrderedByDueDateCardsQuerySet().toModelArray();
break;
default:
cardModels = this.getOrderedByPositionCardsQuerySet().toModelArray();
}
const { filterText } = this.board;

@ -72,7 +72,9 @@ export default class extends BaseModel {
break;
case ActionTypes.TASK_CREATE__SUCCESS:
Task.withId(payload.localId).delete();
if (Task.withId(payload.localId)) {
Task.withId(payload.localId).delete();
}
Task.upsert(payload.task);
break;

@ -1,4 +1,4 @@
import { call, put, select } from 'redux-saga/effects';
import { call, put, select, all } from 'redux-saga/effects';
import { goToBoard, goToCard } from './router';
import request from '../request';
@ -7,6 +7,9 @@ import actions from '../../../actions';
import api from '../../../api';
import i18n from '../../../i18n';
import { createLocalId } from '../../../utils/local-id';
import { addLabelToCard } from './labels';
import { createTask } from './tasks';
import { addUserToCard } from './users';
export function* createCard(listId, data, autoOpen) {
const { boardId } = yield select(selectors.selectListById, listId);
@ -40,6 +43,30 @@ export function* createCard(listId, data, autoOpen) {
if (autoOpen) {
yield call(goToCard, card.id);
}
// Add labels to card //
if (nextData.labels) {
const arr = [];
Object.keys(nextData.labels).map((key) => arr.push(nextData.labels[key].id));
yield all(arr?.map((label) => call(addLabelToCard, label, card.id)));
}
// Add tasks to card //
if (nextData.tasks) {
const tasks = [];
Object.keys(nextData.tasks)?.map((key) => tasks.push(nextData.tasks[key]));
tasks.forEach((task) => {
// eslint-disable-next-line no-param-reassign
task.id = `local:${task.id}`;
});
yield all(tasks?.map((task) => call(createTask, card.id, task)));
}
// Add users to card //
if (nextData.users) {
const users = [];
Object.keys(nextData.users)?.map((key) => users.push(nextData.users[key].id));
yield all(users?.map((user) => call(addUserToCard, user, card.id)));
}
}
export function* handleCardCreate({ id }) {

@ -61,6 +61,24 @@ export function* handleListUpdate(list) {
yield put(actions.handleListUpdate(list));
}
export function* sortList(id, data) {
yield put(actions.sortList(id, data));
let list;
try {
({ item: list } = yield call(request, api.sortList, id, data));
} catch (error) {
yield put(actions.sortList.failure(id, error));
return;
}
yield put(actions.sortList.success(list));
}
export function* handleListSort(list) {
yield put(actions.handleListSort(list));
}
export function* moveList(id, index) {
const { boardId } = yield select(selectors.selectListById, id);
const position = yield select(selectors.selectNextListPosition, boardId, index, id);

@ -7,10 +7,18 @@ import api from '../../../api';
import { createLocalId } from '../../../utils/local-id';
export function* createTask(cardId, data) {
const nextData = {
...data,
position: yield select(selectors.selectNextTaskPosition, cardId),
};
let nextData = {};
if (data.position) {
nextData = {
...data,
position: data.position,
};
} else {
nextData = {
...data,
position: yield select(selectors.selectNextTaskPosition, cardId),
};
}
const localId = yield call(createLocalId);

@ -39,8 +39,13 @@ export default function* cardsWatchers() {
takeEvery(EntryActionTypes.CARD_DELETE_HANDLE, ({ payload: { card } }) =>
services.handleCardDelete(card),
),
takeEvery(EntryActionTypes.CARD_COPY, ({ payload: { listId, data, autoOpen } }) =>
services.createCard(listId, data, autoOpen),
takeEvery(EntryActionTypes.TEXT_FILTER_IN_CURRENT_BOARD, ({ payload: { text } }) =>
services.handleTextFilter(text),
),
]);
}

@ -17,6 +17,9 @@ export default function* listsWatchers() {
takeEvery(EntryActionTypes.LIST_UPDATE_HANDLE, ({ payload: { list } }) =>
services.handleListUpdate(list),
),
takeEvery(EntryActionTypes.LIST_SORT_HANDLE, ({ payload: { list } }) =>
services.handleListSort(list),
),
takeEvery(EntryActionTypes.LIST_MOVE, ({ payload: { id, index } }) =>
services.moveList(id, index),
),

@ -92,6 +92,10 @@ const createSocketEventsChannel = () =>
emit(entryActions.handleListDelete(item));
};
const handleListSort = ({ item }) => {
emit(entryActions.handleListSort(item));
};
const handleLabelCreate = ({ item }) => {
emit(entryActions.handleLabelCreate(item));
};
@ -204,6 +208,7 @@ const createSocketEventsChannel = () =>
socket.on('listUpdate', handleListUpdate);
socket.on('listSort', handleListSort);
socket.on('listDelete', handleListDelete);
socket.on('listSort', handleListSort);
socket.on('labelCreate', handleLabelCreate);
socket.on('labelUpdate', handleLabelUpdate);
@ -263,6 +268,7 @@ const createSocketEventsChannel = () =>
socket.off('listUpdate', handleListUpdate);
socket.off('listSort', handleListSort);
socket.off('listDelete', handleListDelete);
socket.off('listSort', handleListSort);
socket.off('labelCreate', handleLabelCreate);
socket.off('labelUpdate', handleLabelUpdate);

1452
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -9,6 +9,7 @@ const Types = {
CREATE_CARD: 'createCard',
MOVE_CARD: 'moveCard',
COMMENT_CARD: 'commentCard',
COPY_CARD: 'copyCard',
};
module.exports = {

Loading…
Cancel
Save