feat: Version 2

Closes #627, closes #1047
This commit is contained in:
Maksim Eltyshev
2025-05-10 02:09:06 +02:00
parent ad7fb51cfa
commit 2ee1166747
1557 changed files with 76832 additions and 47042 deletions

View File

@@ -0,0 +1,34 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createSelector } from 'redux-orm';
import orm from '../orm';
import { isLocalId } from '../utils/local-id';
export const makeSelectActivityById = () =>
createSelector(
orm,
(_, id) => id,
({ Activity }, id) => {
const activityModel = Activity.withId(id);
if (!activityModel) {
return activityModel;
}
return {
...activityModel.ref,
isPersisted: !isLocalId(activityModel.id),
};
},
);
export const selectActivityById = makeSelectActivityById();
export default {
makeSelectActivityById,
selectActivityById,
};

View File

@@ -1,6 +1,32 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createSelector } from 'redux-orm';
import orm from '../orm';
import { isLocalId } from '../utils/local-id';
export const makeSelectAttachmentById = () =>
createSelector(
orm,
(_, id) => id,
({ Attachment }, id) => {
const attachmentModel = Attachment.withId(id);
if (!attachmentModel) {
return attachmentModel;
}
return {
...attachmentModel.ref,
isPersisted: !isLocalId(attachmentModel.id),
};
},
);
export const selectAttachmentById = makeSelectAttachmentById();
export const selectIsAttachmentWithIdExists = createSelector(
orm,
@@ -9,5 +35,7 @@ export const selectIsAttachmentWithIdExists = createSelector(
);
export default {
makeSelectAttachmentById,
selectAttachmentById,
selectIsAttachmentWithIdExists,
};

View File

@@ -0,0 +1,41 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createSelector } from 'redux-orm';
import orm from '../orm';
import { isLocalId } from '../utils/local-id';
export const makeSelectBackgroundImageById = () =>
createSelector(
orm,
(_, id) => id,
({ BackgroundImage }, id) => {
const backgroundImageModel = BackgroundImage.withId(id);
if (!backgroundImageModel) {
return backgroundImageModel;
}
return {
...backgroundImageModel.ref,
isPersisted: !isLocalId(backgroundImageModel.id),
};
},
);
export const selectBackgroundImageById = makeSelectBackgroundImageById();
export const selectIsBackgroundImageWithIdExists = createSelector(
orm,
(_, id) => id,
({ BackgroundImage }, id) => BackgroundImage.idExists(id),
);
export default {
makeSelectBackgroundImageById,
selectBackgroundImageById,
selectIsBackgroundImageWithIdExists,
};

View File

@@ -0,0 +1,57 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createSelector } from 'redux-orm';
import orm from '../orm';
import { isLocalId } from '../utils/local-id';
export const makeSelectBaseCustomFieldGroupById = () =>
createSelector(
orm,
(_, id) => id,
({ BaseCustomFieldGroup }, id) => {
const baseCustomFieldGroupModel = BaseCustomFieldGroup.withId(id);
if (!baseCustomFieldGroupModel) {
return baseCustomFieldGroupModel;
}
return {
...baseCustomFieldGroupModel.ref,
isPersisted: !isLocalId(baseCustomFieldGroupModel.id),
};
},
);
export const selectBaseCustomFieldGroupById = makeSelectBaseCustomFieldGroupById();
export const makeSelectCustomFieldsByBaseGroupId = () =>
createSelector(
orm,
(_, id) => id,
({ BaseCustomFieldGroup }, id) => {
if (!id) {
return id;
}
const baseCustomFieldGroupModel = BaseCustomFieldGroup.withId(id);
if (!baseCustomFieldGroupModel) {
return baseCustomFieldGroupModel;
}
return baseCustomFieldGroupModel.getCustomFieldsQuerySet().toRefArray();
},
);
export const selectCustomFieldsByBaseGroupId = makeSelectCustomFieldsByBaseGroupId();
export default {
makeSelectBaseCustomFieldGroupById,
selectBaseCustomFieldGroupById,
makeSelectCustomFieldsByBaseGroupId,
selectCustomFieldsByBaseGroupId,
};

View File

@@ -1,6 +1,12 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createSelector } from 'redux-orm';
import orm from '../orm';
import { isLocalId } from '../utils/local-id';
export const makeSelectBoardMembershipById = () =>
createSelector(
@@ -13,7 +19,10 @@ export const makeSelectBoardMembershipById = () =>
return boardMembershipModel;
}
return boardMembershipModel.ref;
return {
...boardMembershipModel.ref,
isPersisted: !isLocalId(boardMembershipModel.id),
};
},
);

View File

