Initial commit

This commit is contained in:
Maksim Eltyshev
2019-08-31 04:07:25 +05:00
commit 5ffef61fe7
613 changed files with 91659 additions and 0 deletions

22
client/src/sagas/app/index.js Executable file
View File

@@ -0,0 +1,22 @@
import {
all, call, fork, take,
} from 'redux-saga/effects';
import watchers from './watchers';
import { initializeAppService } from './services';
import { socket } from '../../api';
import { removeAccessToken } from '../../utils/access-token-storage';
import ActionTypes from '../../constants/ActionTypes';
import Paths from '../../constants/Paths';
export default function* () {
yield all(watchers.map((watcher) => fork(watcher)));
yield call([socket, socket.connect]);
yield call(initializeAppService);
yield take(ActionTypes.LOGOUT);
yield call(removeAccessToken);
window.location.href = Paths.LOGIN;
}

View File

@@ -0,0 +1,35 @@
import { call, put } from 'redux-saga/effects';
import request from './request';
import { fetchActionsFailed, fetchActionsRequested, fetchActionsSucceeded } from '../../../actions';
import api from '../../../api';
// eslint-disable-next-line import/prefer-default-export
export function* fetchActionsRequest(cardId, beforeId) {
yield put(fetchActionsRequested(cardId));
try {
const {
items,
included: { users },
} = yield call(request, api.getActions, cardId, {
beforeId,
});
const action = fetchActionsSucceeded(cardId, items, users);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = fetchActionsFailed(cardId, error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}

View File

@@ -0,0 +1,135 @@
import { call, put } from 'redux-saga/effects';
import request from './request';
import {
createBoardFailed,
createBoardRequested,
createBoardSucceeded,
deleteBoardFailed,
deleteBoardRequested,
deleteBoardSucceeded,
fetchBoardFailed,
fetchBoardRequested,
fetchBoardSucceeded,
updateBoardFailed,
updateBoardRequested,
updateBoardSucceeded,
} from '../../../actions';
import api from '../../../api';
export function* createBoardRequest(projectId, localId, data) {
yield put(
createBoardRequested(localId, {
...data,
projectId,
}),
);
try {
const {
item,
included: { lists, labels },
} = yield call(request, api.createBoard, projectId, data);
const action = createBoardSucceeded(localId, item, lists, labels);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = createBoardFailed(localId, error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* fetchBoardRequest(id) {
yield put(fetchBoardRequested(id));
try {
const {
item,
included: {
lists, labels, cards, cardMemberships, cardLabels, tasks,
},
} = yield call(request, api.getBoard, id);
const action = fetchBoardSucceeded(
item,
lists,
labels,
cards,
cardMemberships,
cardLabels,
tasks,
);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = fetchBoardFailed(id, error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* updateBoardRequest(id, data) {
yield put(updateBoardRequested(id, data));
try {
const { item } = yield call(request, api.updateBoard, id, data);
const action = updateBoardSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = updateBoardFailed(error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* deleteBoardRequest(id) {
yield put(deleteBoardRequested(id));
try {
const { item } = yield call(request, api.deleteBoard, id);
const action = deleteBoardSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = deleteBoardFailed(error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}

View File

@@ -0,0 +1,67 @@
import { call, put } from 'redux-saga/effects';
import request from './request';
import {
createCardLabelFailed,
createCardLabelRequested,
createCardLabelSucceeded,
deleteCardLabelFailed,
deleteCardLabelRequested,
deleteCardLabelSucceeded,
} from '../../../actions';
import api from '../../../api';
export function* createCardLabelRequest(cardId, labelId) {
yield put(
createCardLabelRequested({
cardId,
labelId,
}),
);
try {
const { item } = yield call(request, api.createCardLabel, cardId, {
labelId,
});
const action = createCardLabelSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = createCardLabelFailed(error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* deleteCardLabelRequest(cardId, labelId) {
yield put(deleteCardLabelRequested(cardId, labelId));
try {
const { item } = yield call(request, api.deleteCardLabel, cardId, labelId);
const action = deleteCardLabelSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = deleteCardLabelFailed(cardId, labelId, error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}

View File

@@ -0,0 +1,67 @@
import { call, put } from 'redux-saga/effects';
import request from './request';
import {
createCardMembershipFailed,
createCardMembershipRequested,
createCardMembershipSucceeded,
deleteCardMembershipFailed,
deleteCardMembershipRequested,
deleteCardMembershipSucceeded,
} from '../../../actions';
import api from '../../../api';
export function* createCardMembershipRequest(cardId, userId) {
yield put(
createCardMembershipRequested({
cardId,
userId,
}),
);
try {
const { item } = yield call(request, api.createCardMembership, cardId, {
userId,
});
const action = createCardMembershipSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = createCardMembershipFailed(error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* deleteCardMembershipRequest(cardId, userId) {
yield put(deleteCardMembershipRequested(cardId, userId));
try {
const { item } = yield call(request, api.deleteCardMembership, cardId, userId);
const action = deleteCardMembershipSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = deleteCardMembershipFailed(cardId, userId, error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}

View File

@@ -0,0 +1,119 @@
import { call, put } from 'redux-saga/effects';
import request from './request';
import {
createCardFailed,
createCardRequested,
createCardSucceeded,
deleteCardFailed,
deleteCardRequested,
deleteCardSucceeded,
fetchCardFailed,
fetchCardRequested,
fetchCardSucceeded,
updateCardFailed,
updateCardRequested,
updateCardSucceeded,
} from '../../../actions';
import api from '../../../api';
export function* createCardRequest(listId, localId, data) {
yield put(
createCardRequested(localId, {
...data,
listId,
}),
);
try {
const { item } = yield call(request, api.createCard, listId, data);
const action = createCardSucceeded(localId, item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = createCardFailed(localId, error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* fetchCardRequest(id) {
yield put(fetchCardRequested(id));
try {
const { item } = yield call(request, api.getCard, id);
const action = fetchCardSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = fetchCardFailed(id, error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* updateCardRequest(id, data) {
yield put(updateCardRequested(id, data));
try {
const { item } = yield call(request, api.updateCard, id, data);
const action = updateCardSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = updateCardFailed(error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* deleteCardRequest(id) {
yield put(deleteCardRequested(id));
try {
const { item } = yield call(request, api.deleteCard, id);
const action = deleteCardSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = deleteCardFailed(error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}

View File

@@ -0,0 +1,96 @@
import { call, put } from 'redux-saga/effects';
import request from './request';
import {
createCommentActionFailed,
createCommentActionRequested,
createCommentActionSucceeded,
deleteCommentActionFailed,
deleteCommentActionRequested,
deleteCommentActionSucceeded,
updateCommentActionFailed,
updateCommentActionRequested,
updateCommentActionSucceeded,
} from '../../../actions';
import api from '../../../api';
export function* createCommentActionRequest(cardId, localId, data) {
yield put(
createCommentActionRequested(localId, {
...data,
cardId,
}),
);
try {
const { item } = yield call(request, api.createCommentAction, cardId, data);
const action = createCommentActionSucceeded(localId, item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = createCommentActionFailed(localId, error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* updateCommentActionRequest(id, data) {
yield put(
updateCommentActionRequested(id, {
data,
}),
);
try {
const { item } = yield call(request, api.updateCommentAction, id, data);
const action = updateCommentActionSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = updateCommentActionFailed(id, error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* deleteCommentActionRequest(id) {
yield put(deleteCommentActionRequested(id));
try {
const { item } = yield call(request, api.deleteCommentAction, id);
const action = deleteCommentActionSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = deleteCommentActionFailed(id, error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}

View File

@@ -0,0 +1,15 @@
export * from './users';
export * from './user';
export * from './projects';
export * from './project';
export * from './project-membership';
export * from './board';
export * from './list';
export * from './label';
export * from './card';
export * from './card-membership';
export * from './card-label';
export * from './task';
export * from './actions';
export * from './comment-action';
export * from './notifications';

View File

@@ -0,0 +1,92 @@
import { call, put } from 'redux-saga/effects';
import request from './request';
import {
createLabelFailed,
createLabelRequested,
createLabelSucceeded,
deleteLabelFailed,
deleteLabelRequested,
deleteLabelSucceeded,
updateLabelFailed,
updateLabelRequested,
updateLabelSucceeded,
} from '../../../actions';
import api from '../../../api';
export function* createLabelRequest(boardId, localId, data) {
yield put(
createLabelRequested(localId, {
...data,
boardId,
}),
);
try {
const { item } = yield call(request, api.createLabel, boardId, data);
const action = createLabelSucceeded(localId, item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = createLabelFailed(localId, error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* updateLabelRequest(id, data) {
yield put(updateLabelRequested(id, data));
try {
const { item } = yield call(request, api.updateLabel, id, data);
const action = updateLabelSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = updateLabelFailed(error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* deleteLabelRequest(id) {
yield put(deleteLabelRequested(id));
try {
const { item } = yield call(request, api.deleteLabel, id);
const action = deleteLabelSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = deleteLabelFailed(error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}

View File

@@ -0,0 +1,92 @@
import { call, put } from 'redux-saga/effects';
import request from './request';
import {
createListFailed,
createListRequested,
createListSucceeded,
deleteListFailed,
deleteListRequested,
deleteListSucceeded,
updateListFailed,
updateListRequested,
updateListSucceeded,
} from '../../../actions';
import api from '../../../api';
export function* createListRequest(boardId, localId, data) {
yield put(
createListRequested(localId, {
...data,
boardId,
}),
);
try {
const { item } = yield call(request, api.createList, boardId, data);
const action = createListSucceeded(localId, item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = createListFailed(localId, error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* updateListRequest(id, data) {
yield put(updateListRequested(id, data));
try {
const { item } = yield call(request, api.updateList, id, data);
const action = updateListSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = updateListFailed(error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* deleteListRequest(id) {
yield put(deleteListRequested(id));
try {
const { item } = yield call(request, api.deleteList, id);
const action = deleteListSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = deleteListFailed(error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}

View File

@@ -0,0 +1,65 @@
import { call, put } from 'redux-saga/effects';
import request from './request';
import {
deleteNotificationsFailed,
deleteNotificationsRequested,
deleteNotificationsSucceeded,
fetchNotificationsFailed,
fetchNotificationsRequested,
fetchNotificationsSucceeded,
} from '../../../actions';
import api from '../../../api';
export function* fetchNotificationsRequest() {
yield put(fetchNotificationsRequested());
try {
const {
items,
included: { users, cards, actions },
} = yield call(request, api.getNotifications);
const action = fetchNotificationsSucceeded(items, users, cards, actions);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = fetchNotificationsFailed(error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* deleteNotificationsRequest(ids) {
yield put(deleteNotificationsRequested(ids));
try {
const { items } = yield call(request, api.updateNotifications, ids, {
isRead: true,
});
const action = deleteNotificationsSucceeded(items);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = deleteNotificationsFailed(ids, error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}

View File

@@ -0,0 +1,65 @@
import { call, put } from 'redux-saga/effects';
import request from './request';
import {
createProjectMembershipFailed,
createProjectMembershipRequested,
createProjectMembershipSucceeded,
deleteProjectMembershipFailed,
deleteProjectMembershipRequested,
deleteProjectMembershipSucceeded,
} from '../../../actions';
import api from '../../../api';
export function* createProjectMembershipRequest(projectId, localId, data) {
yield put(
createProjectMembershipRequested(localId, {
...data,
projectId,
}),
);
try {
const { item } = yield call(request, api.createProjectMembership, projectId, data);
const action = createProjectMembershipSucceeded(localId, item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = createProjectMembershipFailed(localId, error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* deleteProjectMembershipRequest(id) {
yield put(deleteProjectMembershipRequested(id));
try {
const { item } = yield call(request, api.deleteProjectMembership, id);
const action = deleteProjectMembershipSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = deleteProjectMembershipFailed(id, error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}

View File

@@ -0,0 +1,90 @@
import { call, put } from 'redux-saga/effects';
import request from './request';
import {
createProjectFailed,
createProjectRequested,
createProjectSucceeded,
deleteProjectFailed,
deleteProjectRequested,
deleteProjectSucceeded,
updateProjectFailed,
updateProjectRequested,
updateProjectSucceeded,
} from '../../../actions';
import api from '../../../api';
export function* createProjectRequest(data) {
yield put(createProjectRequested(data));
try {
const {
item,
included: { users, projectMemberships, boards },
} = yield call(request, api.createProject, data);
const action = createProjectSucceeded(item, users, projectMemberships, boards);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = createProjectFailed(error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* updateProjectRequest(id, data) {
yield put(updateProjectRequested(id, data));
try {
const { item } = yield call(request, api.updateProject, id, data);
const action = updateProjectSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = updateProjectFailed(error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* deleteProjectRequest(id) {
yield put(deleteProjectRequested(id));
try {
const { item } = yield call(request, api.deleteProject, id);
const action = deleteProjectSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = deleteProjectFailed(id, error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}

View File

@@ -0,0 +1,37 @@
import { call, put } from 'redux-saga/effects';
import request from './request';
import {
fetchProjectsFailed,
fetchProjectsRequested,
fetchProjectsSucceeded,
} from '../../../actions';
import api from '../../../api';
// eslint-disable-next-line import/prefer-default-export
export function* fetchProjectsRequest() {
yield put(fetchProjectsRequested());
try {
const {
items,
included: { users, projectMemberships, boards },
} = yield call(request, api.getProjects);
const action = fetchProjectsSucceeded(items, users, projectMemberships, boards);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = fetchProjectsFailed(error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}

View File

@@ -0,0 +1,21 @@
import { call, select, put } from 'redux-saga/effects';
import { accessTokenSelector } from '../../../selectors';
import { logout } from '../../../actions';
import ErrorCodes from '../../../constants/ErrorCodes';
export default function* (method, ...args) {
try {
const accessToken = yield select(accessTokenSelector);
return yield call(method, ...args, {
Authorization: `Bearer ${accessToken}`,
});
} catch (error) {
if (error.code === ErrorCodes.UNAUTHORIZED) {
yield put(logout()); // TODO: next url
}
throw error;
}
}

View File

@@ -0,0 +1,92 @@
import { call, put } from 'redux-saga/effects';
import request from './request';
import {
createTaskFailed,
createTaskRequested,
createTaskSucceeded,
deleteTaskFailed,
deleteTaskRequested,
deleteTaskSucceeded,
updateTaskFailed,
updateTaskRequested,
updateTaskSucceeded,
} from '../../../actions';
import api from '../../../api';
export function* createTaskRequest(cardId, localId, data) {
yield put(
createTaskRequested(localId, {
...data,
cardId,
}),
);
try {
const { item } = yield call(request, api.createTask, cardId, data);
const action = createTaskSucceeded(localId, item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = createTaskFailed(localId, error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* updateTaskRequest(id, data) {
yield put(updateTaskRequested(id, data));
try {
const { item } = yield call(request, api.updateTask, id, data);
const action = updateTaskSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = updateTaskFailed(error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* deleteTaskRequest(id) {
yield put(deleteTaskRequested(id));
try {
const { item } = yield call(request, api.deleteTask, id);
const action = deleteTaskSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = deleteTaskFailed(error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}

View File

@@ -0,0 +1,141 @@
import { call, put } from 'redux-saga/effects';
import request from './request';
import {
createUserFailed,
createUserRequested,
createUserSucceeded,
deleteUserFailed,
deleteUserRequested,
deleteUserSucceeded,
fetchCurrentUserFailed,
fetchCurrentUserRequested,
fetchCurrentUserSucceeded,
updateUserFailed,
updateUserRequested,
updateUserSucceeded,
uploadUserAvatarFailed,
uploadUserAvatarRequested,
uploadUserAvatarSucceeded,
} from '../../../actions';
import api from '../../../api';
export function* createUserRequest(data) {
yield put(createUserRequested(data));
try {
const { item } = yield call(request, api.createUser, data);
const action = createUserSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = createUserFailed(error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* fetchCurrentUserRequest() {
yield put(fetchCurrentUserRequested());
try {
const { item } = yield call(request, api.getCurrentUser);
const action = fetchCurrentUserSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = fetchCurrentUserFailed(error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* updateUserRequest(id, data) {
yield put(updateUserRequested(id, data));
try {
const { item } = yield call(request, api.updateUser, id, data);
const action = updateUserSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = updateUserFailed(id, error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* uploadUserAvatarRequest(id, file) {
yield put(uploadUserAvatarRequested(id));
try {
const { item } = yield call(request, api.uploadUserAvatar, id, file);
const action = uploadUserAvatarSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = uploadUserAvatarFailed(id, error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}
export function* deleteUserRequest(id) {
yield put(deleteUserRequested(id));
try {
const { item } = yield call(request, api.deleteUser, id);
const action = deleteUserSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = deleteUserFailed(error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}

View File

@@ -0,0 +1,30 @@
import { call, put } from 'redux-saga/effects';
import request from './request';
import { fetchUsersFailed, fetchUsersRequested, fetchUsersSucceeded } from '../../../actions';
import api from '../../../api';
// eslint-disable-next-line import/prefer-default-export
export function* fetchUsersRequest() {
yield put(fetchUsersRequested());
try {
const { items } = yield call(request, api.getUsers);
const action = fetchUsersSucceeded(items);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = fetchUsersFailed(error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}

View File

@@ -0,0 +1,16 @@
import { call, select } from 'redux-saga/effects';
import { fetchActionsRequest } from '../requests';
import { lastActionIdByCardIdSelector, pathSelector } from '../../../selectors';
export function* fetchActionsService(cardId) {
const lastId = yield select(lastActionIdByCardIdSelector, cardId);
yield call(fetchActionsRequest, cardId, lastId);
}
export function* fetchActionsInCurrentCardService() {
const { cardId } = yield select(pathSelector);
yield call(fetchActionsService, cardId);
}

View File

@@ -0,0 +1,50 @@
import { call, put, select } from 'redux-saga/effects';
import { runPathActionsService } from './router';
import {
fetchCardRequest,
fetchCurrentUserRequest,
fetchNotificationsRequest,
fetchProjectsRequest,
fetchUsersRequest,
} from '../requests';
import { pathsMatchSelector } from '../../../selectors';
import { appInitialized } from '../../../actions';
import i18n from '../../../i18n';
import Paths from '../../../constants/Paths';
export function* loadLocaleService(language) {
try {
yield call(i18n.loadAppLocale, language);
return true;
} catch (error) {
return false;
}
}
export function* initializeAppService() {
const {
payload: {
user: { isAdmin },
},
} = yield call(fetchCurrentUserRequest); // TODO: success check
if (isAdmin) {
yield call(fetchUsersRequest);
}
yield call(fetchProjectsRequest);
const pathsMatch = yield select(pathsMatchSelector);
if (pathsMatch && pathsMatch.path === Paths.CARDS) {
yield call(fetchCardRequest, pathsMatch.params.id);
}
yield call(fetchNotificationsRequest);
yield call(runPathActionsService, pathsMatch);
yield call(loadLocaleService, i18n.language);
yield put(appInitialized());
}

View File

@@ -0,0 +1,70 @@
import { call, put, select } from 'redux-saga/effects';
import { goToBoardService, goToProjectService } from './router';
import { createBoardRequest, deleteBoardRequest, updateBoardRequest } from '../requests';
import {
boardByIdSelector,
maxIdSelector,
nextBoardPositionSelector,
pathSelector,
} from '../../../selectors';
import { createBoard, deleteBoard, updateBoard } from '../../../actions';
import { nextLocalId } from '../../../utils/local-id';
import { Board } from '../../../models';
export function* createBoardService(projectId, data) {
const nextData = {
...data,
position: yield select(nextBoardPositionSelector, projectId),
};
const localId = nextLocalId(yield select(maxIdSelector, Board.modelName));
yield put(
createBoard({
...nextData,
projectId,
id: localId,
}),
);
const {
success,
payload: { board },
} = yield call(createBoardRequest, projectId, localId, nextData);
if (success) {
yield call(goToBoardService, board.id);
}
}
export function* createBoardInCurrentProjectService(data) {
const { projectId } = yield select(pathSelector);
yield call(createBoardService, projectId, data);
}
export function* updateBoardService(id, data) {
yield put(updateBoard(id, data));
yield call(updateBoardRequest, id, data);
}
export function* moveBoardService(id, index) {
const { projectId } = yield select(boardByIdSelector, id);
const position = yield select(nextBoardPositionSelector, projectId, index, id);
yield call(updateBoardService, id, {
position,
});
}
export function* deleteBoardService(id) {
const { boardId, projectId } = yield select(pathSelector);
if (id === boardId) {
yield call(goToProjectService, projectId);
}
yield put(deleteBoard(id));
yield call(deleteBoardRequest, id);
}

View File

@@ -0,0 +1,64 @@
import { call, put, select } from 'redux-saga/effects';
import { goToBoardService } from './router';
import { createCardRequest, deleteCardRequest, updateCardRequest } from '../requests';
import { maxIdSelector, nextCardPositionSelector, pathSelector } from '../../../selectors';
import { createCard, deleteCard, updateCard } from '../../../actions';
import { nextLocalId } from '../../../utils/local-id';
import { Card } from '../../../models';
export function* createCardService(listId, data) {
const nextData = {
...data,
position: yield select(nextCardPositionSelector, listId),
};
const localId = nextLocalId(yield select(maxIdSelector, Card.modelName));
yield put(
createCard({
...nextData,
listId,
id: localId,
}),
);
yield call(createCardRequest, listId, localId, nextData);
}
export function* updateCardService(id, data) {
yield put(updateCard(id, data));
yield call(updateCardRequest, id, data);
}
export function* updateCurrentCardService(data) {
const { cardId } = yield select(pathSelector);
yield call(updateCardService, cardId, data);
}
export function* moveCardService(id, listId, index) {
const position = yield select(nextCardPositionSelector, listId, index, id);
yield call(updateCardService, id, {
listId,
position,
});
}
export function* deleteCardService(id) {
const { cardId, boardId } = yield select(pathSelector);
if (id === cardId) {
yield call(goToBoardService, boardId);
}
yield put(deleteCard(id));
yield call(deleteCardRequest, id);
}
export function* deleteCurrentCardService() {
const { cardId } = yield select(pathSelector);
yield call(deleteCardService, cardId);
}

View File

@@ -0,0 +1,45 @@
import { call, put, select } from 'redux-saga/effects';
import {
createCommentActionRequest,
deleteCommentActionRequest,
updateCommentActionRequest,
} from '../requests';
import { currentUserIdSelector, maxIdSelector, pathSelector } from '../../../selectors';
import { createCommentAction, deleteCommentAction, updateCommentAction } from '../../../actions';
import { nextLocalId } from '../../../utils/local-id';
import { Action } from '../../../models';
import { ActionTypes } from '../../../constants/Enums';
export function* createCommentActionService(cardId, data) {
const localId = nextLocalId(yield select(maxIdSelector, Action.modelName));
const userId = yield select(currentUserIdSelector);
yield put(
createCommentAction({
cardId,
userId,
data,
id: localId,
type: ActionTypes.COMMENT_CARD,
}),
);
yield call(createCommentActionRequest, cardId, localId, data);
}
export function* createCommentActionInCurrentCardService(data) {
const { cardId } = yield select(pathSelector);
yield call(createCommentActionService, cardId, data);
}
export function* updateCommentActionService(id, data) {
yield put(updateCommentAction(id, data));
yield call(updateCommentActionRequest, id, data);
}
export function* deleteCommentActionService(id) {
yield put(deleteCommentAction(id));
yield call(deleteCommentActionRequest, id);
}

View File

@@ -0,0 +1,17 @@
export * from './router';
export * from './socket';
export * from './login';
export * from './app';
export * from './modal';
export * from './user';
export * from './project';
export * from './project-membership';
export * from './board';
export * from './list';
export * from './label';
export * from './card';
export * from './task';
export * from './actions';
export * from './comment-action';
export * from './notifications';
export * from './notification';

View File

@@ -0,0 +1,93 @@
import { call, put, select } from 'redux-saga/effects';
import {
createCardLabelRequest,
createLabelRequest,
deleteCardLabelRequest,
deleteLabelRequest,
updateLabelRequest,
} from '../requests';
import { maxIdSelector, pathSelector } from '../../../selectors';
import {
addLabelToBoardFilter,
addLabelToCard,
createLabel,
deleteLabel,
removeLabelFromBoardFilter,
removeLabelFromCard,
updateLabel,
} from '../../../actions';
import { nextLocalId } from '../../../utils/local-id';
import { Label } from '../../../models';
export function* createLabelService(boardId, data) {
const localId = nextLocalId(yield select(maxIdSelector, Label.modelName));
yield put(
createLabel({
...data,
boardId,
id: localId,
}),
);
yield call(createLabelRequest, boardId, localId, data);
}
export function* createLabelInCurrentBoardService(data) {
const { boardId } = yield select(pathSelector);
yield call(createLabelService, boardId, data);
}
export function* updateLabelService(id, data) {
yield put(updateLabel(id, data));
yield call(updateLabelRequest, id, data);
}
export function* deleteLabelService(id) {
yield put(deleteLabel(id));
yield call(deleteLabelRequest, id);
}
export function* addLabelToCardService(id, cardId) {
yield put(addLabelToCard(id, cardId));
yield call(createCardLabelRequest, cardId, id);
}
export function* addLabelToCurrentCardService(id) {
const { cardId } = yield select(pathSelector);
yield call(addLabelToCardService, id, cardId);
}
export function* removeLabelFromCardService(id, cardId) {
yield put(removeLabelFromCard(id, cardId));
yield call(deleteCardLabelRequest, cardId, id);
}
export function* removeLabelFromCurrentCardService(id) {
const { cardId } = yield select(pathSelector);
yield call(removeLabelFromCardService, id, cardId);
}
export function* addLabelToBoardFilterService(id, boardId) {
yield put(addLabelToBoardFilter(id, boardId));
}
export function* addLabelToFilterInCurrentBoardService(id) {
const { boardId } = yield select(pathSelector);
yield call(addLabelToBoardFilterService, id, boardId);
}
export function* removeLabelFromBoardFilterService(id, boardId) {
yield put(removeLabelFromBoardFilter(id, boardId));
}
export function* removeLabelFromFilterInCurrentBoardService(id) {
const { boardId } = yield select(pathSelector);
yield call(removeLabelFromBoardFilterService, id, boardId);
}

View File

@@ -0,0 +1,56 @@
import { call, put, select } from 'redux-saga/effects';
import { createListRequest, deleteListRequest, updateListRequest } from '../requests';
import {
listByIdSelector,
maxIdSelector,
nextListPositionSelector,
pathSelector,
} from '../../../selectors';
import { createList, deleteList, updateList } from '../../../actions';
import { nextLocalId } from '../../../utils/local-id';
import { List } from '../../../models';
export function* createListService(boardId, data) {
const nextData = {
...data,
position: yield select(nextListPositionSelector, boardId),
};
const localId = nextLocalId(yield select(maxIdSelector, List.modelName));
yield put(
createList({
...nextData,
boardId,
id: localId,
}),
);
yield call(createListRequest, boardId, localId, nextData);
}
export function* createListInCurrentBoardService(data) {
const { boardId } = yield select(pathSelector);
yield call(createListService, boardId, data);
}
export function* updateListService(id, data) {
yield put(updateList(id, data));
yield call(updateListRequest, id, data);
}
export function* moveListService(id, index) {
const { boardId } = yield select(listByIdSelector, id);
const position = yield select(nextListPositionSelector, boardId, index, id);
yield call(updateListService, id, {
position,
});
}
export function* deleteListService(id) {
yield put(deleteList(id));
yield call(deleteListRequest, id);
}

View File

@@ -0,0 +1,8 @@
import { put } from 'redux-saga/effects';
import { logout } from '../../../actions';
// eslint-disable-next-line import/prefer-default-export
export function* logoutService() {
yield put(logout());
}

View File

@@ -0,0 +1,11 @@
import { put } from 'redux-saga/effects';
import { closeModal, openModal } from '../../../actions';
export function* openModalService(type) {
yield put(openModal(type));
}
export function* closeModalService() {
yield put(closeModal());
}

View File

@@ -0,0 +1,8 @@
import { call } from 'redux-saga/effects';
import { deleteNotificationsService } from './notifications';
// eslint-disable-next-line import/prefer-default-export
export function* deleteNotificationService(id) {
yield call(deleteNotificationsService, [id]);
}

View File

@@ -0,0 +1,18 @@
import { call, put, select } from 'redux-saga/effects';
import { deleteNotificationsRequest } from '../requests';
import { notificationIdsForCurrentCardSelector } from '../../../selectors';
import { deleteNotifications } from '../../../actions';
export function* deleteNotificationsService(ids) {
yield put(deleteNotifications(ids));
yield call(deleteNotificationsRequest, ids);
}
export function* deleteNotificationsInCurrentCardService() {
const notificationIds = yield select(notificationIdsForCurrentCardSelector);
if (notificationIds && notificationIds.length > 0) {
yield call(deleteNotificationsService, notificationIds);
}
}

View File

@@ -0,0 +1,32 @@
import { call, put, select } from 'redux-saga/effects';
import { createProjectMembershipRequest, deleteProjectMembershipRequest } from '../requests';
import { maxIdSelector, pathSelector } from '../../../selectors';
import { createProjectMembership, deleteProjectMembership } from '../../../actions';
import { nextLocalId } from '../../../utils/local-id';
import { ProjectMembership } from '../../../models';
export function* createProjectMembershipService(projectId, data) {
const localId = nextLocalId(yield select(maxIdSelector, ProjectMembership.modelName));
yield put(
createProjectMembership({
...data,
projectId,
id: localId,
}),
);
yield call(createProjectMembershipRequest, projectId, localId, data);
}
export function* createMembershipInCurrentProjectService(data) {
const { projectId } = yield select(pathSelector);
yield call(createProjectMembershipService, projectId, data);
}
export function* deleteProjectMembershipService(id) {
yield put(deleteProjectMembership(id));
yield call(deleteProjectMembershipRequest, id);
}

View File

@@ -0,0 +1,47 @@
import { call, put, select } from 'redux-saga/effects';
import { goToProjectService, goToRootService } from './router';
import { createProjectRequest, deleteProjectRequest, updateProjectRequest } from '../requests';
import { pathSelector } from '../../../selectors';
import { createProject, deleteProject, updateProject } from '../../../actions';
export function* createProjectService(data) {
yield put(createProject(data));
const {
success,
payload: { project },
} = yield call(createProjectRequest, data);
if (success) {
yield call(goToProjectService, project.id);
}
}
export function* updateProjectService(id, data) {
yield put(updateProject(id, data));
yield call(updateProjectRequest, id, data);
}
export function* updateCurrentProjectService(data) {
const { projectId } = yield select(pathSelector);
yield call(updateProjectService, projectId, data);
}
export function* deleteProjectService(id) {
const { projectId } = yield select(pathSelector);
if (id === projectId) {
yield call(goToRootService);
}
yield put(deleteProject(id));
yield call(deleteProjectRequest, id);
}
export function* deleteCurrentProjectService() {
const { projectId } = yield select(pathSelector);
yield call(deleteProjectService, projectId);
}

View File

@@ -0,0 +1,71 @@
import { call, put, select } from 'redux-saga/effects';
import { push } from 'connected-react-router';
import { deleteNotificationsInCurrentCardService } from './notifications';
import { fetchBoardRequest } from '../requests';
import {
currentBoardSelector,
isAppInitializingSelector,
pathsMatchSelector,
} from '../../../selectors';
import Paths from '../../../constants/Paths';
export function* goToRootService() {
yield put(push(Paths.ROOT));
}
export function* goToProjectService(projectId) {
yield put(push(Paths.PROJECTS.replace(':id', projectId)));
}
export function* goToBoardService(boardId) {
yield put(push(Paths.BOARDS.replace(':id', boardId)));
}
export function* goToCardService(cardId) {
yield put(push(Paths.CARDS.replace(':id', cardId)));
}
export function* runPathActionsService(pathsMatch) {
if (!pathsMatch) {
return;
}
switch (pathsMatch.path) {
case Paths.BOARDS:
case Paths.CARDS: {
const currentBoard = yield select(currentBoardSelector);
if (currentBoard && currentBoard.isFetching === null) {
yield call(fetchBoardRequest, currentBoard.id);
}
if (pathsMatch.path === Paths.CARDS) {
yield call(deleteNotificationsInCurrentCardService);
}
break;
}
default:
}
}
export function* locationChangedService() {
const pathsMatch = yield select(pathsMatchSelector);
if (pathsMatch) {
switch (pathsMatch.path) {
case Paths.LOGIN:
yield call(goToRootService);
break;
default:
}
}
const isAppInitializing = yield select(isAppInitializingSelector);
if (!isAppInitializing) {
yield call(runPathActionsService, pathsMatch);
}
}

View File

@@ -0,0 +1,232 @@
import { call, put, select } from 'redux-saga/effects';
import { goToBoardService, goToProjectService, goToRootService } from './router';
import { logoutService } from './login';
import { closeModalService } from './modal';
import { deleteNotificationsRequest, fetchUsersRequest } from '../requests';
import {
currentModalSelector,
currentUserIdSelector,
currentUserSelector,
pathSelector,
} from '../../../selectors';
import {
createActionReceived,
createBoardReceived,
createCardLabelReceived,
createCardMembershipReceived,
createCardReceived,
createLabelReceived,
createListReceived,
createNotificationReceived,
createProjectMembershipReceived,
createProjectReceived,
createTaskReceived,
createUserReceived,
deleteActionReceived,
deleteCardLabelReceived,
deleteCardMembershipReceived,
deleteCardReceived,
deleteBoardReceived,
deleteLabelReceived,
deleteListReceived,
deleteNotificationReceived,
deleteProjectMembershipReceived,
deleteProjectReceived,
deleteTaskReceived,
deleteUserReceived,
socketDisconnected,
socketReconnected,
updateActionReceived,
updateBoardReceived,
updateCardReceived,
updateLabelReceived,
updateListReceived,
updateProjectReceived,
updateTaskReceived,
updateUserReceived,
} from '../../../actions';
import ModalTypes from '../../../constants/ModalTypes';
export function* socketDisconnectedService() {
yield put(socketDisconnected());
}
// TODO: refetch state on reconnect
export function* socketReconnectedService() {
yield put(socketReconnected());
}
export function* createUserReceivedService(user) {
yield put(createUserReceived(user));
}
export function* updateUserReceivedService(user) {
const currentUser = yield select(currentUserSelector);
if (user.id === currentUser.id) {
if (currentUser.isAdmin) {
if (!user.isAdmin) {
const currentModal = yield select(currentModalSelector);
if (currentModal === ModalTypes.USERS) {
yield call(closeModalService);
}
}
} else if (user.isAdmin) {
yield call(fetchUsersRequest);
}
}
yield put(updateUserReceived(user));
}
export function* deleteUserReceivedService(user) {
const currentUserId = yield select(currentUserIdSelector);
if (user.id === currentUserId) {
yield call(logoutService);
}
yield put(deleteUserReceived(user));
}
export function* createProjectReceivedService(project, users, projectMemberships, boards) {
yield put(createProjectReceived(project, users, projectMemberships, boards));
}
export function* updateProjectReceivedService(project) {
yield put(updateProjectReceived(project));
}
export function* deleteProjectReceivedService(project) {
const { projectId } = yield select(pathSelector);
if (project.id === projectId) {
yield call(goToRootService);
}
yield put(deleteProjectReceived(project));
}
export function* createProjectMembershipReceivedService(projectMembership, user) {
yield put(createProjectMembershipReceived(projectMembership, user));
}
export function* deleteProjectMembershipReceivedService(projectMembership) {
yield put(deleteProjectMembershipReceived(projectMembership));
}
export function* createBoardReceivedService(board, lists, labels) {
yield put(createBoardReceived(board, lists, labels));
}
export function* updateBoardReceivedService(board) {
yield put(updateBoardReceived(board));
}
export function* deleteBoardReceivedService(board) {
const { boardId, projectId } = yield select(pathSelector);
if (board.id === boardId) {
yield call(goToProjectService, projectId);
}
yield put(deleteBoardReceived(board));
}
export function* createListReceivedService(list) {
yield put(createListReceived(list));
}
export function* updateListReceivedService(list) {
yield put(updateListReceived(list));
}
export function* deleteListReceivedService(list) {
yield put(deleteListReceived(list));
}
export function* createLabelReceivedService(label) {
yield put(createLabelReceived(label));
}
export function* updateLabelReceivedService(label) {
yield put(updateLabelReceived(label));
}
export function* deleteLabelReceivedService(label) {
yield put(deleteLabelReceived(label));
}
export function* createCardReceivedService(card) {
yield put(createCardReceived(card));
}
export function* updateCardReceivedService(card) {
yield put(updateCardReceived(card));
}
export function* deleteCardReceivedService(card) {
const { cardId, boardId } = yield select(pathSelector);
if (card.id === cardId) {
yield call(goToBoardService, boardId);
}
yield put(deleteCardReceived(card));
}
export function* createCardMembershipReceivedService(cardMembership) {
yield put(createCardMembershipReceived(cardMembership));
}
export function* deleteCardMembershipReceivedService(cardMembership) {
yield put(deleteCardMembershipReceived(cardMembership));
}
export function* createCardLabelReceivedService(cardLabel) {
yield put(createCardLabelReceived(cardLabel));
}
export function* deleteCardLabelReceivedService(cardLabel) {
yield put(deleteCardLabelReceived(cardLabel));
}
export function* createTaskReceivedService(task) {
yield put(createTaskReceived(task));
}
export function* updateTaskReceivedService(task) {
yield put(updateTaskReceived(task));
}
export function* deleteTaskReceivedService(task) {
yield put(deleteTaskReceived(task));
}
export function* createActionReceivedService(action) {
yield put(createActionReceived(action));
}
export function* updateActionReceivedService(action) {
yield put(updateActionReceived(action));
}
export function* deleteActionReceivedService(action) {
yield put(deleteActionReceived(action));
}
export function* createNotificationReceivedService(notification, user, card, action) {
const { cardId } = yield select(pathSelector);
if (card.id === cardId) {
yield call(deleteNotificationsRequest, [notification.id]);
} else {
yield put(createNotificationReceived(notification, user, card, action));
}
}
export function* deleteNotificationReceivedService(notification) {
yield put(deleteNotificationReceived(notification));
}

View File

@@ -0,0 +1,37 @@
import { call, put, select } from 'redux-saga/effects';
import { createTaskRequest, deleteTaskRequest, updateTaskRequest } from '../requests';
import { maxIdSelector, pathSelector } from '../../../selectors';
import { createTask, deleteTask, updateTask } from '../../../actions';
import { nextLocalId } from '../../../utils/local-id';
import { Task } from '../../../models';
export function* createTaskService(cardId, data) {
const localId = nextLocalId(yield select(maxIdSelector, Task.modelName));
yield put(
createTask({
...data,
cardId,
id: localId,
}),
);
yield call(createTaskRequest, cardId, localId, data);
}
export function* createTaskInCurrentCardService(data) {
const { cardId } = yield select(pathSelector);
yield call(createTaskService, cardId, data);
}
export function* updateTaskService(id, data) {
yield put(updateTask(id, data));
yield call(updateTaskRequest, id, data);
}
export function* deleteTaskService(id) {
yield put(deleteTask(id));
yield call(deleteTaskRequest, id);
}

View File

@@ -0,0 +1,96 @@
import { call, put, select } from 'redux-saga/effects';
import {
createCardMembershipRequest,
createUserRequest,
deleteCardMembershipRequest,
deleteUserRequest,
updateUserRequest,
uploadUserAvatarRequest,
} from '../requests';
import { currentUserIdSelector, pathSelector } from '../../../selectors';
import {
addUserToBoardFilter,
addUserToCard,
clearUserCreationError,
createUser,
deleteUser,
updateUser,
removeUserFromBoardFilter,
removeUserFromCard,
} from '../../../actions';
export function* createUserService(data) {
yield put(createUser(data));
yield call(createUserRequest, data);
}
export function* clearUserCreationErrorService() {
yield put(clearUserCreationError());
}
export function* updateUserService(id, data) {
yield put(updateUser(id, data));
yield call(updateUserRequest, id, data);
}
export function* updateCurrentUserService(data) {
const id = yield select(currentUserIdSelector);
yield call(updateUserService, id, data);
}
export function* uploadCurrentUserAvatarService(file) {
const id = yield select(currentUserIdSelector);
yield call(uploadUserAvatarRequest, id, file);
}
export function* deleteUserService(id) {
yield put(deleteUser(id));
yield call(deleteUserRequest, id);
}
export function* addUserToCardService(id, cardId) {
const currentUserId = yield select(currentUserIdSelector);
yield put(addUserToCard(id, cardId, id === currentUserId));
yield call(createCardMembershipRequest, cardId, id);
}
export function* addUserToCurrentCardService(id) {
const { cardId } = yield select(pathSelector);
yield call(addUserToCardService, id, cardId);
}
export function* removeUserFromCardService(id, cardId) {
yield put(removeUserFromCard(id, cardId));
yield call(deleteCardMembershipRequest, cardId, id);
}
export function* removeUserFromCurrentCardService(id) {
const { cardId } = yield select(pathSelector);
yield call(removeUserFromCardService, id, cardId);
}
export function* addUserToBoardFilterService(id, boardId) {
yield put(addUserToBoardFilter(id, boardId));
}
export function* addUserToFilterInCurrentBoardService(id) {
const { boardId } = yield select(pathSelector);
yield call(addUserToBoardFilterService, id, boardId);
}
export function* removeUserFromBoardFilterService(id, boardId) {
yield put(removeUserFromBoardFilter(id, boardId));
}
export function* removeUserFromFilterInCurrentBoardService(id) {
const { boardId } = yield select(pathSelector);
yield call(removeUserFromBoardFilterService, id, boardId);
}

View File

@@ -0,0 +1,9 @@
import { takeLatest } from 'redux-saga/effects';
import { fetchActionsInCurrentCardService } from '../services';
import EntryActionTypes from '../../../constants/EntryActionTypes';
export default function* () {
// eslint-disable-next-line max-len
yield takeLatest(EntryActionTypes.ACTIONS_IN_CURRENT_CARD_FETCH, () => fetchActionsInCurrentCardService());
}

View File

@@ -0,0 +1,20 @@
import { all, takeLatest } from 'redux-saga/effects';
import {
createBoardInCurrentProjectService,
deleteBoardService,
moveBoardService,
updateBoardService,
} from '../services';
import EntryActionTypes from '../../../constants/EntryActionTypes';
export default function* () {
yield all([
/* eslint-disable max-len */
takeLatest(EntryActionTypes.BOARD_IN_CURRENT_PROJECT_CREATE, ({ payload: { data } }) => createBoardInCurrentProjectService(data)),
takeLatest(EntryActionTypes.BOARD_UPDATE, ({ payload: { id, data } }) => updateBoardService(id, data)),
takeLatest(EntryActionTypes.BOARD_MOVE, ({ payload: { id, index } }) => moveBoardService(id, index)),
/* eslint-enable max-len */
takeLatest(EntryActionTypes.BOARD_DELETE, ({ payload: { id } }) => deleteBoardService(id)),
]);
}

View File

@@ -0,0 +1,24 @@
import { all, takeLatest } from 'redux-saga/effects';
import {
createCardService,
deleteCardService,
deleteCurrentCardService,
moveCardService,
updateCardService,
updateCurrentCardService,
} from '../services';
import EntryActionTypes from '../../../constants/EntryActionTypes';
export default function* () {
yield all([
/* eslint-disable max-len */
takeLatest(EntryActionTypes.CARD_CREATE, ({ payload: { listId, data } }) => createCardService(listId, data)),
takeLatest(EntryActionTypes.CARD_UPDATE, ({ payload: { id, data } }) => updateCardService(id, data)),
takeLatest(EntryActionTypes.CURRENT_CARD_UPDATE, ({ payload: { data } }) => updateCurrentCardService(data)),
takeLatest(EntryActionTypes.CARD_MOVE, ({ payload: { id, listId, index } }) => moveCardService(id, listId, index)),
/* eslint-enable max-len */
takeLatest(EntryActionTypes.CARD_DELETE, ({ payload: { id } }) => deleteCardService(id)),
takeLatest(EntryActionTypes.CURRENT_CARD_DELETE, () => deleteCurrentCardService()),
]);
}

View File

@@ -0,0 +1,18 @@
import { all, takeLatest } from 'redux-saga/effects';
import {
createCommentActionInCurrentCardService,
deleteCommentActionService,
updateCommentActionService,
} from '../services';
import EntryActionTypes from '../../../constants/EntryActionTypes';
export default function* () {
yield all([
/* eslint-disable max-len */
takeLatest(EntryActionTypes.COMMENT_ACTION_IN_CURRENT_CARD_CREATE, ({ payload: { data } }) => createCommentActionInCurrentCardService(data)),
takeLatest(EntryActionTypes.COMMENT_ACTION_UPDATE, ({ payload: { id, data } }) => updateCommentActionService(id, data)),
takeLatest(EntryActionTypes.COMMENT_ACTION_DELETE, ({ payload: { id } }) => deleteCommentActionService(id)),
/* eslint-enable max-len */
]);
}

View File

@@ -0,0 +1,33 @@
import router from './router';
import socket from './socket';
import login from './login';
import modal from './modal';
import user from './user';
import project from './project';
import projectMembership from './project-membership';
import board from './board';
import list from './list';
import label from './label';
import card from './card';
import task from './task';
import actions from './actions';
import commentAction from './comment-action';
import notification from './notification';
export default [
router,
socket,
login,
modal,
user,
project,
projectMembership,
board,
list,
label,
card,
task,
actions,
commentAction,
notification,
];

View File

@@ -0,0 +1,32 @@
import { all, takeLatest } from 'redux-saga/effects';
import {
addLabelToCardService,
addLabelToCurrentCardService,
addLabelToFilterInCurrentBoardService,
createLabelInCurrentBoardService,
deleteLabelService,
removeLabelFromCardService,
removeLabelFromCurrentCardService,
removeLabelFromFilterInCurrentBoardService,
updateLabelService,
} from '../services';
import EntryActionTypes from '../../../constants/EntryActionTypes';
export default function* () {
yield all([
/* eslint-disable max-len */
takeLatest(EntryActionTypes.LABEL_IN_CURRENT_BOARD_CREATE, ({ payload: { data } }) => createLabelInCurrentBoardService(data)),
takeLatest(EntryActionTypes.LABEL_UPDATE, ({ payload: { id, data } }) => updateLabelService(id, data)),
/* eslint-enable max-len */
takeLatest(EntryActionTypes.LABEL_DELETE, ({ payload: { id } }) => deleteLabelService(id)),
/* eslint-disable max-len */
takeLatest(EntryActionTypes.LABEL_TO_CARD_ADD, ({ payload: { id, cardId } }) => addLabelToCardService(id, cardId)),
takeLatest(EntryActionTypes.LABEL_TO_CURRENT_CARD_ADD, ({ payload: { id } }) => addLabelToCurrentCardService(id)),
takeLatest(EntryActionTypes.LABEL_FROM_CARD_REMOVE, ({ payload: { id, cardId } }) => removeLabelFromCardService(id, cardId)),
takeLatest(EntryActionTypes.LABEL_FROM_CURRENT_CARD_REMOVE, ({ payload: { id } }) => removeLabelFromCurrentCardService(id)),
takeLatest(EntryActionTypes.LABEL_TO_FILTER_IN_CURRENT_BOARD_ADD, ({ payload: { id } }) => addLabelToFilterInCurrentBoardService(id)),
takeLatest(EntryActionTypes.LABEL_FROM_FILTER_IN_CURRENT_BOARD_REMOVE, ({ payload: { id } }) => removeLabelFromFilterInCurrentBoardService(id)),
/* eslint-enable max-len */
]);
}

View File

@@ -0,0 +1,20 @@
import { all, takeLatest } from 'redux-saga/effects';
import {
createListInCurrentBoardService,
deleteListService,
moveListService,
updateListService,
} from '../services';
import EntryActionTypes from '../../../constants/EntryActionTypes';
export default function* () {
yield all([
/* eslint-disable max-len */
takeLatest(EntryActionTypes.LIST_IN_CURRENT_BOARD_CREATE, ({ payload: { data } }) => createListInCurrentBoardService(data)),
takeLatest(EntryActionTypes.LIST_UPDATE, ({ payload: { id, data } }) => updateListService(id, data)),
takeLatest(EntryActionTypes.LIST_MOVE, ({ payload: { id, index } }) => moveListService(id, index)),
/* eslint-enable max-len */
takeLatest(EntryActionTypes.LIST_DELETE, ({ payload: { id } }) => deleteListService(id)),
]);
}

View File

@@ -0,0 +1,8 @@
import { takeLatest } from 'redux-saga/effects';
import { logoutService } from '../services';
import EntryActionTypes from '../../../constants/EntryActionTypes';
export default function* () {
yield takeLatest(EntryActionTypes.LOGOUT, () => logoutService());
}

View File

@@ -0,0 +1,11 @@
import { all, takeLatest } from 'redux-saga/effects';
import { closeModalService, openModalService } from '../services';
import EntryActionTypes from '../../../constants/EntryActionTypes';
export default function* () {
yield all([
takeLatest(EntryActionTypes.MODAL_OPEN, ({ payload: { type } }) => openModalService(type)),
takeLatest(EntryActionTypes.MODAL_CLOSE, () => closeModalService()),
]);
}

View File

@@ -0,0 +1,9 @@
import { takeLatest } from 'redux-saga/effects';
import { deleteNotificationService } from '../services';
import EntryActionTypes from '../../../constants/EntryActionTypes';
export default function* () {
// eslint-disable-next-line max-len
yield takeLatest(EntryActionTypes.NOTIFICATION_DELETE, ({ payload: { id } }) => deleteNotificationService(id));
}

View File

@@ -0,0 +1,16 @@
import { all, takeLatest } from 'redux-saga/effects';
import {
createMembershipInCurrentProjectService,
deleteProjectMembershipService,
} from '../services';
import EntryActionTypes from '../../../constants/EntryActionTypes';
export default function* () {
yield all([
/* eslint-disable max-len */
takeLatest(EntryActionTypes.MEMBERSHIP_IN_CURRENT_PROJECT_CREATE, ({ payload: { data } }) => createMembershipInCurrentProjectService(data)),
takeLatest(EntryActionTypes.PROJECT_MEMBERSHIP_DELETE, ({ payload: { id } }) => deleteProjectMembershipService(id)),
/* eslint-enable max-len */
]);
}

View File

@@ -0,0 +1,18 @@
import { all, takeLatest } from 'redux-saga/effects';
import {
createProjectService,
deleteCurrentProjectService,
updateCurrentProjectService,
} from '../services';
import EntryActionTypes from '../../../constants/EntryActionTypes';
export default function* () {
yield all([
/* eslint-disable max-len */
takeLatest(EntryActionTypes.PROJECT_CREATE, ({ payload: { data } }) => createProjectService(data)),
takeLatest(EntryActionTypes.CURRENT_PROJECT_UPDATE, ({ payload: { data } }) => updateCurrentProjectService(data)),
/* eslint-enable max-len */
takeLatest(EntryActionTypes.CURRENT_PROJECT_DELETE, () => deleteCurrentProjectService()),
]);
}

View File

@@ -0,0 +1,8 @@
import { takeEvery } from 'redux-saga/effects';
import { LOCATION_CHANGE } from 'connected-react-router';
import { locationChangedService } from '../services';
export default function* () {
yield takeEvery(LOCATION_CHANGE, () => locationChangedService());
}

View File

@@ -0,0 +1,295 @@
import { eventChannel } from 'redux-saga';
import { call, cancelled, take } from 'redux-saga/effects';
import {
createActionReceivedService,
createBoardReceivedService,
createCardLabelReceivedService,
createCardMembershipReceivedService,
createCardReceivedService,
createLabelReceivedService,
createListReceivedService,
createNotificationReceivedService,
createProjectMembershipReceivedService,
createProjectReceivedService,
createTaskReceivedService,
createUserReceivedService,
deleteActionReceivedService,
deleteCardLabelReceivedService,
deleteCardMembershipReceivedService,
deleteCardReceivedService,
deleteBoardReceivedService,
deleteLabelReceivedService,
deleteListReceivedService,
deleteNotificationReceivedService,
deleteProjectMembershipReceivedService,
deleteProjectReceivedService,
deleteTaskReceivedService,
deleteUserReceivedService,
socketDisconnectedService,
socketReconnectedService,
updateActionReceivedService,
updateBoardReceivedService,
updateCardReceivedService,
updateLabelReceivedService,
updateListReceivedService,
updateProjectReceivedService,
updateTaskReceivedService,
updateUserReceivedService,
} from '../services';
import api, { socket } from '../../../api';
const createSocketEventsChannel = () => eventChannel((emit) => {
const handleReconnect = () => {
emit([socketReconnectedService]);
};
const handleUserCreate = ({ item }) => {
emit([createUserReceivedService, item]);
};
const handleUserUpdate = ({ item }) => {
emit([updateUserReceivedService, item]);
};
const handleUserDelete = ({ item }) => {
emit([deleteUserReceivedService, item]);
};
const handleProjectCreate = ({ item, included: { users, projectMemberships, boards } }) => {
emit([createProjectReceivedService, item, users, projectMemberships, boards]);
};
const handleProjectUpdate = ({ item }) => {
emit([updateProjectReceivedService, item]);
};
const handleProjectDelete = ({ item }) => {
emit([deleteProjectReceivedService, item]);
};
const handleProjectMembershipCreate = ({ item, included: { users } }) => {
emit([createProjectMembershipReceivedService, item, users[0]]);
};
const handleProjectMembershipDelete = ({ item }) => {
emit([deleteProjectMembershipReceivedService, item]);
};
const handleBoardCreate = ({ item, included: { lists, labels } }) => {
emit([createBoardReceivedService, item, lists, labels]);
};
const handleBoardUpdate = ({ item }) => {
emit([updateBoardReceivedService, item]);
};
const handleBoardDelete = ({ item }) => {
emit([deleteBoardReceivedService, item]);
};
const handleListCreate = ({ item }) => {
emit([createListReceivedService, item]);
};
const handleListUpdate = ({ item }) => {
emit([updateListReceivedService, item]);
};
const handleListDelete = ({ item }) => {
emit([deleteListReceivedService, item]);
};
const handleLabelCreate = ({ item }) => {
emit([createLabelReceivedService, item]);
};
const handleLabelUpdate = ({ item }) => {
emit([updateLabelReceivedService, item]);
};
const handleLabelDelete = ({ item }) => {
emit([deleteLabelReceivedService, item]);
};
const handleCardCreate = api.makeHandleCardCreate(({ item }) => {
emit([createCardReceivedService, item]);
});
const handleCardUpdate = api.makeHandleCardUpdate(({ item }) => {
emit([updateCardReceivedService, item]);
});
const handleCardDelete = api.makeHandleCardDelete(({ item }) => {
emit([deleteCardReceivedService, item]);
});
const handleCardMembershipCreate = ({ item }) => {
emit([createCardMembershipReceivedService, item]);
};
const handleCardMembershipDelete = ({ item }) => {
emit([deleteCardMembershipReceivedService, item]);
};
const handleCardLabelCreate = ({ item }) => {
emit([createCardLabelReceivedService, item]);
};
const handleCardLabelDelete = ({ item }) => {
emit([deleteCardLabelReceivedService, item]);
};
const handleTaskCreate = ({ item }) => {
emit([createTaskReceivedService, item]);
};
const handleTaskUpdate = ({ item }) => {
emit([updateTaskReceivedService, item]);
};
const handleTaskDelete = ({ item }) => {
emit([deleteTaskReceivedService, item]);
};
const handleActionCreate = api.makeHandleActionCreate(({ item }) => {
emit([createActionReceivedService, item]);
});
const handleActionUpdate = api.makeHandleActionUpdate(({ item }) => {
emit([updateActionReceivedService, item]);
});
const handleActionDelete = api.makeHandleActionDelete(({ item }) => {
emit([deleteActionReceivedService, item]);
});
const handleNotificationCreate = api.makeHandleNotificationCreate(
({ item, included: { users, cards, actions } }) => {
emit([createNotificationReceivedService, item, users[0], cards[0], actions[0]]);
},
);
const handleNotificationDelete = ({ item }) => {
emit([deleteNotificationReceivedService, item]);
};
const handleDisconnect = () => {
socket.off('disconnect', handleDisconnect);
emit([socketDisconnectedService]);
socket.on('reconnect', handleReconnect);
};
socket.on('disconnect', handleDisconnect);
socket.on('userCreate', handleUserCreate);
socket.on('userUpdate', handleUserUpdate);
socket.on('userDelete', handleUserDelete);
socket.on('projectCreate', handleProjectCreate);
socket.on('projectUpdate', handleProjectUpdate);
socket.on('projectDelete', handleProjectDelete);
socket.on('projectMembershipCreate', handleProjectMembershipCreate);
socket.on('projectMembershipDelete', handleProjectMembershipDelete);
socket.on('boardCreate', handleBoardCreate);
socket.on('boardUpdate', handleBoardUpdate);
socket.on('boardDelete', handleBoardDelete);
socket.on('listCreate', handleListCreate);
socket.on('listUpdate', handleListUpdate);
socket.on('listDelete', handleListDelete);
socket.on('labelCreate', handleLabelCreate);
socket.on('labelUpdate', handleLabelUpdate);
socket.on('labelDelete', handleLabelDelete);
socket.on('cardCreate', handleCardCreate);
socket.on('cardUpdate', handleCardUpdate);
socket.on('cardDelete', handleCardDelete);
socket.on('cardMembershipCreate', handleCardMembershipCreate);
socket.on('cardMembershipDelete', handleCardMembershipDelete);
socket.on('cardLabelCreate', handleCardLabelCreate);
socket.on('cardLabelDelete', handleCardLabelDelete);
socket.on('taskCreate', handleTaskCreate);
socket.on('taskUpdate', handleTaskUpdate);
socket.on('taskDelete', handleTaskDelete);
socket.on('actionCreate', handleActionCreate);
socket.on('actionUpdate', handleActionUpdate);
socket.on('actionDelete', handleActionDelete);
socket.on('notificationCreate', handleNotificationCreate);
socket.on('notificationUpdate', handleNotificationDelete);
return () => {
socket.off('disconnect', handleDisconnect);
socket.off('reconnect', handleReconnect);
socket.off('userCreate', handleUserCreate);
socket.off('userUpdate', handleUserUpdate);
socket.off('userDelete', handleUserDelete);
socket.off('projectCreate', handleProjectCreate);
socket.off('projectUpdate', handleProjectUpdate);
socket.off('projectDelete', handleProjectDelete);
socket.off('projectMembershipCreate', handleProjectMembershipCreate);
socket.off('projectMembershipDelete', handleProjectMembershipDelete);
socket.off('boardCreate', handleBoardCreate);
socket.off('boardUpdate', handleBoardUpdate);
socket.off('boardDelete', handleBoardDelete);
socket.off('listCreate', handleListCreate);
socket.off('listUpdate', handleListUpdate);
socket.off('listDelete', handleListDelete);
socket.off('labelCreate', handleLabelCreate);
socket.off('labelUpdate', handleLabelUpdate);
socket.off('labelDelete', handleLabelDelete);
socket.off('cardCreate', handleCardCreate);
socket.off('cardUpdate', handleCardUpdate);
socket.off('cardDelete', handleCardDelete);
socket.off('cardMembershipCreate', handleCardMembershipCreate);
socket.off('cardMembershipDelete', handleCardMembershipDelete);
socket.off('cardLabelCreate', handleCardLabelCreate);
socket.off('cardLabelDelete', handleCardLabelDelete);
socket.off('taskCreate', handleTaskCreate);
socket.off('taskUpdate', handleTaskUpdate);
socket.off('taskDelete', handleTaskDelete);
socket.off('actionCreate', handleActionCreate);
socket.off('actionUpdate', handleActionUpdate);
socket.off('actionDelete', handleActionDelete);
socket.off('notificationCreate', handleNotificationCreate);
socket.off('notificationUpdate', handleNotificationDelete);
};
});
export default function* () {
const socketEventsChannel = yield call(createSocketEventsChannel);
try {
while (true) {
const args = yield take(socketEventsChannel);
yield call(...args);
}
} finally {
if (yield cancelled()) {
socketEventsChannel.close();
}
}
}

View File

@@ -0,0 +1,14 @@
import { all, takeLatest } from 'redux-saga/effects';
import { createTaskInCurrentCardService, deleteTaskService, updateTaskService } from '../services';
import EntryActionTypes from '../../../constants/EntryActionTypes';
export default function* () {
yield all([
/* eslint-disable max-len */
takeLatest(EntryActionTypes.TASK_IN_CURRENT_CARD_CREATE, ({ payload: { data } }) => createTaskInCurrentCardService(data)),
takeLatest(EntryActionTypes.TASK_UPDATE, ({ payload: { id, data } }) => updateTaskService(id, data)),
/* eslint-enable max-len */
takeLatest(EntryActionTypes.TASK_DELETE, ({ payload: { id } }) => deleteTaskService(id)),
]);
}

View File

@@ -0,0 +1,38 @@
import { all, takeLatest } from 'redux-saga/effects';
import {
addUserToCardService,
addUserToCurrentCardService,
addUserToFilterInCurrentBoardService,
clearUserCreationErrorService,
createUserService,
deleteUserService,
removeUserFromCardService,
removeUserFromCurrentCardService,
removeUserFromFilterInCurrentBoardService,
updateUserService,
updateCurrentUserService,
uploadCurrentUserAvatarService,
} from '../services';
import EntryActionTypes from '../../../constants/EntryActionTypes';
export default function* () {
yield all([
takeLatest(EntryActionTypes.USER_CREATE, ({ payload: { data } }) => createUserService(data)),
takeLatest(EntryActionTypes.USER_CREATION_ERROR_CLEAR, () => clearUserCreationErrorService()),
/* eslint-disable max-len */
takeLatest(EntryActionTypes.USER_UPDATE, ({ payload: { id, data } }) => updateUserService(id, data)),
takeLatest(EntryActionTypes.CURRENT_USER_UPDATE, ({ payload: { data } }) => updateCurrentUserService(data)),
takeLatest(EntryActionTypes.CURRENT_USER_AVATAR_UPLOAD, ({ payload: { file } }) => uploadCurrentUserAvatarService(file)),
/* eslint-enable max-len */
takeLatest(EntryActionTypes.USER_DELETE, ({ payload: { id } }) => deleteUserService(id)),
/* eslint-disable max-len */
takeLatest(EntryActionTypes.USER_TO_CARD_ADD, ({ payload: { id, cardId } }) => addUserToCardService(id, cardId)),
takeLatest(EntryActionTypes.USER_TO_CURRENT_CARD_ADD, ({ payload: { id } }) => addUserToCurrentCardService(id)),
takeLatest(EntryActionTypes.USER_FROM_CARD_REMOVE, ({ payload: { id, cardId } }) => removeUserFromCardService(id, cardId)),
takeLatest(EntryActionTypes.USER_FROM_CURRENT_CARD_REMOVE, ({ payload: { id } }) => removeUserFromCurrentCardService(id)),
takeLatest(EntryActionTypes.USER_TO_FILTER_IN_CURRENT_BOARD_ADD, ({ payload: { id } }) => addUserToFilterInCurrentBoardService(id)),
takeLatest(EntryActionTypes.USER_FROM_FILTER_IN_CURRENT_BOARD_REMOVE, ({ payload: { id } }) => removeUserFromFilterInCurrentBoardService(id)),
/* eslint-enable max-len */
]);
}

15
client/src/sagas/index.js Executable file
View File

@@ -0,0 +1,15 @@
import { call, select } from 'redux-saga/effects';
import loginSaga from './login';
import appSaga from './app';
import { accessTokenSelector } from '../selectors';
export default function* () {
const accessToken = yield select(accessTokenSelector);
if (!accessToken) {
yield call(loginSaga);
}
yield call(appSaga);
}

21
client/src/sagas/login/index.js Executable file
View File

@@ -0,0 +1,21 @@
import {
all, call, cancel, fork, take,
} from 'redux-saga/effects';
import watchers from './watchers';
import { goToRootService } from './services';
import { setAccessToken } from '../../utils/access-token-storage';
import ActionTypes from '../../constants/ActionTypes';
export default function* () {
const watcherTasks = yield all(watchers.map((watcher) => fork(watcher)));
const {
payload: { accessToken },
} = yield take(ActionTypes.AUTHENTICATE_SUCCEEDED);
yield cancel(watcherTasks);
yield call(setAccessToken, accessToken);
yield call(goToRootService);
}

View File

@@ -0,0 +1 @@
export * from './login';

View File

@@ -0,0 +1,29 @@
import { call, put } from 'redux-saga/effects';
import { authenticateFailed, authenticateRequested, authenticateSucceeded } from '../../../actions';
import api from '../../../api';
// eslint-disable-next-line import/prefer-default-export
export function* authenticateRequest(data) {
yield put(authenticateRequested(data));
try {
const { item } = yield call(api.createAccessToken, data);
const action = authenticateSucceeded(item);
yield put(action);
return {
success: true,
payload: action.payload,
};
} catch (error) {
const action = authenticateFailed(error);
yield put(action);
return {
success: false,
payload: action.payload,
};
}
}

View File

@@ -0,0 +1,2 @@
export * from './router';
export * from './login';

View File

@@ -0,0 +1,14 @@
import { call, put } from 'redux-saga/effects';
import { authenticateRequest } from '../requests';
import { authenticate, clearAuthenticationError } from '../../../actions';
export function* authenticateService(data) {
yield put(authenticate(data));
yield call(authenticateRequest, data);
}
export function* clearAuthenticationErrorService() {
yield put(clearAuthenticationError());
}

View File

@@ -0,0 +1,32 @@
import { call, put, select } from 'redux-saga/effects';
import { push } from 'connected-react-router';
import { pathsMatchSelector } from '../../../selectors';
import Paths from '../../../constants/Paths';
export function* goToLoginService() {
yield put(push(Paths.LOGIN));
}
export function* goToRootService() {
yield put(push(Paths.ROOT));
}
export function* locationChangedService() {
const pathsMatch = yield select(pathsMatchSelector);
if (!pathsMatch) {
return;
}
switch (pathsMatch.path) {
case Paths.ROOT:
case Paths.PROJECTS:
case Paths.BOARDS:
case Paths.CARDS:
yield call(goToLoginService);
break;
default:
}
}

View File

@@ -0,0 +1,4 @@
import router from './router';
import login from './login';
export default [router, login];

View File

@@ -0,0 +1,12 @@
import { all, takeLatest } from 'redux-saga/effects';
import { authenticateService, clearAuthenticationErrorService } from '../services';
import EntryActionTypes from '../../../constants/EntryActionTypes';
export default function* () {
yield all([
takeLatest(EntryActionTypes.AUTHENTICATE, ({ payload: { data } }) => authenticateService(data)),
// eslint-disable-next-line max-len
takeLatest(EntryActionTypes.AUTHENTICATION_ERROR_CLEAR, () => clearAuthenticationErrorService()),
]);
}

View File

@@ -0,0 +1,8 @@
import { takeEvery } from 'redux-saga/effects';
import { LOCATION_CHANGE } from 'connected-react-router';
import { locationChangedService } from '../services';
export default function* () {
yield takeEvery(LOCATION_CHANGE, () => locationChangedService());
}