diff --git a/website/client/src/assets/scss/form.scss b/website/client/src/assets/scss/form.scss
index 2e5934f5d5..edfb99245e 100644
--- a/website/client/src/assets/scss/form.scss
+++ b/website/client/src/assets/scss/form.scss
@@ -23,7 +23,7 @@ input, textarea, input.form-control, textarea.form-control {
border-radius: 2px;
font-size: 14px;
line-height: 1.43;
- color: $gray-200;
+ color: $gray-50;
border: 1px solid $gray-400;
&:hover:not(:disabled) {
@@ -32,7 +32,6 @@ input, textarea, input.form-control, textarea.form-control {
&:active:not(:disabled), &:focus:not(:disabled) {
border-color: $purple-500;
- color: $gray-50;
outline: 0;
box-shadow: none;
}
diff --git a/website/client/src/components/header/userDropdown.vue b/website/client/src/components/header/userDropdown.vue
index fc0ae7edb5..093868c844 100644
--- a/website/client/src/components/header/userDropdown.vue
+++ b/website/client/src/components/header/userDropdown.vue
@@ -133,13 +133,12 @@
diff --git a/website/client/src/store/actions/user.js b/website/client/src/store/actions/user.js
index 3a98137fed..fa1de5dcc7 100644
--- a/website/client/src/store/actions/user.js
+++ b/website/client/src/store/actions/user.js
@@ -6,6 +6,7 @@ import { togglePinnedItem as togglePinnedItemOp } from '@/../../common/script/op
import changeClassOp from '@/../../common/script/ops/changeClass';
import disableClassesOp from '@/../../common/script/ops/disableClasses';
import openMysteryItemOp from '@/../../common/script/ops/openMysteryItem';
+import markPMSRead from '../../../../common/script/ops/markPMSRead';
export function fetch (store, options = {}) { // eslint-disable-line no-shadow
return loadAsyncResource({
@@ -172,6 +173,11 @@ export function unblock (store, params) {
return axios.post(`/api/v4/user/block/${params.uuid}`);
}
+export function markPrivMessagesRead (store) {
+ markPMSRead(store.state.user.data);
+ return axios.post('/api/v4/user/mark-pms-read');
+}
+
export function newPrivateMessageTo (store, params) {
const { member } = params;
diff --git a/website/common/locales/en/generic.json b/website/common/locales/en/generic.json
index 0a10811c2a..ac5b9c5555 100644
--- a/website/common/locales/en/generic.json
+++ b/website/common/locales/en/generic.json
@@ -296,7 +296,7 @@
"dismissAll": "Dismiss All",
"messages": "Messages",
"emptyMessagesLine1": "You don't have any messages",
- "emptyMessagesLine2": "Send a message to start a conversation!",
+ "emptyMessagesLine2": "You can send a new message to a user by visiting their profile and clicking the \"Message\" button.",
"userSentMessage": "<%= user %> sent you a message",
"letsgo": "Let's Go!",
"selected": "Selected",
diff --git a/website/common/locales/en/groups.json b/website/common/locales/en/groups.json
index ee7280939d..31c8edff5f 100644
--- a/website/common/locales/en/groups.json
+++ b/website/common/locales/en/groups.json
@@ -143,6 +143,9 @@
"PMDisabledOptPopoverText": "Private Messages are disabled. Enable this option to allow users to contact you via your profile.",
"PMDisabledCaptionTitle": "Private Messages are disabled",
"PMDisabledCaptionText": "You can still send messages, but no one can send them to you.",
+ "PMCanNotReply": "You can not reply to this conversation",
+ "PMUserDoesNotReceiveMessages": "This user is no longer receiving private messages",
+ "PMUnblockUserToSendMessages": "Unblock this user to continue sending and receiving messages.",
"block": "Block",
"unblock": "Un-block",
"blockWarning": "Block - This will have no effect if the player is a moderator now or becomes a moderator in future.",
diff --git a/website/server/controllers/api-v4/inbox.js b/website/server/controllers/api-v4/inbox.js
index be5cca6a4d..28843f93e9 100644
--- a/website/server/controllers/api-v4/inbox.js
+++ b/website/server/controllers/api-v4/inbox.js
@@ -93,6 +93,7 @@ api.clearMessages = {
* "text":"last message of conversation",
* "userStyles": {},
* "contributor": {},
+ * "canReceive": true,
* "count":1
* }
* }
diff --git a/website/server/libs/inbox/conversation.methods.js b/website/server/libs/inbox/conversation.methods.js
index b1f20d53fe..72a50111e3 100644
--- a/website/server/libs/inbox/conversation.methods.js
+++ b/website/server/libs/inbox/conversation.methods.js
@@ -2,70 +2,40 @@ import { inboxModel as Inbox, setUserStyles } from '../../models/message';
import { model as User } from '../../models/user';
/**
- * Get the users for conversations
- * 1. Get the user data of last sent message by conversation
- * 2. If the target user hasn't replied yet ( 'sent:true' ) , list user data by users directly
- * @param owner
+ * Get the current user (avatar/setting etc) for conversations
* @param users
* @returns {Promise}
*/
-async function usersMapByConversations (owner, users) {
- const query = Inbox
- .aggregate([
- {
- $match: {
- ownerId: owner._id,
- uuid: { $in: users },
- sent: false, // only messages the other user sent to you
- },
- },
- {
- $group: {
- _id: '$uuid',
- userStyles: { $last: '$userStyles' },
- contributor: { $last: '$contributor' },
- backer: { $last: '$backer' },
- },
- },
- ]);
-
-
- const usersAr = await query.exec();
+async function usersMapByConversations (users) {
const usersMap = {};
- for (const usr of usersAr) {
- usersMap[usr._id] = usr;
- }
+ const usersQuery = {
+ _id: { $in: users },
+ };
- // if a conversation doesn't have a response of the chat-partner,
- // those won't be listed by the query above
- const usersStillNeedToBeLoaded = users.filter(userId => !usersMap[userId]);
+ const loadedUsers = await User.find(usersQuery, {
+ _id: 1,
+ contributor: 1,
+ backer: 1,
+ items: 1,
+ preferences: 1,
+ stats: 1,
+ flags: 1,
+ inbox: 1,
+ }).exec();
- if (usersStillNeedToBeLoaded.length > 0) {
- const usersQuery = {
- _id: { $in: usersStillNeedToBeLoaded },
+ for (const usr of loadedUsers) {
+ const loadedUserConversation = {
+ _id: usr._id,
+ backer: usr.backer,
+ contributor: usr.contributor,
+ optOut: usr.inbox.optOut,
+ blocks: usr.inbox.blocks || [],
};
+ // map user values to conversation properties
+ setUserStyles(loadedUserConversation, usr);
- const loadedUsers = await User.find(usersQuery, {
- _id: 1,
- contributor: 1,
- backer: 1,
- items: 1,
- preferences: 1,
- stats: 1,
- }).exec();
-
- for (const usr of loadedUsers) {
- const loadedUserConversation = {
- _id: usr._id,
- backer: usr.backer,
- contributor: usr.contributor,
- };
- // map user values to conversation properties
- setUserStyles(loadedUserConversation, usr);
-
- usersMap[usr._id] = loadedUserConversation;
- }
+ usersMap[usr._id] = loadedUserConversation;
}
return usersMap;
@@ -98,7 +68,7 @@ export async function listConversations (owner) {
const userIdList = conversationsList.map(c => c._id);
// get user-info based on conversations
- const usersMap = await usersMapByConversations(owner, userIdList);
+ const usersMap = await usersMapByConversations(userIdList);
const conversations = conversationsList.map(res => {
const uuid = res._id;
@@ -109,9 +79,15 @@ export async function listConversations (owner) {
};
if (usersMap[uuid]) {
- conversation.userStyles = usersMap[uuid].userStyles;
- conversation.contributor = usersMap[uuid].contributor;
- conversation.backer = usersMap[uuid].backer;
+ const user = usersMap[uuid];
+
+ conversation.userStyles = user.userStyles;
+ conversation.contributor = user.contributor;
+ conversation.backer = user.backer;
+
+ const isOwnerBlocked = user.blocks.includes(owner._id);
+
+ conversation.canReceive = !(user.optOut || isOwnerBlocked) || owner.isAdmin();
}
return conversation;