@@ -1,14 +1,22 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createSelector } from 'redux-orm';
import orm from '../orm';
import { selectPath } from './router';
import { selectCurrentUserId } from './users';
import { isLocalId } from '../utils/local-id';
import { isListArchiveOrTrash } from '../utils/record-helpers';
import { ListTypes } from '../constants/Enums';
export const makeSelectBoardById = () =>
createSelector(
orm,
(_, id) => id,
(state) => selectPath(state).boardId,
({ Board }, id) => {
const boardModel = Board.withId(id);
@@ -16,12 +24,98 @@ export const makeSelectBoardById = () =>
return boardModel;
}
return boardModel.ref;
return {
...boardModel.ref,
isPersisted: !isLocalId(boardModel.id),
};
},
);
export const selectBoardById = makeSelectBoardById();
export const makeSelectCurrentUserMembershipByBoardId = () =>
createSelector(
orm,
(_, id) => id,
(state) => selectCurrentUserId(state),
({ Board }, id, currentUserId) => {
if (!id) {
return id;
}
const boardModel = Board.withId(id);
if (!boardModel) {
return boardModel;
}
const boardMembershipModel = boardModel.getMembershipModelByUserId(currentUserId);
if (!boardMembershipModel) {
return boardMembershipModel;
}
return boardMembershipModel.ref;
},
);
const selectCurrentUserMembershipByBoardId = makeSelectCurrentUserMembershipByBoardId();
export const makeSelectNotificationsTotalByBoardId = () =>
createSelector(
orm,
(_, id) => id,
(state) => selectCurrentUserId(state),
({ Board }, id) => {
const boardModel = Board.withId(id);
if (!boardModel) {
return boardModel;
}
return boardModel.getUnreadNotificationsQuerySet().count();
},
);
export const selectNotificationsTotalByBoardId = makeSelectNotificationsTotalByBoardId();
export const makeSelectNotificationServiceIdsByBoardId = () =>
createSelector(
orm,
(_, id) => id,
(state) => selectCurrentUserId(state),
({ Board }, id) => {
const boardModel = Board.withId(id);
if (!boardModel) {
return boardModel;
}
return boardModel
.getNotificationServicesQuerySet()
.toRefArray()
.map((notificationService) => notificationService.id);
},
);
export const selectNotificationServiceIdsByBoardId = makeSelectNotificationServiceIdsByBoardId();
export const selectIsBoardWithIdAvailableForCurrentUser = createSelector(
orm,
(_, id) => id,
(state) => selectCurrentUserId(state),
({ Board, User }, id, currentUserId) => {
const boardModel = Board.withId(id);
if (!boardModel) {
return false;
}
const currentUserModel = User.withId(currentUserId);
return boardModel.isAvailableForUser(currentUserModel);
},
);
export const selectCurrentBoard = createSelector(
orm,
(state) => selectPath(state).boardId,
@@ -43,8 +137,7 @@ export const selectCurrentBoard = createSelector(
export const selectMembershipsForCurrentBoard = createSelector(
orm,
(state) => selectPath(state).boardId,
(state) => selectCurrentUserId(state),
({ Board }, id, currentUserId) => {
({ Board }, id) => {
if (!id) {
return id;
}
@@ -55,14 +148,35 @@ export const selectMembershipsForCurrentBoard = createSelector(
return boardModel;
}
return boardModel.getOrderedMembershipsModelArray().map((boardMembershipModel) => ({
...boardMembershipModel.ref,
isPersisted: !isLocalId(boardMembershipModel.id),
user: {
...boardMembershipModel.user.ref,
isCurrent: boardMembershipModel.user.id === currentUserId,
},
}));
return boardModel
.getMembershipsQuerySet()
.toModelArray()
.map((boardMembershipModel) => ({
...boardMembershipModel.ref,
isPersisted: !isLocalId(boardMembershipModel.id),
user: boardMembershipModel.user.ref,
}));
},
);
export const selectMemberUserIdsForCurrentBoard = createSelector(
orm,
(state) => selectPath(state).boardId,
({ Board }, id) => {
if (!id) {
return id;
}
const boardModel = Board.withId(id);
if (!boardModel) {
return boardModel;
}
return boardModel
.getMembershipsQuerySet()
.toModelArray()
.map((boardMembershipModel) => boardMembershipModel.user.id);
},
);
@@ -81,7 +195,7 @@ export const selectCurrentUserMembershipForCurrentBoard = createSelector(
return boardModel;
}
const boardMembershipModel = boardModel.getMembershipModelForUser(currentUserId);
const boardMembershipModel = boardModel.getMembershipModelByUserId(currentUserId);
if (!boardMembershipModel) {
return boardMembershipModel;
@@ -105,17 +219,59 @@ export const selectLabelsForCurrentBoard = createSelector(
return boardModel;
}
return boardModel
.getOrderedLabelsQuerySet()
.toRefArray()
.map((label) => ({
...label,
isPersisted: !isLocalId(label.id),
}));
return boardModel.getLabelsQuerySet().toRefArray();
},
);
export const selectListIdsForCurrentBoard = createSelector(
export const selectArchiveListIdForCurrentBoard = createSelector(
orm,
(state) => selectPath(state).boardId,
({ Board }, id) => {
if (!id) {
return id;
}
const boardModel = Board.withId(id);
if (!boardModel) {
return boardModel;
}
const listModel = boardModel.lists
.filter({
type: ListTypes.ARCHIVE,
})
.first();
return listModel.id;
},
);
export const selectTrashListIdForCurrentBoard = createSelector(
orm,
(state) => selectPath(state).boardId,
({ Board }, id) => {
if (!id) {
return id;
}
const boardModel = Board.withId(id);
if (!boardModel) {
return boardModel;
}
const listModel = boardModel.lists
.filter({
type: ListTypes.TRASH,
})
.first();
return listModel.id;
},
);
export const selectFiniteListIdsForCurrentBoard = createSelector(
orm,
(state) => selectPath(state).boardId,
({ Board }, id) => {
@@ -130,13 +286,14 @@ export const selectListIdsForCurrentBoard = createSelector(
}
return boardModel
.getOrderedListsQuerySet()
.getFiniteListsQuerySet()
.toRefArray()
.map((list) => list.id);
},
);
export const selectFilterUsersForCurrentBoard = createSelector(
// TODO: rename?
export const selectAvailableListsForCurrentBoard = createSelector(
orm,
(state) => selectPath(state).boardId,
({ Board }, id) => {
@@ -150,11 +307,14 @@ export const selectFilterUsersForCurrentBoard = createSelector(
return boardModel;
}
return boardModel.filterUsers.toRefArray();
return boardModel
.getListsQuerySet()
.toRefArray()
.filter((list) => !isListArchiveOrTrash(list));
},
);
export const selectFilterLabelsForCurrentBoard = createSelector(
export const selectFilteredCardIdsForCurrentBoard = createSelector(
orm,
(state) => selectPath(state).boardId,
({ Board }, id) => {
@@ -168,11 +328,11 @@ export const selectFilterLabelsForCurrentBoard = createSelector(
return boardModel;
}
return boardModel.filterLabels.toRefArray();
return boardModel.getFilteredCardsModelArray().map((cardModel) => cardModel.id);
},
);
export const selectFilterTextForCurrentBoard = createSelector(
export const selectCustomFieldGroupIdsForCurrentBoard = createSelector(
orm,
(state) => selectPath(state).boardId,
({ Board }, id) => {
@@ -186,7 +346,76 @@ export const selectFilterTextForCurrentBoard = createSelector(
return boardModel;
}
return boardModel.filterText;
return boardModel
.getCustomFieldGroupsQuerySet()
.toRefArray()
.map((customFieldGroup) => customFieldGroup.id);
},
);
export const selectCustomFieldGroupsForCurrentBoard = createSelector(
orm,
(state) => selectPath(state).boardId,
({ Board }, id) => {
if (!id) {
return id;
}
const boardModel = Board.withId(id);
if (!boardModel) {
return boardModel;
}
return boardModel
.getCustomFieldGroupsQuerySet()
.toModelArray()
.map((customFieldGroupModel) => {
if (!customFieldGroupModel.name) {
return {
...customFieldGroupModel.ref,
name: customFieldGroupModel.baseCustomFieldGroup.name,
};
}
return customFieldGroupModel.ref;
});
},
);
export const selectFilterUserIdsForCurrentBoard = createSelector(
orm,
(state) => selectPath(state).boardId,
({ Board }, id) => {
if (!id) {
return id;
}
const boardModel = Board.withId(id);
if (!boardModel) {
return boardModel;
}
return boardModel.filterUsers.toRefArray().map((user) => user.id);
},
);
export const selectFilterLabelIdsForCurrentBoard = createSelector(
orm,
(state) => selectPath(state).boardId,
({ Board }, id) => {
if (!id) {
return id;
}
const boardModel = Board.withId(id);
if (!boardModel) {
return boardModel;
}
return boardModel.filterLabels.toRefArray().map((label) => label.id);
},
);
@@ -199,13 +428,26 @@ export const selectIsBoardWithIdExists = createSelector(
export default {
makeSelectBoardById,
selectBoardById,
makeSelectCurrentUserMembershipByBoardId,
selectCurrentUserMembershipByBoardId,
makeSelectNotificationsTotalByBoardId,
selectNotificationsTotalByBoardId,
makeSelectNotificationServiceIdsByBoardId,
selectNotificationServiceIdsByBoardId,
selectIsBoardWithIdAvailableForCurrentUser,
selectCurrentBoard,
selectMembershipsForCurrentBoard,
selectMemberUserIdsForCurrentBoard,
selectCurrentUserMembershipForCurrentBoard,
selectLabelsForCurrentBoard,
selectListIdsForCurrentBoard,
selectFilterUsersForCurrentBoard,
selectFilterLabelsForCurrentBoard,
selectFilterTextForCurrentBoard,
selectArchiveListIdForCurrentBoard,
selectTrashListIdForCurrentBoard,
selectFiniteListIdsForCurrentBoard,
selectAvailableListsForCurrentBoard,
selectFilteredCardIdsForCurrentBoard,
selectCustomFieldGroupIdsForCurrentBoard,
selectCustomFieldGroupsForCurrentBoard,
selectFilterUserIdsForCurrentBoard,
selectFilterLabelIdsForCurrentBoard,
selectIsBoardWithIdExists,
};

View File

@@ -1,8 +1,15 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createSelector } from 'redux-orm';
import orm from '../orm';
import { selectRecentCardId } from './core';
import { selectPath } from './router';
import { selectCurrentUserId } from './users';
import { buildCustomFieldValueId } from '../models/CustomFieldValue';
import { isLocalId } from '../utils/local-id';
export const makeSelectCardById = () =>
@@ -18,7 +25,6 @@ export const makeSelectCardById = () =>
return {
...cardModel.ref,
coverUrl: cardModel.coverAttachment && cardModel.coverAttachment.coverUrl,
isPersisted: !isLocalId(id),
};
},
@@ -37,14 +43,15 @@ export const makeSelectCardIndexById = () =>
return cardModel;
}
const cardModels = cardModel.list.getFilteredOrderedCardsModelArray();
return cardModels.findIndex((cardModelItem) => cardModelItem.id === cardModel.id);
return cardModel.list
.getCardsModelArray()
.findIndex((cardModelItem) => cardModelItem.id === cardModel.id);
},
);
export const selectCardIndexById = makeSelectCardIndexById();
export const makeSelectUsersByCardId = () =>
export const makeSelectUserIdsByCardId = () =>
createSelector(
orm,
(_, id) => id,
@@ -55,13 +62,13 @@ export const makeSelectUsersByCardId = () =>
return cardModel;
}
return cardModel.users.toRefArray();
return cardModel.users.toRefArray().map((user) => user.id);
},
);
export const selectUsersByCardId = makeSelectUsersByCardId();
export const selectUserIdsByCardId = makeSelectUserIdsByCardId();
export const makeSelectLabelsByCardId = () =>
export const makeSelectLabelIdsByCardId = () =>
createSelector(
orm,
(_, id) => id,
@@ -72,13 +79,13 @@ export const makeSelectLabelsByCardId = () =>
return cardModel;
}
return cardModel.labels.toRefArray();
return cardModel.labels.toRefArray().map((label) => label.id);
},
);
export const selectLabelsByCardId = makeSelectLabelsByCardId();
export const selectLabelIdsByCardId = makeSelectLabelIdsByCardId();
export const makeSelectTaskIdsByCardId = () =>
export const makeSelectShownOnFrontOfCardTaskListIdsByCardId = () =>
createSelector(
orm,
(_, id) => id,
@@ -89,31 +96,12 @@ export const makeSelectTaskIdsByCardId = () =>
return cardModel;
}
return cardModel
.getOrderedTasksQuerySet()
.toRefArray()
.map((task) => task.id);
return cardModel.getShownOnFrontOfCardTaskListsModelArray().map((taskList) => taskList.id);
},
);
export const selectTaskIdsByCardId = makeSelectTaskIdsByCardId();
export const makeSelectTasksByCardId = () =>
createSelector(
orm,
(_, id) => id,
({ Card }, id) => {
const cardModel = Card.withId(id);
if (!cardModel) {
return cardModel;
}
return cardModel.getOrderedTasksQuerySet().toRefArray();
},
);
export const selectTasksByCardId = makeSelectTasksByCardId();
export const selectShownOnFrontOfCardTaskListIdsByCardId =
makeSelectShownOnFrontOfCardTaskListIdsByCardId();
export const makeSelectAttachmentsTotalByCardId = () =>
createSelector(
@@ -132,24 +120,64 @@ export const makeSelectAttachmentsTotalByCardId = () =>
export const selectAttachmentsTotalByCardId = makeSelectAttachmentsTotalByCardId();
export const makeSelectLastActivityIdByCardId = () =>
export const makeSelectShownOnFrontOfCardCustomFieldValueIdsByCardId = () =>
createSelector(
orm,
(_, id) => id,
({ Card }, id) => {
({ Card, CustomFieldValue }, id) => {
if (!id) {
return id;
}
const cardModel = Card.withId(id);
if (!cardModel) {
return cardModel;
}
const lastActivityModel = cardModel.getFilteredOrderedInCardActivitiesQuerySet().last();
return [
...cardModel.board
.getCustomFieldGroupsQuerySet()
.toModelArray()
.flatMap((customFieldGroupModel) =>
customFieldGroupModel
.getShownOnFrontOfCardCustomFieldsModelArray()
.flatMap((customFieldModel) => {
const customFieldValue = CustomFieldValue.withId(
buildCustomFieldValueId({
cardId: id,
customFieldGroupId: customFieldGroupModel.id,
customFieldId: customFieldModel.id,
}),
);
return lastActivityModel && lastActivityModel.id;
return customFieldValue ? customFieldValue.id : [];
}),
),
...cardModel
.getCustomFieldGroupsQuerySet()
.toModelArray()
.flatMap((customFieldGroupModel) =>
customFieldGroupModel
.getShownOnFrontOfCardCustomFieldsModelArray()
.flatMap((customFieldModel) => {
const customFieldValue = CustomFieldValue.withId(
buildCustomFieldValueId({
cardId: id,
customFieldGroupId: customFieldGroupModel.id,
customFieldId: customFieldModel.id,
}),
);
return customFieldValue ? customFieldValue.id : [];
}),
),
];
},
);
export const selectLastActivityIdByCardId = makeSelectLastActivityIdByCardId();
export const selectShownOnFrontOfCardCustomFieldValueIdsByCardId =
makeSelectShownOnFrontOfCardCustomFieldValueIdsByCardId();
export const makeSelectNotificationsByCardId = () =>
createSelector(
@@ -185,6 +213,40 @@ export const makeSelectNotificationsTotalByCardId = () =>
export const selectNotificationsTotalByCardId = makeSelectNotificationsTotalByCardId();
export const makeSelectIsCardWithIdRecent = () =>
createSelector(
orm,
(_, id) => id,
(state) => selectRecentCardId(state),
({ Card }, id, recentCardId) => {
const cardModel = Card.withId(id);
if (!cardModel) {
return false;
}
return cardModel.id === recentCardId;
},
);
export const selectIsCardWithIdRecent = makeSelectIsCardWithIdRecent();
export const selectIsCardWithIdAvailableForCurrentUser = createSelector(
orm,
(_, id) => id,
(state) => selectCurrentUserId(state),
({ Card, User }, id, currentUserId) => {
const cardModel = Card.withId(id);
if (!cardModel) {
return false;
}
const currentUserModel = User.withId(currentUserId);
return cardModel.isAvailableForUser(currentUserModel);
},
);
export const selectCurrentCard = createSelector(
orm,
(state) => selectPath(state).cardId,
@@ -203,7 +265,7 @@ export const selectCurrentCard = createSelector(
},
);
export const selectUsersForCurrentCard = createSelector(
export const selectUserIdsForCurrentCard = createSelector(
orm,
(state) => selectPath(state).cardId,
({ Card }, id) => {
@@ -217,11 +279,11 @@ export const selectUsersForCurrentCard = createSelector(
return cardModel;
}
return cardModel.users.toRefArray();
return cardModel.users.toRefArray().map((user) => user.id);
},
);
export const selectLabelsForCurrentCard = createSelector(
export const selectLabelIdsForCurrentCard = createSelector(
orm,
(state) => selectPath(state).cardId,
({ Card }, id) => {
@@ -235,11 +297,11 @@ export const selectLabelsForCurrentCard = createSelector(
return cardModel;
}
return cardModel.labels.toRefArray();
return cardModel.labels.toRefArray().map((label) => label.id);
},
);
export const selectTasksForCurrentCard = createSelector(
export const selectTaskListIdsForCurrentCard = createSelector(
orm,
(state) => selectPath(state).cardId,
({ Card }, id) => {
@@ -254,12 +316,55 @@ export const selectTasksForCurrentCard = createSelector(
}
return cardModel
.getOrderedTasksQuerySet()
.getTaskListsQuerySet()
.toRefArray()
.map((task) => ({
...task,
isPersisted: !isLocalId(task.id),
}));
.map((taskList) => taskList.id);
},
);
export const selectAttachmentIdsForCurrentCard = createSelector(
orm,
(state) => selectPath(state).cardId,
({ Card }, id) => {
if (!id) {
return id;
}
const cardModel = Card.withId(id);
if (!cardModel) {
return cardModel;
}
return cardModel
.getAttachmentsQuerySet()
.toRefArray()
.map((attachment) => attachment.id);
},
);
export const selectImageAttachmentIdsExceptCoverForCurrentCard = createSelector(
orm,
(state) => selectPath(state).cardId,
({ Card }, id) => {
if (!id) {
return id;
}
const cardModel = Card.withId(id);
if (!cardModel) {
return cardModel;
}
return cardModel
.getAttachmentsQuerySet()
.toModelArray()
.filter(
(attachmentModel) =>
attachmentModel.data && attachmentModel.data.image && !attachmentModel.coveredCard,
)
.map((attachmentModel) => attachmentModel.id);
},
);
@@ -277,47 +382,11 @@ export const selectAttachmentsForCurrentCard = createSelector(
return cardModel;
}
return cardModel
.getOrderedAttachmentsQuerySet()
.toRefArray()
.map((attachment) => ({
...attachment,
isCover: attachment.id === cardModel.coverAttachmentId,
isPersisted: !isLocalId(attachment.id),
}));
return cardModel.getAttachmentsQuerySet().toRefArray();
},
);
export const selectActivitiesForCurrentCard = createSelector(
orm,
(state) => selectPath(state).cardId,
(state) => selectCurrentUserId(state),
({ Card }, id, currentUserId) => {
if (!id) {
return id;
}
const cardModel = Card.withId(id);
if (!cardModel) {
return cardModel;
}
return cardModel
.getFilteredOrderedInCardActivitiesQuerySet()
.toModelArray()
.map((activityModel) => ({
...activityModel.ref,
isPersisted: !isLocalId(activityModel.id),
user: {
...activityModel.user.ref,
isCurrent: activityModel.user.id === currentUserId,
},
}));
},
);
export const selectNotificationIdsForCurrentCard = createSelector(
export const selectCustomFieldGroupIdsForCurrentCard = createSelector(
orm,
(state) => selectPath(state).cardId,
({ Card }, id) => {
@@ -332,9 +401,64 @@ export const selectNotificationIdsForCurrentCard = createSelector(
}
return cardModel
.getUnreadNotificationsQuerySet()
.getCustomFieldGroupsQuerySet()
.toRefArray()
.map((notification) => notification.id);
.map((customFieldGroup) => customFieldGroup.id);
},
);
export const selectCommentIdsForCurrentCard = createSelector(
orm,
(state) => selectPath(state).cardId,
({ Card }, id) => {
if (!id) {
return id;
}
const cardModel = Card.withId(id);
if (!cardModel) {
return cardModel;
}
return cardModel.getCommentsModelArray().map((commentModel) => commentModel.id);
},
);
export const selectActivityIdsForCurrentCard = createSelector(
orm,
(state) => selectPath(state).cardId,
({ Card }, id) => {
if (!id) {
return id;
}
const cardModel = Card.withId(id);
if (!cardModel) {
return cardModel;
}
return cardModel.getActivitiesModelArray().map((activity) => activity.id);
},
);
export const selectIsCurrentUserInCurrentCard = createSelector(
orm,
(state) => selectPath(state).cardId,
(state) => selectCurrentUserId(state),
({ Card }, id, currentUserId) => {
if (!id) {
return false;
}
const cardModel = Card.withId(id);
if (!cardModel) {
return false;
}
return cardModel.hasUserWithId(currentUserId);
},
);
@@ -343,27 +467,32 @@ export default {
selectCardById,
makeSelectCardIndexById,
selectCardIndexById,
makeSelectUsersByCardId,
selectUsersByCardId,
makeSelectLabelsByCardId,
selectLabelsByCardId,
makeSelectTaskIdsByCardId,
selectTaskIdsByCardId,
makeSelectTasksByCardId,
selectTasksByCardId,
makeSelectUserIdsByCardId,
selectUserIdsByCardId,
makeSelectLabelIdsByCardId,
selectLabelIdsByCardId,
makeSelectShownOnFrontOfCardTaskListIdsByCardId,
selectShownOnFrontOfCardTaskListIdsByCardId,
makeSelectAttachmentsTotalByCardId,
makeSelectShownOnFrontOfCardCustomFieldValueIdsByCardId,
selectShownOnFrontOfCardCustomFieldValueIdsByCardId,
selectAttachmentsTotalByCardId,
makeSelectLastActivityIdByCardId,
selectLastActivityIdByCardId,
makeSelectNotificationsByCardId,
selectNotificationsByCardId,
makeSelectNotificationsTotalByCardId,
selectNotificationsTotalByCardId,
makeSelectIsCardWithIdRecent,
selectIsCardWithIdRecent,
selectIsCardWithIdAvailableForCurrentUser,
selectCurrentCard,
selectUsersForCurrentCard,
selectLabelsForCurrentCard,
selectTasksForCurrentCard,
selectUserIdsForCurrentCard,
selectLabelIdsForCurrentCard,
selectTaskListIdsForCurrentCard,
selectAttachmentIdsForCurrentCard,
selectImageAttachmentIdsExceptCoverForCurrentCard,
selectAttachmentsForCurrentCard,
selectActivitiesForCurrentCard,
selectNotificationIdsForCurrentCard,
selectCustomFieldGroupIdsForCurrentCard,
selectCommentIdsForCurrentCard,
selectActivityIdsForCurrentCard,
selectIsCurrentUserInCurrentCard,
};

View File

@@ -0,0 +1,34 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createSelector } from 'redux-orm';
import orm from '../orm';
import { isLocalId } from '../utils/local-id';
export const makeSelectCommentById = () =>
createSelector(
orm,
(_, id) => id,
({ Comment }, id) => {
const commentModel = Comment.withId(id);
if (!commentModel) {
return commentModel;
}
return {
...commentModel.ref,
isPersisted: !isLocalId(commentModel.id),
};
},
);
export const selectCommentById = makeSelectCommentById();
export default {
makeSelectCommentById,
selectCommentById,
};

View File

@@ -0,0 +1,34 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
export const selectIsSocketDisconnected = ({ socket: { isDisconnected } }) => isDisconnected;
export const selectIsInitializing = ({ common: { isInitializing } }) => isInitializing;
export const selectConfig = ({ common: { config } }) => config;
export const selectOidcConfig = (state) => selectConfig(state).oidc;
export const selectActiveUsersLimit = (state) => selectConfig(state).activeUsersLimit;
export const selectAccessToken = ({ auth: { accessToken } }) => accessToken;
export const selectAuthenticateForm = ({ ui: { authenticateForm } }) => authenticateForm;
export const selectUserCreateForm = ({ ui: { userCreateForm } }) => userCreateForm;
export const selectProjectCreateForm = ({ ui: { projectCreateForm } }) => projectCreateForm;
export default {
selectIsSocketDisconnected,
selectIsInitializing,
selectConfig,
selectOidcConfig,
selectActiveUsersLimit,
selectAccessToken,
selectAuthenticateForm,
selectUserCreateForm,
selectProjectCreateForm,
};

129
client/src/selectors/core.js Executable file → Normal file
View File

@@ -1,122 +1,35 @@
import { createSelector } from 'redux-orm';
import isUndefined from 'lodash/isUndefined';
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import orm from '../orm';
import Config from '../constants/Config';
export const selectAccessToken = ({ auth: { accessToken } }) => accessToken;
export const selectIsContentFetching = ({ core: { isContentFetching } }) => isContentFetching;
export const selectIsLogouting = ({ core: { isLogouting } }) => isLogouting;
const nextPosition = (items, index, excludedId) => {
const filteredItems = isUndefined(excludedId)
? items
: items.filter((item) => item.id !== excludedId);
export const selectIsFavoritesEnabled = ({ core: { isFavoritesEnabled } }) => isFavoritesEnabled;
if (isUndefined(index)) {
const lastItem = filteredItems[filteredItems.length - 1];
export const selectIsEditModeEnabled = ({ core: { isEditModeEnabled } }) => isEditModeEnabled;
return (lastItem ? lastItem.position : 0) + Config.POSITION_GAP;
}
export const selectRecentCardId = ({ core: { recentCardId } }) => recentCardId;
const prevItem = filteredItems[index - 1];
const nextItem = filteredItems[index];
export const selectHomeView = ({ core: { homeView } }) => homeView;
const prevPosition = prevItem ? prevItem.position : 0;
export const selectProjectsSearch = ({ core: { projectsSearch } }) => projectsSearch;
if (!nextItem) {
return prevPosition + Config.POSITION_GAP;
}
export const selectProjectsOrder = ({ core: { projectsOrder } }) => projectsOrder;
return prevPosition + (nextItem.position - prevPosition) / 2;
};
export const selectNextBoardPosition = createSelector(
orm,
(_, projectId) => projectId,
(_, __, index) => index,
(_, __, ___, excludedId) => excludedId,
({ Project }, projectId, index, excludedId) => {
const projectModel = Project.withId(projectId);
if (!projectModel) {
return projectModel;
}
return nextPosition(projectModel.getOrderedBoardsQuerySet().toRefArray(), index, excludedId);
},
);
export const selectNextLabelPosition = createSelector(
orm,
(_, boardId) => boardId,
(_, __, index) => index,
(_, __, ___, excludedId) => excludedId,
({ Board }, boardId, index, excludedId) => {
const boardModel = Board.withId(boardId);
if (!boardModel) {
return boardModel;
}
return nextPosition(boardModel.getOrderedLabelsQuerySet().toRefArray(), index, excludedId);
},
);
export const selectNextListPosition = createSelector(
orm,
(_, boardId) => boardId,
(_, __, index) => index,
(_, __, ___, excludedId) => excludedId,
({ Board }, boardId, index, excludedId) => {
const boardModel = Board.withId(boardId);
if (!boardModel) {
return boardModel;
}
return nextPosition(boardModel.getOrderedListsQuerySet().toRefArray(), index, excludedId);
},
);
export const selectNextCardPosition = createSelector(
orm,
(_, listId) => listId,
(_, __, index) => index,
(_, __, ___, excludedId) => excludedId,
({ List }, listId, index, excludedId) => {
const listModel = List.withId(listId);
if (!listModel) {
return listModel;
}
return nextPosition(listModel.getFilteredOrderedCardsModelArray(), index, excludedId);
},
);
export const selectNextTaskPosition = createSelector(
orm,
(_, cardId) => cardId,
(_, __, index) => index,
(_, __, ___, excludedId) => excludedId,
({ Card }, cardId, index, excludedId) => {
const cardModel = Card.withId(cardId);
if (!cardModel) {
return cardModel;
}
return nextPosition(cardModel.getOrderedTasksQuerySet().toRefArray(), index, excludedId);
},
);
export const selectIsHiddenProjectsVisible = ({ core: { isHiddenProjectsVisible } }) =>
isHiddenProjectsVisible;
export default {
selectAccessToken,
selectIsContentFetching,
selectIsLogouting,
selectNextBoardPosition,
selectNextLabelPosition,
selectNextListPosition,
selectNextCardPosition,
selectNextTaskPosition,
selectIsFavoritesEnabled,
selectIsEditModeEnabled,
selectRecentCardId,
selectHomeView,
selectProjectsSearch,
selectProjectsOrder,
selectIsHiddenProjectsVisible,
};

View File

@@ -0,0 +1,85 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createSelector } from 'redux-orm';
import orm from '../orm';
import { isLocalId } from '../utils/local-id';
export const makeSelectCustomFieldGroupById = () =>
createSelector(
orm,
(_, id) => id,
({ CustomFieldGroup }, id) => {
const customFieldGroupModel = CustomFieldGroup.withId(id);
if (!customFieldGroupModel) {
return customFieldGroupModel;
}
return {
...customFieldGroupModel.ref,
name: customFieldGroupModel.name || customFieldGroupModel.baseCustomFieldGroup.name,
isPersisted: !isLocalId(customFieldGroupModel.id),
};
},
);
export const selectCustomFieldGroupById = makeSelectCustomFieldGroupById();
export const makeSelectCustomFieldIdsByGroupId = () =>
createSelector(
orm,
(_, id) => id,
({ CustomFieldGroup }, id) => {
if (!id) {
return id;
}
const customFieldGroupModel = CustomFieldGroup.withId(id);
if (!customFieldGroupModel) {
return customFieldGroupModel;
}
return customFieldGroupModel
.getCustomFieldsModelArray()
.map((customFieldModel) => customFieldModel.id);
},
);
export const selectCustomFieldIdsByGroupId = makeSelectCustomFieldIdsByGroupId();
export const makeSelectCustomFieldsByGroupId = () =>
createSelector(
orm,
(_, id) => id,
({ CustomFieldGroup }, id) => {
if (!id) {
return id;
}
const customFieldGroupModel = CustomFieldGroup.withId(id);
if (!customFieldGroupModel) {
return customFieldGroupModel;
}
return customFieldGroupModel
.getCustomFieldsModelArray()
.map((customFieldModel) => customFieldModel.ref);
},
);
export const selectCustomFieldsByGroupId = makeSelectCustomFieldsByGroupId();
export default {
makeSelectCustomFieldGroupById,
selectCustomFieldGroupById,
makeSelectCustomFieldIdsByGroupId,
selectCustomFieldIdsByGroupId,
makeSelectCustomFieldsByGroupId,
selectCustomFieldsByGroupId,
};

View File

@@ -0,0 +1,30 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createSelector } from 'redux-orm';
import orm from '../orm';
export const makeSelectCustomFieldValueById = () =>
createSelector(
orm,
(_, id) => id,
({ CustomFieldValue }, id) => {
const customFieldValueModel = CustomFieldValue.withId(id);
if (!customFieldValueModel) {
return customFieldValueModel;
}
return customFieldValueModel.ref;
},
);
export const selectCustomFieldValueById = makeSelectCustomFieldValueById();
export default {
makeSelectCustomFieldValueById,
selectCustomFieldValueById,
};

View File

@@ -0,0 +1,34 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createSelector } from 'redux-orm';
import orm from '../orm';
import { isLocalId } from '../utils/local-id';
export const makeSelectCustomFieldById = () =>
createSelector(
orm,
(_, id) => id,
({ CustomField }, id) => {
const customFieldModel = CustomField.withId(id);
if (!customFieldModel) {
return customFieldModel;
}
return {
...customFieldModel.ref,
isPersisted: !isLocalId(customFieldModel.id),
};
},
);
export const selectCustomFieldById = makeSelectCustomFieldById();
export default {
makeSelectCustomFieldById,
selectCustomFieldById,
};

View File

@@ -1,33 +1,58 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import router from './router';
import socket from './socket';
import root from './root';
import common from './common';
import core from './core';
import modals from './modals';
import positioning from './positioning';
import users from './users';
import projects from './projects';
import projectManagers from './project-managers';
import backgroundImages from './background-images';
import baseCustomFieldGroups from './base-custom-field-groups';
import boards from './boards';
import boardMemberships from './board-memberships';
import labels from './labels';
import lists from './lists';
import cards from './cards';
import taskLists from './task-lists';
import tasks from './tasks';
import attachments from './attachments';
import customFieldGroups from './custom-field-groups';
import customFields from './custom-fields';
import customFieldValues from './custom-field-values';
import comments from './comments';
import activities from './activities';
import notifications from './notifications';
import notificationServices from './notification-services';
export default {
...router,
...socket,
...root,
...common,
...core,
...modals,
...positioning,
...users,
...projects,
...projectManagers,
...backgroundImages,
...baseCustomFieldGroups,
...boards,
...boardMemberships,
...labels,
...lists,
...cards,
...taskLists,
...tasks,
...attachments,
...customFieldGroups,
...customFields,
...customFieldValues,
...comments,
...activities,
...notifications,
...notificationServices,
};

View File

@@ -1,6 +1,12 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createSelector } from 'redux-orm';
import orm from '../orm';
import { isLocalId } from '../utils/local-id';
export const makeSelectLabelById = () =>
createSelector(
@@ -13,7 +19,10 @@ export const makeSelectLabelById = () =>
return labelModel;
}
return labelModel.ref;
return {
...labelModel.ref,
isPersisted: !isLocalId(labelModel.id),
};
},
);

