1
0
Fork 0
mirror of https://github.com/plankanban/planka.git synced 2025-07-22 22:59:44 +02:00

Project managers, board members, auto-update after reconnection, refactoring

This commit is contained in:
Maksim Eltyshev 2021-06-24 01:05:22 +05:00
parent 7956503a46
commit fe91b5241e
478 changed files with 21226 additions and 19495 deletions

View file

@ -29,23 +29,53 @@ export default class extends Model {
static reducer({ type, payload }, Action) {
switch (type) {
case ActionTypes.ACTIONS_FETCH_SUCCEEDED:
case ActionTypes.SOCKET_RECONNECT_HANDLE:
Action.all().delete();
payload.actions.forEach((action) => {
Action.upsert({
...action,
isInCard: false,
});
});
break;
case ActionTypes.CORE_INITIALIZE:
payload.actions.forEach((action) => {
Action.upsert({
...action,
isInCard: false,
});
});
break;
case ActionTypes.ACTIONS_FETCH__SUCCESS:
case ActionTypes.NOTIFICATION_CREATE_HANDLE:
payload.actions.forEach((action) => {
Action.upsert(action);
});
break;
case ActionTypes.ACTION_CREATE_RECEIVED:
case ActionTypes.ACTION_CREATE_HANDLE:
case ActionTypes.ACTION_UPDATE_HANDLE:
case ActionTypes.COMMENT_ACTION_CREATE:
case ActionTypes.COMMENT_ACTION_UPDATE__SUCCESS:
Action.upsert(payload.action);
break;
case ActionTypes.ACTION_UPDATE_RECEIVED:
Action.withId(payload.action.id).update(payload.action);
case ActionTypes.ACTION_DELETE_HANDLE:
case ActionTypes.COMMENT_ACTION_DELETE__SUCCESS: {
const actionModel = Action.withId(payload.action.id);
if (actionModel) {
actionModel.delete();
}
break;
case ActionTypes.ACTION_DELETE_RECEIVED:
Action.withId(payload.action.id).delete();
}
case ActionTypes.COMMENT_ACTION_CREATE__SUCCESS:
Action.withId(payload.localId).delete();
Action.upsert(payload.action);
break;
case ActionTypes.COMMENT_ACTION_UPDATE:
@ -58,30 +88,6 @@ export default class extends Model {
Action.withId(payload.id).delete();
break;
case ActionTypes.COMMENT_ACTION_CREATE_SUCCEEDED:
Action.withId(payload.localId).delete();
Action.upsert(payload.action);
break;
case ActionTypes.NOTIFICATIONS_FETCH_SUCCEEDED:
payload.actions.forEach((action) => {
Action.upsert({
...action,
isInCard: false,
});
});
break;
case ActionTypes.NOTIFICATION_CREATE_RECEIVED: {
const actionModel = Action.withId(payload.action.id);
Action.upsert({
...payload.action,
isInCard: actionModel ? actionModel.isInCard : false,
});
break;
}
default:
}
}

View file

@ -19,16 +19,53 @@ export default class extends Model {
static reducer({ type, payload }, Attachment) {
switch (type) {
case ActionTypes.BOARD_FETCH_SUCCEEDED:
case ActionTypes.CARD_CREATE_SUCCEEDED:
case ActionTypes.CARD_CREATE_RECEIVED:
case ActionTypes.LOCATION_CHANGE_HANDLE:
case ActionTypes.CORE_INITIALIZE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
if (payload.attachments) {
payload.attachments.forEach((attachment) => {
Attachment.upsert(attachment);
});
}
break;
case ActionTypes.SOCKET_RECONNECT_HANDLE:
if (payload.attachments) {
// FIXME: bug with oneToOne relation in Redux-ORM
const attachmentIds = payload.attachments.map((attachment) => attachment.id);
Attachment.all()
.toModelArray()
.forEach((attachmentModel) => {
if (!attachmentIds.includes(attachmentModel.id)) {
attachmentModel.delete();
}
});
payload.attachments.forEach((attachment) => {
Attachment.upsert(attachment);
});
} else {
Attachment.all().delete();
}
break;
case ActionTypes.BOARD_FETCH__SUCCESS:
payload.attachments.forEach((attachment) => {
Attachment.upsert(attachment);
});
break;
case ActionTypes.ATTACHMENT_CREATE:
case ActionTypes.ATTACHMENT_CREATE_RECEIVED:
case ActionTypes.ATTACHMENT_CREATE_HANDLE:
case ActionTypes.ATTACHMENT_UPDATE__SUCCESS:
case ActionTypes.ATTACHMENT_UPDATE_HANDLE:
Attachment.upsert(payload.attachment);
break;
case ActionTypes.ATTACHMENT_CREATE__SUCCESS:
Attachment.withId(payload.localId).delete();
Attachment.upsert(payload.attachment);
break;
@ -40,19 +77,16 @@ export default class extends Model {
Attachment.withId(payload.id).delete();
break;
case ActionTypes.ATTACHMENT_CREATE_SUCCEEDED:
Attachment.withId(payload.localId).delete();
Attachment.upsert(payload.attachment);
break;
case ActionTypes.ATTACHMENT_UPDATE_RECEIVED:
Attachment.withId(payload.attachment.id).update(payload.attachment);
break;
case ActionTypes.ATTACHMENT_DELETE_RECEIVED:
Attachment.withId(payload.attachment.id).delete();
case ActionTypes.ATTACHMENT_DELETE__SUCCESS:
case ActionTypes.ATTACHMENT_DELETE_HANDLE: {
const attachmentModel = Attachment.withId(payload.attachment.id);
if (attachmentModel) {
attachmentModel.delete();
}
break;
}
default:
}
}

View file

@ -18,12 +18,74 @@ export default class extends Model {
as: 'project',
relatedName: 'boards',
}),
memberUsers: many({
to: 'User',
through: 'BoardMembership',
relatedName: 'boards',
}),
filterUsers: many('User', 'filterBoards'),
filterLabels: many('Label', 'filterBoards'),
};
static reducer({ type, payload }, Board) {
switch (type) {
case ActionTypes.LOCATION_CHANGE_HANDLE:
case ActionTypes.BOARD_FETCH__SUCCESS:
Board.upsert({
...payload.board,
isFetching: false,
});
break;
case ActionTypes.LOCATION_CHANGE_HANDLE__BOARD_FETCH:
case ActionTypes.BOARD_FETCH:
Board.withId(payload.id).update({
isFetching: true,
});
break;
case ActionTypes.SOCKET_RECONNECT_HANDLE:
Board.all().delete();
if (payload.board) {
Board.upsert({
...payload.board,
isFetching: false,
});
}
payload.boards.forEach((board) => {
Board.upsert(board);
});
break;
case ActionTypes.SOCKET_RECONNECT_HANDLE__CORE_FETCH:
Board.all()
.toModelArray()
.forEach((boardModel) => {
if (boardModel.id !== payload.currentBoardId) {
boardModel.update({
isFetching: null,
});
boardModel.deleteRelated(payload.currentUserId);
}
});
break;
case ActionTypes.CORE_INITIALIZE:
if (payload.board) {
Board.upsert({
...payload.board,
isFetching: false,
});
}
payload.boards.forEach((board) => {
Board.upsert(board);
});
break;
case ActionTypes.USER_TO_BOARD_FILTER_ADD:
Board.withId(payload.boardId).filterUsers.add(payload.id);
@ -32,18 +94,48 @@ export default class extends Model {
Board.withId(payload.boardId).filterUsers.remove(payload.id);
break;
case ActionTypes.PROJECTS_FETCH_SUCCEEDED:
case ActionTypes.PROJECT_CREATE_SUCCEEDED:
case ActionTypes.PROJECT_CREATE_RECEIVED:
case ActionTypes.PROJECT_CREATE_HANDLE:
payload.boards.forEach((board) => {
Board.upsert(board);
});
break;
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
if (payload.boards) {
payload.boards.forEach((board) => {
Board.upsert({
...board,
...(payload.board &&
payload.board.id === board.id && {
isFetching: false,
}),
});
});
}
break;
case ActionTypes.BOARD_CREATE:
case ActionTypes.BOARD_CREATE_RECEIVED:
case ActionTypes.BOARD_CREATE_HANDLE:
case ActionTypes.BOARD_UPDATE__SUCCESS:
case ActionTypes.BOARD_UPDATE_HANDLE:
Board.upsert(payload.board);
break;
case ActionTypes.BOARD_CREATE__SUCCESS:
Board.withId(payload.localId).delete();
Board.upsert({
...payload.board,
isFetching: false,
});
break;
case ActionTypes.BOARD_FETCH__FAILURE:
Board.withId(payload.id).update({
isFetching: null,
});
break;
case ActionTypes.BOARD_UPDATE:
Board.withId(payload.id).update(payload.data);
@ -53,35 +145,16 @@ export default class extends Model {
Board.withId(payload.id).deleteWithRelated();
break;
case ActionTypes.BOARD_CREATE_SUCCEEDED:
Board.withId(payload.localId).delete();
Board.upsert({
...payload.board,
isFetching: false,
});
break;
case ActionTypes.BOARD_FETCH_REQUESTED:
Board.withId(payload.id).update({
isFetching: true,
});
break;
case ActionTypes.BOARD_FETCH_SUCCEEDED:
Board.withId(payload.board.id).update({
...payload.board,
isFetching: false,
});
break;
case ActionTypes.BOARD_UPDATE_RECEIVED:
Board.withId(payload.board.id).update(payload.board);
break;
case ActionTypes.BOARD_DELETE_RECEIVED:
Board.withId(payload.board.id).deleteWithRelated();
case ActionTypes.BOARD_DELETE__SUCCESS:
case ActionTypes.BOARD_DELETE_HANDLE: {
const boardModel = Board.withId(payload.board.id);
if (boardModel) {
boardModel.deleteWithRelated();
}
break;
}
case ActionTypes.LABEL_TO_BOARD_FILTER_ADD:
Board.withId(payload.boardId).filterLabels.add(payload.id);
@ -94,15 +167,38 @@ export default class extends Model {
}
}
getOrderedMembershipsQuerySet() {
return this.memberships.orderBy('id');
}
getOrderedListsQuerySet() {
return this.lists.orderBy('position');
}
deleteWithRelated() {
this.cards.toModelArray().forEach((cardModel) => {
cardModel.deleteWithRelated();
hasMemberUser(userId) {
return this.memberships
.filter({
userId,
})
.exists();
}
deleteRelated(exceptMemberUserId) {
this.memberships.toModelArray().forEach((boardMembershipModel) => {
if (boardMembershipModel.userId !== exceptMemberUserId) {
boardMembershipModel.deleteWithRelated();
}
});
this.labels.delete();
this.lists.toModelArray().forEach((listModel) => {
listModel.deleteWithRelated();
});
}
deleteWithRelated() {
this.deleteRelated();
this.delete();
}
}

View file

@ -0,0 +1,103 @@
import { Model, attr, fk } from 'redux-orm';
import ActionTypes from '../constants/ActionTypes';
export default class extends Model {
static modelName = 'BoardMembership';
static fields = {
id: attr(),
boardId: fk({
to: 'Board',
as: 'board',
relatedName: 'memberships',
}),
userId: fk({
to: 'User',
as: 'user',
relatedName: 'boardMemberships',
}),
};
static reducer({ type, payload }, BoardMembership) {
switch (type) {
case ActionTypes.LOCATION_CHANGE_HANDLE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
if (payload.boardMemberships) {
payload.boardMemberships.forEach((boardMembership) => {
BoardMembership.upsert(boardMembership);
});
}
break;
case ActionTypes.SOCKET_RECONNECT_HANDLE:
BoardMembership.all().delete();
payload.boardMemberships.forEach((boardMembership) => {
BoardMembership.upsert(boardMembership);
});
break;
case ActionTypes.CORE_INITIALIZE:
case ActionTypes.PROJECT_CREATE_HANDLE:
case ActionTypes.BOARD_CREATE__SUCCESS:
case ActionTypes.BOARD_FETCH__SUCCESS:
payload.boardMemberships.forEach((boardMembership) => {
BoardMembership.upsert(boardMembership);
});
break;
case ActionTypes.BOARD_MEMBERSHIP_CREATE:
BoardMembership.upsert(payload.boardMembership);
break;
case ActionTypes.BOARD_MEMBERSHIP_CREATE__SUCCESS:
BoardMembership.withId(payload.localId).delete();
BoardMembership.upsert(payload.boardMembership);
break;
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
BoardMembership.upsert(payload.boardMembership);
if (payload.boardMemberships) {
payload.boardMemberships.forEach((boardMembership) => {
BoardMembership.upsert(boardMembership);
});
}
break;
case ActionTypes.BOARD_MEMBERSHIP_DELETE:
BoardMembership.withId(payload.id).deleteWithRelated();
break;
case ActionTypes.BOARD_MEMBERSHIP_DELETE__SUCCESS:
case ActionTypes.BOARD_MEMBERSHIP_DELETE_HANDLE: {
const boardMembershipModel = BoardMembership.withId(payload.boardMembership.id);
if (boardMembershipModel) {
boardMembershipModel.deleteWithRelated();
}
break;
}
default:
}
}
deleteRelated() {
this.board.cards.toModelArray().forEach((cardModel) => {
try {
cardModel.users.remove(this.userId);
} catch {} // eslint-disable-line no-empty
});
try {
this.board.filterUsers.remove(this.userId);
} catch {} // eslint-disable-line no-empty
}
deleteWithRelated() {
this.deleteRelated();
this.delete();
}
}

View file

@ -43,6 +43,51 @@ export default class extends Model {
static reducer({ type, payload }, Card) {
switch (type) {
case ActionTypes.LOCATION_CHANGE_HANDLE:
case ActionTypes.CORE_INITIALIZE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
if (payload.cards) {
payload.cards.forEach((card) => {
Card.upsert(card);
});
}
if (payload.cardMemberships) {
payload.cardMemberships.forEach(({ cardId, userId }) => {
Card.withId(cardId).users.add(userId);
});
}
if (payload.cardLabels) {
payload.cardLabels.forEach(({ cardId, labelId }) => {
Card.withId(cardId).labels.add(labelId);
});
}
break;
case ActionTypes.SOCKET_RECONNECT_HANDLE:
Card.all().delete();
if (payload.cards) {
payload.cards.forEach((card) => {
Card.upsert(card);
});
}
if (payload.cardMemberships) {
payload.cardMemberships.forEach(({ cardId, userId }) => {
Card.withId(cardId).users.add(userId);
});
}
if (payload.cardLabels) {
payload.cardLabels.forEach(({ cardId, labelId }) => {
Card.withId(cardId).labels.add(labelId);
});
}
break;
case ActionTypes.USER_TO_CARD_ADD: {
const cardModel = Card.withId(payload.cardId);
cardModel.users.add(payload.id);
@ -53,11 +98,25 @@ export default class extends Model {
break;
}
case ActionTypes.USER_TO_CARD_ADD__SUCCESS:
case ActionTypes.USER_TO_CARD_ADD_HANDLE:
try {
Card.withId(payload.cardMembership.cardId).users.add(payload.cardMembership.userId);
} catch {} // eslint-disable-line no-empty
break;
case ActionTypes.USER_FROM_CARD_REMOVE:
Card.withId(payload.cardId).users.remove(payload.id);
break;
case ActionTypes.BOARD_FETCH_SUCCEEDED:
case ActionTypes.USER_FROM_CARD_REMOVE__SUCCESS:
case ActionTypes.USER_FROM_CARD_REMOVE_HANDLE:
try {
Card.withId(payload.cardMembership.cardId).users.remove(payload.cardMembership.userId);
} catch {} // eslint-disable-line no-empty
break;
case ActionTypes.BOARD_FETCH__SUCCESS:
payload.cards.forEach((card) => {
Card.upsert(card);
});
@ -74,14 +133,34 @@ export default class extends Model {
case ActionTypes.LABEL_TO_CARD_ADD:
Card.withId(payload.cardId).labels.add(payload.id);
break;
case ActionTypes.LABEL_TO_CARD_ADD__SUCCESS:
case ActionTypes.LABEL_TO_CARD_ADD_HANDLE:
try {
Card.withId(payload.cardLabel.cardId).labels.add(payload.cardLabel.labelId);
} catch {} // eslint-disable-line no-empty
break;
case ActionTypes.LABEL_FROM_CARD_REMOVE:
Card.withId(payload.cardId).labels.remove(payload.id);
break;
case ActionTypes.LABEL_FROM_CARD_REMOVE__SUCCESS:
case ActionTypes.LABEL_FROM_CARD_REMOVE_HANDLE:
try {
Card.withId(payload.cardLabel.cardId).labels.remove(payload.cardLabel.labelId);
} catch {} // eslint-disable-line no-empty
break;
case ActionTypes.CARD_CREATE:
case ActionTypes.CARD_FETCH_SUCCEEDED:
case ActionTypes.NOTIFICATION_CREATE_RECEIVED:
case ActionTypes.CARD_CREATE_HANDLE:
case ActionTypes.CARD_UPDATE__SUCCESS:
case ActionTypes.CARD_UPDATE_HANDLE:
Card.upsert(payload.card);
break;
case ActionTypes.CARD_CREATE__SUCCESS:
Card.withId(payload.localId).delete();
Card.upsert(payload.card);
break;
@ -90,77 +169,33 @@ export default class extends Model {
break;
case ActionTypes.CARD_DELETE:
Card.withId(payload.id).deleteWithRelated();
Card.withId(payload.id).delete();
break;
case ActionTypes.CARD_CREATE_SUCCEEDED:
Card.withId(payload.localId).delete();
Card.upsert(payload.card);
case ActionTypes.CARD_DELETE__SUCCESS:
case ActionTypes.CARD_DELETE_HANDLE: {
const cardModel = Card.withId(payload.card.id);
payload.cardMemberships.forEach(({ cardId, userId }) => {
Card.withId(cardId).users.add(userId);
});
payload.cardLabels.forEach(({ cardId, labelId }) => {
Card.withId(cardId).labels.add(labelId);
});
break;
case ActionTypes.CARD_CREATE_RECEIVED:
Card.upsert(payload.card);
payload.cardMemberships.forEach(({ cardId, userId }) => {
Card.withId(cardId).users.add(userId);
});
payload.cardLabels.forEach(({ cardId, labelId }) => {
Card.withId(cardId).labels.add(labelId);
});
break;
case ActionTypes.CARD_UPDATE_RECEIVED: {
const card = Card.withId(payload.card.id);
if (card) {
card.update(payload.card);
if (cardModel) {
cardModel.deleteWithRelated();
}
break;
}
case ActionTypes.CARD_DELETE_RECEIVED:
Card.withId(payload.card.id).deleteWithRelated();
break;
case ActionTypes.CARD_MEMBERSHIP_CREATE_RECEIVED:
Card.withId(payload.cardMembership.cardId).users.add(payload.cardMembership.userId);
break;
case ActionTypes.CARD_MEMBERSHIP_DELETE_RECEIVED:
Card.withId(payload.cardMembership.cardId).users.remove(payload.cardMembership.userId);
break;
case ActionTypes.CARD_LABEL_CREATE_RECEIVED:
Card.withId(payload.cardLabel.cardId).labels.add(payload.cardLabel.labelId);
break;
case ActionTypes.CARD_LABEL_DELETE_RECEIVED:
Card.withId(payload.cardLabel.cardId).labels.remove(payload.cardLabel.labelId);
break;
case ActionTypes.ACTIONS_FETCH_REQUESTED:
case ActionTypes.ACTIONS_FETCH:
Card.withId(payload.cardId).update({
isActionsFetching: true,
});
break;
case ActionTypes.ACTIONS_FETCH_SUCCEEDED:
case ActionTypes.ACTIONS_FETCH__SUCCESS:
Card.withId(payload.cardId).update({
isActionsFetching: false,
isAllActionsFetched: payload.actions.length < Config.ACTIONS_LIMIT,
});
break;
case ActionTypes.NOTIFICATIONS_FETCH_SUCCEEDED:
case ActionTypes.NOTIFICATION_CREATE_HANDLE:
payload.cards.forEach((card) => {
Card.upsert(card);
});
@ -188,11 +223,14 @@ export default class extends Model {
});
}
deleteWithRelated() {
deleteRelated() {
this.tasks.delete();
this.attachments.delete();
this.actions.delete();
}
deleteWithRelated() {
this.deleteRelated();
this.delete();
}
}

View file

@ -18,16 +18,42 @@ export default class extends Model {
static reducer({ type, payload }, Label) {
switch (type) {
case ActionTypes.BOARD_CREATE_SUCCEEDED:
case ActionTypes.BOARD_CREATE_RECEIVED:
case ActionTypes.BOARD_FETCH_SUCCEEDED:
case ActionTypes.LOCATION_CHANGE_HANDLE:
case ActionTypes.CORE_INITIALIZE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
if (payload.labels) {
payload.labels.forEach((label) => {
Label.upsert(label);
});
}
break;
case ActionTypes.SOCKET_RECONNECT_HANDLE:
Label.all().delete();
if (payload.labels) {
payload.labels.forEach((label) => {
Label.upsert(label);
});
}
break;
case ActionTypes.BOARD_FETCH__SUCCESS:
payload.labels.forEach((label) => {
Label.upsert(label);
});
break;
case ActionTypes.LABEL_CREATE:
case ActionTypes.LABEL_CREATE_RECEIVED:
case ActionTypes.LABEL_CREATE_HANDLE:
case ActionTypes.LABEL_UPDATE__SUCCESS:
case ActionTypes.LABEL_UPDATE_HANDLE:
Label.upsert(payload.label);
break;
case ActionTypes.LABEL_CREATE__SUCCESS:
Label.withId(payload.localId).delete();
Label.upsert(payload.label);
break;
@ -39,19 +65,16 @@ export default class extends Model {
Label.withId(payload.id).delete();
break;
case ActionTypes.LABEL_CREATE_SUCCEEDED:
Label.withId(payload.localId).delete();
Label.upsert(payload.label);
break;
case ActionTypes.LABEL_UPDATE_RECEIVED:
Label.withId(payload.label.id).update(payload.label);
break;
case ActionTypes.LABEL_DELETE_RECEIVED:
Label.withId(payload.label.id).delete();
case ActionTypes.LABEL_DELETE__SUCCESS:
case ActionTypes.LABEL_DELETE_HANDLE: {
const labelModel = Label.withId(payload.label.id);
if (labelModel) {
labelModel.delete();
}
break;
}
default:
}
}

View file

@ -18,16 +18,42 @@ export default class extends Model {
static reducer({ type, payload }, List) {
switch (type) {
case ActionTypes.BOARD_CREATE_SUCCEEDED:
case ActionTypes.BOARD_CREATE_RECEIVED:
case ActionTypes.BOARD_FETCH_SUCCEEDED:
case ActionTypes.LOCATION_CHANGE_HANDLE:
case ActionTypes.CORE_INITIALIZE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
if (payload.lists) {
payload.lists.forEach((list) => {
List.upsert(list);
});
}
break;
case ActionTypes.SOCKET_RECONNECT_HANDLE:
List.all().delete();
if (payload.lists) {
payload.lists.forEach((list) => {
List.upsert(list);
});
}
break;
case ActionTypes.BOARD_FETCH__SUCCESS:
payload.lists.forEach((list) => {
List.upsert(list);
});
break;
case ActionTypes.LIST_CREATE:
case ActionTypes.LIST_CREATE_RECEIVED:
case ActionTypes.LIST_CREATE_HANDLE:
case ActionTypes.LIST_UPDATE__SUCCESS:
case ActionTypes.LIST_UPDATE_HANDLE:
List.upsert(payload.list);
break;
case ActionTypes.LIST_CREATE__SUCCESS:
List.withId(payload.localId).delete();
List.upsert(payload.list);
break;
@ -39,19 +65,16 @@ export default class extends Model {
List.withId(payload.id).deleteWithRelated();
break;
case ActionTypes.LIST_CREATE_SUCCEEDED:
List.withId(payload.localId).delete();
List.upsert(payload.list);
break;
case ActionTypes.LIST_UPDATE_RECEIVED:
List.withId(payload.list.id).update(payload.list);
break;
case ActionTypes.LIST_DELETE_RECEIVED:
List.withId(payload.list.id).deleteWithRelated();
case ActionTypes.LIST_DELETE__SUCCESS:
case ActionTypes.LIST_DELETE_HANDLE: {
const listModel = List.withId(payload.list.id);
if (listModel) {
listModel.deleteWithRelated();
}
break;
}
default:
}
}
@ -85,11 +108,14 @@ export default class extends Model {
return cardModels;
}
deleteWithRelated() {
deleteRelated() {
this.cards.toModelArray().forEach((cardModel) => {
cardModel.deleteWithRelated();
});
}
deleteWithRelated() {
this.deleteRelated();
this.delete();
}
}

View file

@ -29,27 +29,44 @@ export default class extends Model {
static reducer({ type, payload }, Notification) {
switch (type) {
case ActionTypes.NOTIFICATIONS_DELETE:
payload.ids.forEach((id) => {
Notification.withId(id).delete();
});
case ActionTypes.LOCATION_CHANGE_HANDLE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
if (payload.notifications) {
payload.notifications.forEach((notification) => {
Notification.withId(notification.id).deleteWithRelated();
});
}
break;
case ActionTypes.NOTIFICATIONS_FETCH_SUCCEEDED:
case ActionTypes.SOCKET_RECONNECT_HANDLE:
Notification.all().delete();
payload.notifications.forEach((notification) => {
Notification.upsert(notification);
});
break;
case ActionTypes.NOTIFICATION_CREATE_RECEIVED:
case ActionTypes.CORE_INITIALIZE:
payload.notifications.forEach((notification) => {
Notification.upsert(notification);
});
break;
case ActionTypes.NOTIFICATION_CREATE_HANDLE:
Notification.upsert(payload.notification);
break;
case ActionTypes.NOTIFICATION_DELETE_RECEIVED: {
case ActionTypes.NOTIFICATION_DELETE:
Notification.withId(payload.id).deleteWithRelated();
break;
case ActionTypes.NOTIFICATION_DELETE__SUCCESS:
case ActionTypes.NOTIFICATION_DELETE_HANDLE: {
const notificationModel = Notification.withId(payload.notification.id);
if (notificationModel) {
notificationModel.delete();
notificationModel.deleteWithRelated();
}
break;
@ -57,4 +74,15 @@ export default class extends Model {
default:
}
}
deleteRelated() {
if (this.action && !this.action.isInCard) {
this.action.delete();
}
}
deleteWithRelated() {
this.deleteRelated();
this.delete();
}
}

View file

@ -14,20 +14,44 @@ export default class extends Model {
isBackgroundImageUpdating: attr({
getDefault: () => false,
}),
users: many({
managerUsers: many({
to: 'User',
through: 'ProjectMembership',
through: 'ProjectManager',
relatedName: 'projects',
}),
};
static reducer({ type, payload }, Project) {
switch (type) {
case ActionTypes.PROJECTS_FETCH_SUCCEEDED:
case ActionTypes.LOCATION_CHANGE_HANDLE:
if (payload.projects) {
payload.projects.forEach((project) => {
Project.upsert(project);
});
}
break;
case ActionTypes.SOCKET_RECONNECT_HANDLE:
Project.all().delete();
payload.projects.forEach((project) => {
Project.upsert(project);
});
break;
case ActionTypes.CORE_INITIALIZE:
case ActionTypes.BOARD_FETCH__SUCCESS:
payload.projects.forEach((project) => {
Project.upsert(project);
});
break;
case ActionTypes.PROJECT_CREATE__SUCCESS:
case ActionTypes.PROJECT_CREATE_HANDLE:
case ActionTypes.PROJECT_UPDATE__SUCCESS:
case ActionTypes.PROJECT_UPDATE_HANDLE:
Project.upsert(payload.project);
break;
case ActionTypes.PROJECT_UPDATE: {
const project = Project.withId(payload.id);
@ -43,59 +67,118 @@ export default class extends Model {
break;
}
case ActionTypes.PROJECT_DELETE:
Project.withId(payload.id).deleteWithRelated();
break;
case ActionTypes.PROJECT_CREATE_SUCCEEDED:
case ActionTypes.PROJECT_CREATE_RECEIVED:
Project.upsert(payload.project);
break;
case ActionTypes.PROJECT_UPDATE_RECEIVED:
Project.withId(payload.project.id).update(payload.project);
break;
case ActionTypes.PROJECT_BACKGROUND_IMAGE_UPDATE_REQUESTED:
case ActionTypes.PROJECT_BACKGROUND_IMAGE_UPDATE:
Project.withId(payload.id).update({
isBackgroundImageUpdating: true,
});
break;
case ActionTypes.PROJECT_BACKGROUND_IMAGE_UPDATE_SUCCEEDED:
case ActionTypes.PROJECT_BACKGROUND_IMAGE_UPDATE__SUCCESS:
Project.withId(payload.project.id).update({
...payload.project,
isBackgroundImageUpdating: false,
});
break;
case ActionTypes.PROJECT_BACKGROUND_IMAGE_UPDATE_FAILED:
case ActionTypes.PROJECT_BACKGROUND_IMAGE_UPDATE__FAILURE:
Project.withId(payload.id).update({
isBackgroundImageUpdating: false,
});
break;
case ActionTypes.PROJECT_DELETE_RECEIVED:
Project.withId(payload.project.id).deleteWithRelated();
case ActionTypes.PROJECT_DELETE:
Project.withId(payload.id).deleteWithRelated();
break;
case ActionTypes.PROJECT_DELETE__SUCCESS:
case ActionTypes.PROJECT_DELETE_HANDLE: {
const projectModel = Project.withId(payload.project.id);
if (projectModel) {
projectModel.deleteWithRelated();
}
break;
}
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
if (payload.project) {
const projectModel = Project.withId(payload.project.id);
if (projectModel) {
projectModel.deleteWithRelated();
}
Project.upsert(payload.project);
}
break;
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE__PROJECT_FETCH:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE__PROJECT_FETCH: {
const projectModel = Project.withId(payload.id);
if (projectModel) {
projectModel.boards.toModelArray().forEach((boardModel) => {
if (boardModel.id !== payload.currentBoardId) {
boardModel.update({
isFetching: null,
});
boardModel.deleteRelated(payload.currentUserId);
}
});
}
break;
}
default:
}
}
getOrderedMembershipsQuerySet() {
return this.memberships.orderBy('id');
getOrderedManagersQuerySet() {
return this.managers.orderBy('id');
}
getOrderedBoardsQuerySet() {
return this.boards.orderBy('position');
}
deleteWithRelated() {
getOrderedMemberBoardsModelArray(userId) {
return this.getOrderedBoardsQuerySet()
.toModelArray()
.filter((boardModel) => boardModel.hasMemberUser(userId));
}
getOrderedAvailableBoardsModelArray(userId) {
if (this.hasManagerUser(userId)) {
return this.getOrderedBoardsQuerySet().toModelArray();
}
return this.getOrderedMemberBoardsModelArray(userId);
}
hasManagerUser(userId) {
return this.managers
.filter({
userId,
})
.exists();
}
hasMemberUserForAnyBoard(userId) {
return this.boards.toModelArray().some((boardModel) => boardModel.hasMemberUser(userId));
}
deleteRelated() {
this.managers.delete();
this.boards.toModelArray().forEach((boardModel) => {
boardModel.deleteWithRelated();
});
}
deleteWithRelated() {
this.deleteRelated();
this.delete();
}
}

View file

@ -0,0 +1,84 @@
import { Model, attr, fk } from 'redux-orm';
import ActionTypes from '../constants/ActionTypes';
export default class extends Model {
static modelName = 'ProjectManager';
static fields = {
id: attr(),
projectId: fk({
to: 'Project',
as: 'project',
relatedName: 'managers',
}),
userId: fk({
to: 'User',
as: 'user',
relatedName: 'projectManagers',
}),
};
static reducer({ type, payload }, ProjectManager) {
switch (type) {
case ActionTypes.SOCKET_RECONNECT_HANDLE:
ProjectManager.all().delete();
payload.projectManagers.forEach((projectManager) => {
ProjectManager.upsert(projectManager);
});
break;
case ActionTypes.CORE_INITIALIZE:
case ActionTypes.PROJECT_CREATE__SUCCESS:
case ActionTypes.PROJECT_CREATE_HANDLE:
payload.projectManagers.forEach((projectManager) => {
ProjectManager.upsert(projectManager);
});
break;
case ActionTypes.PROJECT_MANAGER_CREATE:
ProjectManager.upsert(payload.projectManager);
break;
case ActionTypes.PROJECT_MANAGER_CREATE__SUCCESS:
ProjectManager.withId(payload.localId).delete();
ProjectManager.upsert(payload.projectManager);
break;
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
ProjectManager.upsert(payload.projectManager);
if (payload.projectManagers) {
payload.projectManagers.forEach((projectManager) => {
ProjectManager.upsert(projectManager);
});
}
break;
case ActionTypes.PROJECT_MANAGER_DELETE:
ProjectManager.withId(payload.id).delete();
break;
case ActionTypes.PROJECT_MANAGER_DELETE__SUCCESS:
case ActionTypes.PROJECT_MANAGER_DELETE_HANDLE: {
const projectManagerModel = ProjectManager.withId(payload.projectManager.id);
if (projectManagerModel) {
projectManagerModel.delete();
}
break;
}
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
if (payload.projectManagers) {
payload.projectManagers.forEach((projectManager) => {
ProjectManager.upsert(projectManager);
});
}
break;
default:
}
}
}

View file

@ -1,69 +0,0 @@
import { Model, attr, fk } from 'redux-orm';
import ActionTypes from '../constants/ActionTypes';
export default class extends Model {
static modelName = 'ProjectMembership';
static fields = {
id: attr(),
projectId: fk({
to: 'Project',
as: 'project',
relatedName: 'memberships',
}),
userId: fk({
to: 'User',
as: 'user',
relatedName: 'projectMemberships',
}),
};
static reducer({ type, payload }, ProjectMembership) {
switch (type) {
case ActionTypes.PROJECTS_FETCH_SUCCEEDED:
case ActionTypes.PROJECT_CREATE_SUCCEEDED:
case ActionTypes.PROJECT_CREATE_RECEIVED:
payload.projectMemberships.forEach((projectMembership) => {
ProjectMembership.upsert(projectMembership);
});
break;
case ActionTypes.PROJECT_MEMBERSHIP_CREATE:
case ActionTypes.PROJECT_MEMBERSHIP_CREATE_RECEIVED:
ProjectMembership.upsert(payload.projectMembership);
break;
case ActionTypes.PROJECT_MEMBERSHIP_CREATE_SUCCEEDED:
ProjectMembership.withId(payload.localId).delete();
ProjectMembership.upsert(payload.projectMembership);
break;
case ActionTypes.PROJECT_MEMBERSHIP_DELETE:
ProjectMembership.withId(payload.id).deleteWithRelated();
break;
case ActionTypes.PROJECT_MEMBERSHIP_DELETE_RECEIVED:
ProjectMembership.withId(payload.projectMembership.id).deleteWithRelated();
break;
default:
}
}
deleteWithRelated() {
this.project.boards.toModelArray().forEach((boardModel) => {
boardModel.cards.toModelArray().forEach((cardModel) => {
try {
cardModel.users.remove(this.userId);
} catch {} // eslint-disable-line no-empty
});
try {
boardModel.filterUsers.remove(this.userId);
} catch {} // eslint-disable-line no-empty
});
this.delete();
}
}

View file

@ -20,16 +20,42 @@ export default class extends Model {
static reducer({ type, payload }, Task) {
switch (type) {
case ActionTypes.BOARD_FETCH_SUCCEEDED:
case ActionTypes.CARD_CREATE_SUCCEEDED:
case ActionTypes.CARD_CREATE_RECEIVED:
case ActionTypes.LOCATION_CHANGE_HANDLE:
case ActionTypes.CORE_INITIALIZE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
if (payload.tasks) {
payload.tasks.forEach((task) => {
Task.upsert(task);
});
}
break;
case ActionTypes.SOCKET_RECONNECT_HANDLE:
Task.all().delete();
if (payload.tasks) {
payload.tasks.forEach((task) => {
Task.upsert(task);
});
}
break;
case ActionTypes.BOARD_FETCH__SUCCESS:
payload.tasks.forEach((task) => {
Task.upsert(task);
});
break;
case ActionTypes.TASK_CREATE:
case ActionTypes.TASK_CREATE_RECEIVED:
case ActionTypes.TASK_CREATE_HANDLE:
case ActionTypes.TASK_UPDATE__SUCCESS:
case ActionTypes.TASK_UPDATE_HANDLE:
Task.upsert(payload.task);
break;
case ActionTypes.TASK_CREATE__SUCCESS:
Task.withId(payload.localId).delete();
Task.upsert(payload.task);
break;
@ -41,19 +67,16 @@ export default class extends Model {
Task.withId(payload.id).delete();
break;
case ActionTypes.TASK_CREATE_SUCCEEDED:
Task.withId(payload.localId).delete();
Task.upsert(payload.task);
break;
case ActionTypes.TASK_UPDATE_RECEIVED:
Task.withId(payload.task.id).update(payload.task);
break;
case ActionTypes.TASK_DELETE_RECEIVED:
Task.withId(payload.task.id).delete();
case ActionTypes.TASK_DELETE__SUCCESS:
case ActionTypes.TASK_DELETE_HANDLE: {
const taskModel = Task.withId(payload.task.id);
if (taskModel) {
taskModel.delete();
}
break;
}
default:
}
}

View file

@ -60,74 +60,53 @@ export default class extends Model {
static reducer({ type, payload }, User) {
switch (type) {
case ActionTypes.USER_CREATE_SUCCEEDED:
case ActionTypes.USER_CREATE_RECEIVED:
case ActionTypes.CURRENT_USER_FETCH_SUCCEEDED:
case ActionTypes.PROJECT_MEMBERSHIP_CREATE_RECEIVED:
case ActionTypes.NOTIFICATION_CREATE_RECEIVED:
User.upsert(payload.user);
case ActionTypes.LOCATION_CHANGE_HANDLE:
if (payload.users) {
payload.users.forEach((user) => {
User.upsert(user);
});
}
break;
case ActionTypes.USERS_FETCH_SUCCEEDED:
case ActionTypes.PROJECTS_FETCH_SUCCEEDED:
case ActionTypes.PROJECT_CREATE_SUCCEEDED:
case ActionTypes.PROJECT_CREATE_RECEIVED:
case ActionTypes.ACTIONS_FETCH_SUCCEEDED:
case ActionTypes.NOTIFICATIONS_FETCH_SUCCEEDED:
case ActionTypes.SOCKET_RECONNECT_HANDLE:
User.all().delete();
User.upsert(payload.user);
payload.users.forEach((user) => {
User.upsert(user);
});
break;
case ActionTypes.CORE_INITIALIZE:
User.upsert(payload.user);
payload.users.forEach((user) => {
User.upsert(user);
});
break;
case ActionTypes.USER_CREATE__SUCCESS:
case ActionTypes.USER_CREATE_HANDLE:
case ActionTypes.USER_UPDATE__SUCCESS:
User.upsert(payload.user);
break;
case ActionTypes.USER_UPDATE:
User.withId(payload.id).update(payload.data);
break;
case ActionTypes.USER_EMAIL_UPDATE_ERROR_CLEAR: {
const userModel = User.withId(payload.id);
case ActionTypes.USER_UPDATE_HANDLE:
User.upsert(payload.user);
userModel.update({
emailUpdateForm: {
...userModel.emailUpdateForm,
error: null,
},
});
if (payload.users) {
payload.users.forEach((user) => {
User.upsert(user);
});
}
break;
}
case ActionTypes.USER_PASSWORD_UPDATE_ERROR_CLEAR: {
const userModel = User.withId(payload.id);
userModel.update({
passwordUpdateForm: {
...userModel.passwordUpdateForm,
error: null,
},
});
break;
}
case ActionTypes.USER_USERNAME_UPDATE_ERROR_CLEAR: {
const userModel = User.withId(payload.id);
userModel.update({
usernameUpdateForm: {
...userModel.usernameUpdateForm,
error: null,
},
});
break;
}
case ActionTypes.USER_DELETE:
User.withId(payload.id).deleteWithRelated();
break;
case ActionTypes.USER_UPDATE_RECEIVED:
User.withId(payload.user.id).update(payload.user);
break;
case ActionTypes.USER_EMAIL_UPDATE_REQUESTED: {
case ActionTypes.USER_EMAIL_UPDATE: {
const userModel = User.withId(payload.id);
userModel.update({
@ -140,7 +119,7 @@ export default class extends Model {
break;
}
case ActionTypes.USER_EMAIL_UPDATE_SUCCEEDED: {
case ActionTypes.USER_EMAIL_UPDATE__SUCCESS: {
User.withId(payload.user.id).update({
...payload.user,
emailUpdateForm: DEFAULT_EMAIL_UPDATE_FORM,
@ -148,7 +127,7 @@ export default class extends Model {
break;
}
case ActionTypes.USER_EMAIL_UPDATE_FAILED: {
case ActionTypes.USER_EMAIL_UPDATE__FAILURE: {
const userModel = User.withId(payload.id);
userModel.update({
@ -161,7 +140,7 @@ export default class extends Model {
break;
}
case ActionTypes.USER_PASSWORD_UPDATE_REQUESTED: {
case ActionTypes.USER_PASSWORD_UPDATE: {
const userModel = User.withId(payload.id);
userModel.update({
@ -174,7 +153,7 @@ export default class extends Model {
break;
}
case ActionTypes.USER_PASSWORD_UPDATE_SUCCEEDED: {
case ActionTypes.USER_PASSWORD_UPDATE__SUCCESS: {
User.withId(payload.user.id).update({
...payload.user,
passwordUpdateForm: DEFAULT_PASSWORD_UPDATE_FORM,
@ -182,7 +161,7 @@ export default class extends Model {
break;
}
case ActionTypes.USER_PASSWORD_UPDATE_FAILED: {
case ActionTypes.USER_PASSWORD_UPDATE__FAILURE: {
const userModel = User.withId(payload.id);
userModel.update({
@ -195,7 +174,7 @@ export default class extends Model {
break;
}
case ActionTypes.USER_USERNAME_UPDATE_REQUESTED: {
case ActionTypes.USER_USERNAME_UPDATE: {
const userModel = User.withId(payload.id);
userModel.update({
@ -208,7 +187,7 @@ export default class extends Model {
break;
}
case ActionTypes.USER_USERNAME_UPDATE_SUCCEEDED: {
case ActionTypes.USER_USERNAME_UPDATE__SUCCESS: {
User.withId(payload.user.id).update({
...payload.user,
usernameUpdateForm: DEFAULT_USERNAME_UPDATE_FORM,
@ -216,7 +195,7 @@ export default class extends Model {
break;
}
case ActionTypes.USER_USERNAME_UPDATE_FAILED: {
case ActionTypes.USER_USERNAME_UPDATE__FAILURE: {
const userModel = User.withId(payload.id);
userModel.update({
@ -229,29 +208,44 @@ export default class extends Model {
break;
}
case ActionTypes.USER_AVATAR_UPDATE_REQUESTED:
case ActionTypes.USER_AVATAR_UPDATE:
User.withId(payload.id).update({
isAvatarUpdating: true,
});
break;
case ActionTypes.USER_AVATAR_UPDATE_SUCCEEDED:
case ActionTypes.USER_AVATAR_UPDATE__SUCCESS:
User.withId(payload.user.id).update({
...payload.user,
isAvatarUpdating: false,
});
break;
case ActionTypes.USER_AVATAR_UPDATE_FAILED:
case ActionTypes.USER_AVATAR_UPDATE__FAILURE:
User.withId(payload.id).update({
isAvatarUpdating: false,
});
break;
case ActionTypes.USER_DELETE_SUCCEEDED:
case ActionTypes.USER_DELETE_RECEIVED:
case ActionTypes.USER_DELETE:
User.withId(payload.id).deleteWithRelated();
break;
case ActionTypes.USER_DELETE__SUCCESS:
case ActionTypes.USER_DELETE_HANDLE:
User.withId(payload.user.id).deleteWithRelated(payload.user);
break;
case ActionTypes.PROJECT_CREATE_HANDLE:
case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE:
case ActionTypes.BOARD_FETCH__SUCCESS:
case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
case ActionTypes.ACTIONS_FETCH__SUCCESS:
case ActionTypes.NOTIFICATION_CREATE_HANDLE:
payload.users.forEach((user) => {
User.upsert(user);
});
break;
default:
}
@ -263,8 +257,12 @@ export default class extends Model {
}).orderBy('id');
}
getOrderedProjectMembershipsQuerySet() {
return this.projectMemberships.orderBy('id');
getOrderedProjectManagersQuerySet() {
return this.projectManagers.orderBy('id');
}
getOrderedBoardMembershipsQuerySet() {
return this.boardMemberships.orderBy('id');
}
getOrderedUnreadNotificationsQuerySet() {
@ -275,10 +273,41 @@ export default class extends Model {
.orderBy('id', false);
}
deleteWithRelated(user) {
this.projectMemberships.toModelArray().forEach((projectMembershipModel) => {
projectMembershipModel.deleteWithRelated();
getOrderedAvailableProjectsModelArray() {
const projectIds = [];
const projectModels = this.getOrderedProjectManagersQuerySet()
.toModelArray()
.map(({ project: projectModel }) => {
projectIds.push(projectModel.id);
return projectModel;
});
this.getOrderedBoardMembershipsQuerySet()
.toModelArray()
.forEach(({ board: { project: projectModel } }) => {
if (projectIds.includes(projectModel.id)) {
return;
}
projectIds.push(projectModel.id);
projectModels.push(projectModel);
});
return projectModels;
}
deleteRelated() {
this.projectManagers.delete();
this.boardMemberships.toModelArray().forEach((boardMembershipModel) => {
boardMembershipModel.deleteWithRelated();
});
}
deleteWithRelated(user) {
this.deleteRelated();
this.update(
user || {

View file

@ -1,7 +1,8 @@
import User from './User';
import Project from './Project';
import ProjectMembership from './ProjectMembership';
import ProjectManager from './ProjectManager';
import Board from './Board';
import BoardMembership from './BoardMembership';
import Label from './Label';
import List from './List';
import Card from './Card';
@ -13,8 +14,9 @@ import Notification from './Notification';
export {
User,
Project,
ProjectMembership,
ProjectManager,
Board,
BoardMembership,
Label,
List,
Card,