View File

@@ -1,7 +1,14 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createSelector } from 'redux-orm';
import orm from '../orm';
import { selectPath } from './router';
import { isLocalId } from '../utils/local-id';
import { BoardContexts, ListTypes } from '../constants/Enums';
export const makeSelectListById = () =>
createSelector(
@@ -34,15 +41,121 @@ export const makeSelectCardIdsByListId = () =>
return listModel;
}
return listModel.getFilteredOrderedCardsModelArray().map((cardModel) => cardModel.id);
return listModel.getCardsModelArray().map((cardModel) => cardModel.id);
},
);
export const selectCardIdsByListId = makeSelectCardIdsByListId();
export const makeSelectFilteredCardIdsByListId = () =>
createSelector(
orm,
(_, id) => id,
({ List }, id) => {
const listModel = List.withId(id);
if (!listModel) {
return listModel;
}
return listModel.getFilteredCardsModelArray().map((cardModel) => cardModel.id);
},
);
export const selectFilteredCardIdsByListId = makeSelectFilteredCardIdsByListId();
export const selectCurrentListId = createSelector(
orm,
(state) => selectPath(state).boardId,
({ Board }, id) => {
if (!id) {
return id;
}
const boardModel = Board.withId(id);
if (!boardModel) {
return boardModel;
}
if (boardModel.context === BoardContexts.BOARD) {
return null;
}
const listModel = boardModel.lists
.filter({
type: boardModel.context || ListTypes.ACTIVE, // TODO: hack?
})
.first();
return listModel && listModel.id;
},
);
export const selectCurrentList = createSelector(
orm,
(state) => selectCurrentListId(state),
({ List }, id) => {
if (!id) {
return id;
}
const listModel = List.withId(id);
if (!listModel) {
return listModel;
}
return listModel.ref;
},
);
export const selectFirstFiniteListId = createSelector(
orm,
(state) => selectPath(state).boardId,
({ Board }, id) => {
if (!id) {
return id;
}
const boardModel = Board.withId(id);
if (!boardModel) {
return boardModel;
}
const listModel = boardModel.getFiniteListsQuerySet().first();
return listModel && listModel.id;
},
);
export const selectFilteredCardIdsForCurrentList = createSelector(
orm,
(state) => selectCurrentListId(state),
({ List }, id) => {
if (!id) {
return id;
}
const listModel = List.withId(id);
if (!listModel) {
return listModel;
}
return listModel.getFilteredCardsModelArray().map((cardModel) => cardModel.id);
},
);
export default {
makeSelectListById,
selectListById,
makeSelectCardIdsByListId,
selectCardIdsByListId,
makeSelectFilteredCardIdsByListId,
selectFilteredCardIdsByListId,
selectCurrentListId,
selectCurrentList,
selectFirstFiniteListId,
selectFilteredCardIdsForCurrentList,
};

View File

@@ -1,5 +1,56 @@
export const selectCurrentModal = ({ core: { currentModal } }) => currentModal;
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createSelector } from 'redux-orm';
import orm from '../orm';
import { selectPath } from './router';
import { selectCurrentUserId } from './users';
import { isUserAdminOrProjectOwner } from '../utils/record-helpers';
import ModalTypes from '../constants/ModalTypes';
import { UserRoles } from '../constants/Enums';
export const selectCurrentModal = ({ core: { modal } }) => modal;
export const isCurrentModalAvailableForCurrentUser = createSelector(
orm,
selectCurrentModal,
(state) => selectCurrentUserId(state),
(state) => selectPath(state).projectId,
({ User, Project, Board }, currentModal, currentUserId, currentProjectId) => {
if (currentModal) {
const currentUserModel = User.withId(currentUserId);
switch (currentModal.type) {
case ModalTypes.ADMINISTRATION:
return currentUserModel.role === UserRoles.ADMIN;
case ModalTypes.ADD_PROJECT:
return isUserAdminOrProjectOwner(currentUserModel);
case ModalTypes.PROJECT_SETTINGS: {
const projectModel = Project.withId(currentProjectId);
return !!projectModel && projectModel.isExternalAccessibleForUser(currentUserModel);
}
case ModalTypes.BOARD_SETTINGS: {
const boardModel = Board.withId(currentModal.params.id);
if (!boardModel || !boardModel.isAvailableForUser(currentUserModel)) {
return false;
}
return boardModel.project.hasManagerWithUserId(currentUserId);
}
default:
return true;
}
}
return true;
},
);
export default {
selectCurrentModal,
isCurrentModalAvailableForCurrentUser,
};

View File

@@ -0,0 +1,34 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createSelector } from 'redux-orm';
import orm from '../orm';
import { isLocalId } from '../utils/local-id';
export const makeSelectNotificationServiceById = () =>
createSelector(
orm,
(_, id) => id,
({ NotificationService }, id) => {
const notificationServiceModel = NotificationService.withId(id);
if (!notificationServiceModel) {
return notificationServiceModel;
}
return {
...notificationServiceModel.ref,
isPersisted: !isLocalId(notificationServiceModel.id),
};
},
);
export const selectNotificationServiceById = makeSelectNotificationServiceById();
export default {
makeSelectNotificationServiceById,
selectNotificationServiceById,
};

View File

@@ -0,0 +1,46 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createSelector } from 'redux-orm';
import orm from '../orm';
export const makeSelectNotificationById = () =>
createSelector(
orm,
(_, id) => id,
({ Notification }, id) => {
const notificationModel = Notification.withId(id);
if (!notificationModel) {
return notificationModel;
}
return notificationModel.ref;
},
);
export const selectNotificationById = makeSelectNotificationById();
export const makeSelectNotificationIdsByCardId = () =>
createSelector(
orm,
(_, id) => id,
({ Notification }, id) =>
Notification.filter({
cardId: id,
})
.toRefArray()
.map((notification) => notification.id),
);
export const selectNotificationIdsByCardId = makeSelectNotificationIdsByCardId();
export default {
makeSelectNotificationById,
selectNotificationById,
makeSelectNotificationIdsByCardId,
selectNotificationIdsByCardId,
};

View File

@@ -0,0 +1,213 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import isUndefined from 'lodash/isUndefined';
import { createSelector } from 'redux-orm';
import orm from '../orm';
import Config from '../constants/Config';
const nextPosition = (items, index, excludedId) => {
const filteredItems = isUndefined(excludedId)
? items
: items.filter((item) => item.id !== excludedId);
if (isUndefined(index)) {
const lastItem = filteredItems[filteredItems.length - 1];
return (lastItem ? lastItem.position : 0) + Config.POSITION_GAP;
}
const prevItem = filteredItems[index - 1];
const nextItem = filteredItems[index];
const prevPosition = prevItem ? prevItem.position : 0;
if (!nextItem) {
return prevPosition + Config.POSITION_GAP;
}
return prevPosition + (nextItem.position - prevPosition) / 2;
};
export const selectNextBoardPosition = createSelector(
orm,
(_, projectId) => projectId,
(_, __, index) => index,
(_, __, ___, excludedId) => excludedId,
({ Project }, projectId, index, excludedId) => {
const projectModel = Project.withId(projectId);
if (!projectModel) {
return projectModel;
}
return nextPosition(projectModel.getBoardsQuerySet().toRefArray(), index, excludedId);
},
);
export const selectNextLabelPosition = createSelector(
orm,
(_, boardId) => boardId,
(_, __, index) => index,
(_, __, ___, excludedId) => excludedId,
({ Board }, boardId, index, excludedId) => {
const boardModel = Board.withId(boardId);
if (!boardModel) {
return boardModel;
}
return nextPosition(boardModel.getLabelsQuerySet().toRefArray(), index, excludedId);
},
);
export const selectNextListPosition = createSelector(
orm,
(_, boardId) => boardId,
(_, __, index) => index,
(_, __, ___, excludedId) => excludedId,
({ Board }, boardId, index, excludedId) => {
const boardModel = Board.withId(boardId);
if (!boardModel) {
return boardModel;
}
return nextPosition(boardModel.getFiniteListsQuerySet().toRefArray(), index, excludedId);
},
);
export const selectNextCardPosition = createSelector(
orm,
(_, listId) => listId,
(_, __, index) => index,
(_, __, ___, excludedId) => excludedId,
({ List }, listId, index, excludedId) => {
const listModel = List.withId(listId);
if (!listModel) {
return listModel;
}
return nextPosition(listModel.getFilteredCardsModelArray(), index, excludedId);
},
);
export const selectNextTaskListPosition = createSelector(
orm,
(_, cardId) => cardId,
(_, __, index) => index,
(_, __, ___, excludedId) => excludedId,
({ Card }, cardId, index, excludedId) => {
const cardModel = Card.withId(cardId);
if (!cardModel) {
return cardModel;
}
return nextPosition(cardModel.getTaskListsQuerySet().toRefArray(), index, excludedId);
},
);
export const selectNextTaskPosition = createSelector(
orm,
(_, taskListId) => taskListId,
(_, __, index) => index,
(_, __, ___, excludedId) => excludedId,
({ TaskList }, taskListId, index, excludedId) => {
const taskListModel = TaskList.withId(taskListId);
if (!taskListModel) {
return taskListModel;
}
return nextPosition(taskListModel.getTasksQuerySet().toRefArray(), index, excludedId);
},
);
export const selectNextCustomFieldGroupPositionInBoard = createSelector(
orm,
(_, cardId) => cardId,
(_, __, index) => index,
(_, __, ___, excludedId) => excludedId,
({ Board }, boardId, index, excludedId) => {
const boardModel = Board.withId(boardId);
if (!boardModel) {
return boardModel;
}
return nextPosition(boardModel.getCustomFieldGroupsQuerySet().toRefArray(), index, excludedId);
},
);
export const selectNextCustomFieldGroupPositionInCard = createSelector(
orm,
(_, cardId) => cardId,
(_, __, index) => index,
(_, __, ___, excludedId) => excludedId,
({ Card }, cardId, index, excludedId) => {
const cardModel = Card.withId(cardId);
if (!cardModel) {
return cardModel;
}
return nextPosition(cardModel.getCustomFieldGroupsQuerySet().toRefArray(), index, excludedId);
},
);
export const selectNextCustomFieldPositionInBaseGroup = createSelector(
orm,
(_, baseCustomFieldGroupId) => baseCustomFieldGroupId,
(_, __, index) => index,
(_, __, ___, excludedId) => excludedId,
({ BaseCustomFieldGroup }, baseCustomFieldGroupId, index, excludedId) => {
const baseCustomFieldGroupModel = BaseCustomFieldGroup.withId(baseCustomFieldGroupId);
if (!baseCustomFieldGroupModel) {
return baseCustomFieldGroupModel;
}
return nextPosition(
baseCustomFieldGroupModel.getCustomFieldsQuerySet().toRefArray(),
index,
excludedId,
);
},
);
export const selectNextCustomFieldPositionInGroup = createSelector(
orm,
(_, customFieldGroupId) => customFieldGroupId,
(_, __, index) => index,
(_, __, ___, excludedId) => excludedId,
({ CustomFieldGroup }, customFieldGroupId, index, excludedId) => {
const customFieldGroupModel = CustomFieldGroup.withId(customFieldGroupId);
if (!customFieldGroupModel) {
return customFieldGroupModel;
}
return nextPosition(
customFieldGroupModel.getCustomFieldsQuerySet().toRefArray(),
index,
excludedId,
);
},
);
export default {
selectNextBoardPosition,
selectNextLabelPosition,
selectNextListPosition,
selectNextCardPosition,
selectNextTaskListPosition,
selectNextTaskPosition,
selectNextCustomFieldGroupPositionInBoard,
selectNextCustomFieldGroupPositionInCard,
selectNextCustomFieldPositionInBaseGroup,
selectNextCustomFieldPositionInGroup,
};

View File

@@ -1,3 +1,8 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createSelector } from 'redux-orm';
import orm from '../orm';

View File

@@ -1,3 +1,8 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createSelector } from 'redux-orm';
import orm from '../orm';
@@ -5,6 +10,134 @@ import { selectPath } from './router';
import { selectCurrentUserId } from './users';
import { isLocalId } from '../utils/local-id';
export const makeSelectProjectById = () =>
createSelector(
orm,
(_, id) => id,
({ Project }, id) => {
const projectModel = Project.withId(id);
if (!projectModel) {
return projectModel;
}
return projectModel.ref;
},
);
export const selectProjectById = makeSelectProjectById();
export const makeSelectBoardIdsByProjectId = () =>
createSelector(
orm,
(_, id) => id,
(state) => selectCurrentUserId(state),
({ Project, User }, id, currentUserId) => {
if (!id) {
return id;
}
const projectModel = Project.withId(id);
if (!projectModel) {
return projectModel;
}
const currentUserModel = User.withId(currentUserId);
return projectModel
.getBoardsModelArrayAvailableForUser(currentUserModel)
.map((boardModel) => boardModel.id);
},
);
export const selectBoardIdsByProjectId = makeSelectBoardIdsByProjectId();
export const makeSelectFirstBoardIdByProjectId = () =>
createSelector(
orm,
(_, id) => id,
(state) => selectCurrentUserId(state),
({ Project, User }, id, currentUserId) => {
const projectModel = Project.withId(id);
if (!projectModel) {
return projectModel;
}
const currentUserModel = User.withId(currentUserId);
const boardsModels = projectModel.getBoardsModelArrayAvailableForUser(currentUserModel);
return boardsModels[0] && boardsModels[0].id;
},
);
export const selectFirstBoardIdByProjectId = makeSelectFirstBoardIdByProjectId();
export const makeSelectNotificationsTotalByProjectId = () =>
createSelector(
orm,
(_, id) => id,
(state) => selectCurrentUserId(state),
({ Project, User }, id, currentUserId) => {
const projectModel = Project.withId(id);
if (!projectModel) {
return projectModel;
}
const currentUserModel = User.withId(currentUserId);
const boardsModels = projectModel.getBoardsModelArrayAvailableForUser(currentUserModel);
return boardsModels.reduce(
(result, boardModel) => result + boardModel.getUnreadNotificationsQuerySet().count(),
0,
);
},
);
export const selectNotificationsTotalByProjectId = makeSelectNotificationsTotalByProjectId();
export const makeSelectIsProjectWithIdAvailableForCurrentUser = () =>
createSelector(
orm,
(_, id) => id,
(state) => selectCurrentUserId(state),
({ Project, User }, id, currentUserId) => {
const projectModel = Project.withId(id);
if (!projectModel) {
return false;
}
const currentUserModel = User.withId(currentUserId);
return projectModel.isAvailableForUser(currentUserModel);
},
);
export const selectIsProjectWithIdAvailableForCurrentUser =
makeSelectIsProjectWithIdAvailableForCurrentUser();
export const makeSelectIsProjectWithIdExternalAccessibleForCurrentUser = () =>
createSelector(
orm,
(_, id) => id,
(state) => selectCurrentUserId(state),
({ Project, User }, id, currentUserId) => {
const projectModel = Project.withId(id);
if (!projectModel) {
return false;
}
const currentUserModel = User.withId(currentUserId);
return projectModel.isExternalAccessibleForUser(currentUserModel);
},
);
export const selectIsProjectWithIdExternalAccessibleForCurrentUser =
makeSelectIsProjectWithIdExternalAccessibleForCurrentUser();
export const selectCurrentProject = createSelector(
orm,
(state) => selectPath(state).projectId,
@@ -26,8 +159,7 @@ export const selectCurrentProject = createSelector(
export const selectManagersForCurrentProject = createSelector(
orm,
(state) => selectPath(state).projectId,
(state) => selectCurrentUserId(state),
({ Project }, id, currentUserId) => {
({ Project }, id) => {
if (!id) {
return id;
}
@@ -39,24 +171,20 @@ export const selectManagersForCurrentProject = createSelector(
}
return projectModel
.getOrderedManagersQuerySet()
.getManagersQuerySet()
.toModelArray()
.map((projectManagerModel) => ({
...projectManagerModel.ref,
isPersisted: !isLocalId(projectManagerModel.id),
user: {
...projectManagerModel.user.ref,
isCurrent: projectManagerModel.user.id === currentUserId,
},
user: projectManagerModel.user.ref,
}));
},
);
export const selectBoardsForCurrentProject = createSelector(
export const selectManagerUserIdsForCurrentProject = createSelector(
orm,
(state) => selectPath(state).projectId,
(state) => selectCurrentUserId(state),
({ Project }, id, currentUserId) => {
({ Project }, id) => {
if (!id) {
return id;
}
@@ -68,14 +196,101 @@ export const selectBoardsForCurrentProject = createSelector(
}
return projectModel
.getOrderedBoardsModelArrayAvailableForUser(currentUserId)
.map((boardModel) => ({
...boardModel.ref,
isPersisted: !isLocalId(boardModel.id),
.getManagersQuerySet()
.toRefArray()
.map((projectManager) => projectManager.userId);
},
);
export const selectBackgroundImageIdsForCurrentProject = createSelector(
orm,
(state) => selectPath(state).projectId,
({ Project }, id) => {
if (!id) {
return id;
}
const projectModel = Project.withId(id);
if (!projectModel) {
return projectModel;
}
return projectModel
.getBackgroundImagesQuerySet()
.toRefArray()
.map((backgroundImage) => backgroundImage.id);
},
);
export const selectBaseCustomFieldGroupIdsForCurrentProject = createSelector(
orm,
(state) => selectPath(state).projectId,
({ Project }, id) => {
if (!id) {
return id;
}
const projectModel = Project.withId(id);
if (!projectModel) {
return projectModel;
}
return projectModel
.getBaseCustomFieldGroupsQuerySet()
.toRefArray()
.map((baseCustomFieldGroup) => baseCustomFieldGroup.id);
},
);
export const selectBaseCustomFieldGroupsForCurrentProject = createSelector(
orm,
(state) => selectPath(state).projectId,
({ Project }, id) => {
if (!id) {
return id;
}
const projectModel = Project.withId(id);
if (!projectModel) {
return projectModel;
}
return projectModel
.getBaseCustomFieldGroupsQuerySet()
.toRefArray()
.map((baseCustomFieldGroup) => ({
...baseCustomFieldGroup,
isPersisted: !isLocalId(baseCustomFieldGroup.id),
}));
},
);
export const selectBoardIdsForCurrentProject = createSelector(
orm,
(state) => selectPath(state).projectId,
(state) => selectCurrentUserId(state),
({ Project, User }, id, currentUserId) => {
if (!id) {
return id;
}
const projectModel = Project.withId(id);
if (!projectModel) {
return projectModel;
}
const currentUserModel = User.withId(currentUserId);
return projectModel
.getBoardsModelArrayAvailableForUser(currentUserModel)
.map((boardModel) => boardModel.id);
},
);
export const selectIsCurrentUserManagerForCurrentProject = createSelector(
orm,
(state) => selectPath(state).projectId,
@@ -91,13 +306,29 @@ export const selectIsCurrentUserManagerForCurrentProject = createSelector(
return false;
}
return projectModel.hasManagerForUser(currentUserId);
return projectModel.hasManagerWithUserId(currentUserId);
},
);
export default {
makeSelectProjectById,
selectProjectById,
makeSelectBoardIdsByProjectId,
selectBoardIdsByProjectId,
makeSelectFirstBoardIdByProjectId,
selectFirstBoardIdByProjectId,
makeSelectNotificationsTotalByProjectId,
selectNotificationsTotalByProjectId,
makeSelectIsProjectWithIdAvailableForCurrentUser,
selectIsProjectWithIdAvailableForCurrentUser,
makeSelectIsProjectWithIdExternalAccessibleForCurrentUser,
selectIsProjectWithIdExternalAccessibleForCurrentUser,
selectCurrentProject,
selectManagersForCurrentProject,
selectBoardsForCurrentProject,
selectManagerUserIdsForCurrentProject,
selectBackgroundImageIdsForCurrentProject,
selectBaseCustomFieldGroupIdsForCurrentProject,
selectBaseCustomFieldGroupsForCurrentProject,
selectBoardIdsForCurrentProject,
selectIsCurrentUserManagerForCurrentProject,
};

View File

@@ -1,11 +0,0 @@
export const selectIsInitializing = ({ root: { isInitializing } }) => isInitializing;
export const selectConfig = ({ root: { config } }) => config;
export const selectOidcConfig = (state) => selectConfig(state).oidc;
export default {
selectIsInitializing,
selectConfig,
selectOidcConfig,
};

View File

@@ -1,3 +1,8 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createSelector as createReselectSelector } from 'reselect';
import { createSelector as createReduxOrmSelector } from 'redux-orm';
@@ -20,13 +25,15 @@ export const selectPath = createReduxOrmSelector(
orm,
selectPathsMatch,
(state) => selectCurrentUserId(state),
({ Project, Board, Card }, pathsMatch, currentUserId) => {
({ User, Project, Board, Card }, pathsMatch, currentUserId) => {
if (pathsMatch) {
const currentUserModel = User.withId(currentUserId);
switch (pathsMatch.pattern.path) {
case Paths.PROJECTS: {
const projectModel = Project.withId(pathsMatch.params.id);
if (!projectModel || !projectModel.isAvailableForUser(currentUserId)) {
if (!projectModel || !projectModel.isAvailableForUser(currentUserModel)) {
return {
projectId: null,
};
@@ -39,7 +46,7 @@ export const selectPath = createReduxOrmSelector(
case Paths.BOARDS: {
const boardModel = Board.withId(pathsMatch.params.id);
if (!boardModel || !boardModel.isAvailableForUser(currentUserId)) {
if (!boardModel || !boardModel.isAvailableForUser(currentUserModel)) {
return {
boardId: null,
projectId: null,
@@ -54,7 +61,7 @@ export const selectPath = createReduxOrmSelector(
case Paths.CARDS: {
const cardModel = Card.withId(pathsMatch.params.id);
if (!cardModel || !cardModel.isAvailableForUser(currentUserId)) {
if (!cardModel || !cardModel.isAvailableForUser(currentUserModel)) {
return {
cardId: null,
boardId: null,

View File

@@ -1,5 +0,0 @@
export const selectIsSocketDisconnected = ({ socket: { isDisconnected } }) => isDisconnected;
export default {
selectIsSocketDisconnected,
};

View File

@@ -0,0 +1,53 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createSelector } from 'redux-orm';
import orm from '../orm';
import { isLocalId } from '../utils/local-id';
export const makeSelectTaskListById = () =>
createSelector(
orm,
(_, id) => id,
({ TaskList }, id) => {
const taskListModel = TaskList.withId(id);
if (!taskListModel) {
return taskListModel;
}
return {
...taskListModel.ref,
isPersisted: !isLocalId(taskListModel.id),
};
},
);
export const selectTaskListById = makeSelectTaskListById();
export const makeSelectTasksByTaskListId = () =>
createSelector(
orm,
(_, id) => id,
({ TaskList }, id) => {
const taskListModel = TaskList.withId(id);
if (!taskListModel) {
return taskListModel;
}
return taskListModel.getTasksQuerySet().toRefArray();
},
);
export const selectTasksByTaskListId = makeSelectTasksByTaskListId();
export default {
makeSelectTaskListById,
selectTaskListById,
makeSelectTasksByTaskListId,
selectTasksByTaskListId,
};

View File

@@ -1,6 +1,12 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createSelector } from 'redux-orm';
import orm from '../orm';
import { isLocalId } from '../utils/local-id';
export const makeSelectTaskById = () =>
createSelector(
@@ -13,7 +19,10 @@ export const makeSelectTaskById = () =>
return taskModel;
}
return taskModel.ref;
return {
...taskModel.ref,
isPersisted: !isLocalId(taskModel.id),
};
},
);

View File

@@ -1,24 +1,75 @@
/*!
* Copyright (c) 2024 PLANKA Software GmbH
* Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
*/
import { createSelector } from 'redux-orm';
import orm from '../orm';
import {
selectIsFavoritesEnabled,
selectIsHiddenProjectsVisible,
selectProjectsOrder,
selectProjectsSearch,
} from './core';
import { isLocalId } from '../utils/local-id';
import { isUserAdminOrProjectOwner } from '../utils/record-helpers';
import { STATIC_USER_BY_ID } from '../constants/StaticUsers';
import { BoardMembershipRoles, ProjectGroups, ProjectOrders } from '../constants/Enums';
const ORDER_BY_ARGS_BY_PROJECTS_ORDER = {
[ProjectOrders.ALPHABETICALLY]: [['name', 'id.length', 'id']],
[ProjectOrders.BY_CREATION_TIME]: [['id', 'id.length']],
};
export const selectCurrentUserId = ({ auth: { userId } }) => userId;
export const selectUsers = createSelector(orm, ({ User }) =>
User.getOrderedUndeletedQuerySet().toRefArray(),
);
export const makeSelectUserById = () =>
createSelector(
orm,
(_, id) => id,
({ User }, id) => {
if (STATIC_USER_BY_ID[id]) {
return STATIC_USER_BY_ID[id];
}
const userModel = User.withId(id);
if (!userModel) {
return userModel;
}
return userModel.ref;
},
);
export const selectUserById = makeSelectUserById();
export const selectUsersExceptCurrent = createSelector(
orm,
(state) => selectCurrentUserId(state),
({ User }, id) =>
User.getOrderedUndeletedQuerySet()
User.getAllQuerySet()
.exclude({
id,
})
.toRefArray(),
);
export const selectActiveUsers = createSelector(orm, ({ User }) =>
User.getActiveQuerySet().toRefArray(),
);
export const selectActiveUsersTotal = createSelector(orm, ({ User }) =>
User.getActiveQuerySet().count(),
);
export const selectActiveAdminOrProjectOwnerUsers = createSelector(orm, ({ User }) =>
User.getActiveQuerySet()
.filter((user) => isUserAdminOrProjectOwner(user))
.toRefArray(),
);
export const selectCurrentUser = createSelector(
orm,
(state) => selectCurrentUserId(state),
@@ -37,7 +88,7 @@ export const selectCurrentUser = createSelector(
},
);
export const selectProjectsForCurrentUser = createSelector(
export const selectProjectIdsForCurrentUser = createSelector(
orm,
(state) => selectCurrentUserId(state),
({ User }, id) => {
@@ -51,26 +102,102 @@ export const selectProjectsForCurrentUser = createSelector(
return userModel;
}
return userModel.getOrderedAvailableProjectsModelArray().map((projectModel) => {
const boardsModels = projectModel.getOrderedBoardsModelArrayAvailableForUser(userModel.id);
let notificationsTotal = 0;
boardsModels.forEach((boardModel) => {
boardModel.cards.toModelArray().forEach((cardModel) => {
notificationsTotal += cardModel.getUnreadNotificationsQuerySet().count();
});
});
return {
...projectModel.ref,
notificationsTotal,
firstBoardId: boardsModels[0] && boardsModels[0].id,
};
});
return userModel.getProjectsModelArray().map((projectModel) => projectModel.id);
},
);
export const selectProjectsToListsForCurrentUser = createSelector(
export const selectFilteredProjectIdsForCurrentUser = createSelector(
orm,
(state) => selectCurrentUserId(state),
(state) => selectProjectsSearch(state),
(state) => selectIsHiddenProjectsVisible(state),
(state) => selectProjectsOrder(state),
({ User }, id, projectsSearch, isHiddenProjectsVisible, projectsOrder) => {
if (!id) {
return id;
}
const userModel = User.withId(id);
if (!userModel) {
return userModel;
}
return userModel
.getFilteredProjectsModelArray(
projectsSearch,
isHiddenProjectsVisible,
ORDER_BY_ARGS_BY_PROJECTS_ORDER[projectsOrder],
)
.map((projectModel) => projectModel.id);
},
);
export const selectFilteredProjctIdsByGroupForCurrentUser = createSelector(
orm,
(state) => selectCurrentUserId(state),
(state) => selectProjectsSearch(state),
(state) => selectIsHiddenProjectsVisible(state),
(state) => selectProjectsOrder(state),
({ User }, id, projectsSearch, isHiddenProjectsVisible, projectsOrder) => {
if (!id) {
return id;
}
const userModel = User.withId(id);
if (!userModel) {
return userModel;
}
const { managerProjectModels, membershipProjectModels, adminProjectModels } =
userModel.getFilteredSeparatedProjectsModelArray(
projectsSearch,
isHiddenProjectsVisible,
ORDER_BY_ARGS_BY_PROJECTS_ORDER[projectsOrder],
);
return managerProjectModels.reduce(
(result, projectModel) => {
const group = projectModel.ownerProjectManager ? ProjectGroups.MY_OWN : ProjectGroups.TEAM;
result[group].push(projectModel.id);
return result;
},
{
[ProjectGroups.MY_OWN]: [],
[ProjectGroups.TEAM]: [],
[ProjectGroups.SHARED_WITH_ME]: membershipProjectModels.map(
(projectModel) => projectModel.id,
),
[ProjectGroups.OTHERS]: adminProjectModels.map((projectModel) => projectModel.id),
},
);
},
);
export const selectFavoriteProjectIdsForCurrentUser = createSelector(
orm,
(state) => selectCurrentUserId(state),
(state) => selectProjectsOrder(state),
({ User }, id, projectsOrder) => {
if (!id) {
return id;
}
const userModel = User.withId(id);
if (!userModel) {
return userModel;
}
return userModel
.getFavoriteProjectsModelArray(ORDER_BY_ARGS_BY_PROJECTS_ORDER[projectsOrder])
.map((projectModel) => projectModel.id);
},
);
export const selectProjectsToListsWithEditorRightsForCurrentUser = createSelector(
orm,
(state) => selectCurrentUserId(state),
({ User }, id) => {
@@ -84,17 +211,31 @@ export const selectProjectsToListsForCurrentUser = createSelector(
return userModel;
}
return userModel.getOrderedAvailableProjectsModelArray().map((projectModel) => ({
return userModel.getMembershipProjectsModelArray().map((projectModel) => ({
...projectModel.ref,
boards: projectModel.getOrderedBoardsModelArrayForUser(id).map((boardModel) => ({
...boardModel.ref,
lists: boardModel.getOrderedListsQuerySet().toRefArray(),
})),
boards: projectModel.getBoardsModelArrayForUserWithId(id).flatMap((boardModel) => {
const boardMembersipModel = boardModel.getMembershipModelByUserId(id);
if (boardMembersipModel.role !== BoardMembershipRoles.EDITOR) {
return [];
}
return {
...boardModel.ref,
lists: boardModel
.getListsQuerySet()
.toRefArray()
.map((list) => ({
...list,
isPersisted: !isLocalId(list.id),
})),
};
}),
}));
},
);
export const selectNotificationsForCurrentUser = createSelector(
export const selectBoardIdsForCurrentUser = createSelector(
orm,
(state) => selectCurrentUserId(state),
({ User }, id) => {
@@ -109,25 +250,93 @@ export const selectNotificationsForCurrentUser = createSelector(
}
return userModel
.getOrderedUnreadNotificationsQuerySet()
.toModelArray()
.map((notificationModel) => ({
...notificationModel.ref,
activity: notificationModel.activity && {
...notificationModel.activity.ref,
user: notificationModel.activity.user.ref,
},
card: notificationModel.card && notificationModel.card.ref,
}));
.getProjectsModelArray()
.flatMap((projectModel) =>
projectModel
.getBoardsModelArrayAvailableForUser(userModel)
.map((boardModel) => boardModel.id),
);
},
);
export const selectNotificationIdsForCurrentUser = createSelector(
orm,
(state) => selectCurrentUserId(state),
({ User }, id) => {
if (!id) {
return id;
}
const userModel = User.withId(id);
if (!userModel) {
return userModel;
}
return userModel
.getUnreadNotificationsQuerySet()
.toRefArray()
.map((notification) => notification.id);
},
);
export const selectNotificationServiceIdsForCurrentUser = createSelector(
orm,
(state) => selectCurrentUserId(state),
({ User }, id) => {
if (!id) {
return id;
}
const userModel = User.withId(id);
if (!userModel) {
return userModel;
}
return userModel
.getNotificationServicesQuerySet()
.toRefArray()
.map((notificationService) => notificationService.id);
},
);
export const selectIsFavoritesActiveForCurrentUser = createSelector(
orm,
(state) => selectCurrentUserId(state),
(state) => selectIsFavoritesEnabled(state),
({ User }, id, isFavoritesEnabled) => {
if (!id) {
return false;
}
const userModel = User.withId(id);
if (!userModel) {
return false;
}
// TODO: use selectFavoriteProjectIdsForCurrentUser instead
return isFavoritesEnabled && userModel.getFavoriteProjectsModelArray().length > 0;
},
);
export default {
makeSelectUserById,
selectUserById,
selectCurrentUserId,
selectUsers,
selectUsersExceptCurrent,
selectActiveUsers,
selectActiveUsersTotal,
selectActiveAdminOrProjectOwnerUsers,
selectCurrentUser,
selectProjectsForCurrentUser,
selectProjectsToListsForCurrentUser,
selectNotificationsForCurrentUser,
selectProjectIdsForCurrentUser,
selectFilteredProjectIdsForCurrentUser,
selectFilteredProjctIdsByGroupForCurrentUser,
selectFavoriteProjectIdsForCurrentUser,
selectProjectsToListsWithEditorRightsForCurrentUser,
selectBoardIdsForCurrentUser,
selectNotificationIdsForCurrentUser,
selectNotificationServiceIdsForCurrentUser,
selectIsFavoritesActiveForCurrentUser,
};