mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 14:47:53 +01:00
Improve @mention handling (#10872)
* move the update username route to v3 (#10836) * Add API Call to retrieve auto-complete options for usernames * Create links to users profile in chat messages * Begin adding server-side autocomplete to web client * Add Test to opt out of username being searchable * Fix issue with username highlighting * Correctly update message text when using autocomplete * remove old autocomplete component * Improve chat input design * rewrite mongoose migration to avoid using recursion * fixes * select more fields * use lean and .update * fix(tests): correct expects * fix(tests): linting & more expects Also one more tweak for invite validation responsiveness * chore(news): Bailey * chore(i18n): update locales * 4.70.0 * fix(chat): less intrusive highlight and better margins * fix(chat): more width tweakage * feat(content): Oddballs Bundle Also includes one more tweak to @mention text highlighting * chore(sprites): compile * chore(i18n): update locales * 4.71.0 * groupChatReceived webhook fix (#10802) * Moved sendGroupChatReceivedWebhooks to group.sendChat function. * Added test for new functionality. * Set width on .custom-control-label (#10840) Set `width: 100%` on the `.custom-control-label`. Although `overflow-wrap: break-word` is set on the parent `.checklist-item` element, it doesn't seem to take effect unless a width is set on the label. * Very large Guild member counts overflow the badge #10753 (#10812) * Update superagent to the latest version 🚀 (#10848) * fix(package): update superagent to version 4.0.0 * chore(package): update lockfile package-lock.json * fix(chat): prevent duplicate messages, closes #10823 * Fix for #10814, prevent ParallelSave errors (#10852) * fix(group leave): prevent ParallelSave errors while leaving a group with multiple group or challenge tasks * fix typo * move computed-props to methods - refactor mountItem to use the states inside (#10853) * feat(content): Frost Hatching Potions * chore(sprites): compile * chore(i18n): update locales * 4.72.0 * fix(stable): remove progress number from petItem * add two slurs - TRIGGER / CONTENT WARNING: assault, slurs, swearwords, etc * more checks on the item.klass, also added the specialClass checks (#10859) * feat(content): Turkey Day 2018 * chore(sprites): compile * chore(i18n): update locales * 4.73.0 * chore(i18n): update locales * 4.73.1 * feat(footer): always show expanded footer (#10862) * Fixes issue #10857 ("Tags have extra space at the bottom when they should be centered") (#10861) * Fix for #10857 centered category tag text * Fixes #10857 and #10856 display tag markdown. * Attach client to chat messages (#10845) * Attach client to chat messages * Word * Design tweaks * Fix potential error * chore(event): end Thanksgiving tweaks * chore(i18n): update locales * 4.73.2 * Improve chat input design * Fix test errors * Move tier icons import to index * correctly name event variable * Debounce autocomplete calls * optimize mention highlighting * fix failing tests * Fix sending private messages * Cache username autocomplete requests * optimize autocomplete regex * Fix lint error * add optional parameters to limit autocompletion to specific group * Fix non-profile urls not being usable. * Correctly handle autocomplete for public and private guilds * Add check to make sure users don’t search for parties/guilds they are not part of * fix lint error * limit autocomplete results to 5 * fix(mentioning): change default, adapt settings control to checkbox * Improve auto completing * improve username autocomplete * Fix merge issue * remove old code * Send push notifications on mentions * Improve handling for sending mention notifications * Fix lint error * Update schema.js * Fix failing test * Don't send push notification to users who aren't in the party * Remove tributejs from dependencies
This commit is contained in:
18
package-lock.json
generated
18
package-lock.json
generated
@@ -13877,6 +13877,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz",
|
||||||
"integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw=="
|
"integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw=="
|
||||||
},
|
},
|
||||||
|
"is-builtin-module": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
|
||||||
|
"requires": {
|
||||||
|
"builtin-modules": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"is-callable": {
|
"is-callable": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
|
||||||
@@ -25708,6 +25716,11 @@
|
|||||||
"punycode": "^1.4.1"
|
"punycode": "^1.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"tributejs": {
|
||||||
|
"version": "3.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tributejs/-/tributejs-3.4.0.tgz",
|
||||||
|
"integrity": "sha512-BWB2YvfKpa6hZgcP9hKN5/tH3P/Guspn4r+ePgwNpftnQwMb6GVWTUgBpkMtVXkR5dwLLcP/iW87i9C1mp21zQ=="
|
||||||
|
},
|
||||||
"trim-leading-lines": {
|
"trim-leading-lines": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/trim-leading-lines/-/trim-leading-lines-0.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/trim-leading-lines/-/trim-leading-lines-0.1.1.tgz",
|
||||||
@@ -27112,6 +27125,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz",
|
||||||
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw=="
|
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw=="
|
||||||
},
|
},
|
||||||
|
"vue-tribute": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-tribute/-/vue-tribute-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-ThJfdoEjxUBd4izjQ4NqmaCVRz0="
|
||||||
|
},
|
||||||
"vuedraggable": {
|
"vuedraggable": {
|
||||||
"version": "2.23.1",
|
"version": "2.23.1",
|
||||||
"resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.23.1.tgz",
|
||||||
|
|||||||
64
test/api/unit/libs/highlightMentions.js
Normal file
64
test/api/unit/libs/highlightMentions.js
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import {
|
||||||
|
highlightMentions,
|
||||||
|
} from '../../../../website/server/libs/highlightMentions';
|
||||||
|
import mongoose from 'mongoose';
|
||||||
|
|
||||||
|
describe('highlightMentions', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const mockFind = {
|
||||||
|
select () {
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
lean () {
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
exec () {
|
||||||
|
return Promise.resolve([{
|
||||||
|
auth: { local: { username: 'user' } }, _id: '111',
|
||||||
|
}, { auth: { local: { username: 'user2' } }, _id: '222',
|
||||||
|
}, { auth: { local: { username: 'user3' } }, _id: '333',
|
||||||
|
}, { auth: { local: { username: 'user-dash' } }, _id: '444',
|
||||||
|
}, { auth: { local: { username: 'user_underscore' } }, _id: '555',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
sinon.stub(mongoose.Model, 'find').returns(mockFind);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
sinon.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('doesn\'t change text without mentions', async () => {
|
||||||
|
let text = 'some chat text';
|
||||||
|
let result = await highlightMentions(text);
|
||||||
|
expect(result[0]).to.equal(text);
|
||||||
|
});
|
||||||
|
it('highlights existing users', async () => {
|
||||||
|
let text = '@user: message';
|
||||||
|
let result = await highlightMentions(text);
|
||||||
|
expect(result[0]).to.equal('[@user](/profile/111): message');
|
||||||
|
});
|
||||||
|
it('highlights special characters', async () => {
|
||||||
|
let text = '@user-dash: message @user_underscore';
|
||||||
|
let result = await highlightMentions(text);
|
||||||
|
expect(result[0]).to.equal('[@user-dash](/profile/444): message [@user_underscore](/profile/555)');
|
||||||
|
});
|
||||||
|
it('doesn\'t highlight nonexisting users', async () => {
|
||||||
|
let text = '@nouser message';
|
||||||
|
let result = await highlightMentions(text);
|
||||||
|
expect(result[0]).to.equal('@nouser message');
|
||||||
|
});
|
||||||
|
it('highlights multiple existing users', async () => {
|
||||||
|
let text = '@user message (@user2) @user3 @user';
|
||||||
|
let result = await highlightMentions(text);
|
||||||
|
expect(result[0]).to.equal('[@user](/profile/111) message ([@user2](/profile/222)) [@user3](/profile/333) [@user](/profile/111)');
|
||||||
|
});
|
||||||
|
it('doesn\'t highlight more than 5 users', async () => {
|
||||||
|
let text = '@user @user2 @user3 @user4 @user5 @user6';
|
||||||
|
let result = await highlightMentions(text);
|
||||||
|
expect(result[0]).to.equal(text);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -9,7 +9,7 @@ div
|
|||||||
span.mr-1(v-if="msg.username") •
|
span.mr-1(v-if="msg.username") •
|
||||||
span(v-b-tooltip="", :title="msg.timestamp | date") {{ msg.timestamp | timeAgo }}
|
span(v-b-tooltip="", :title="msg.timestamp | date") {{ msg.timestamp | timeAgo }}
|
||||||
span(v-if="msg.client && user.contributor.level >= 4") ({{ msg.client }})
|
span(v-if="msg.client && user.contributor.level >= 4") ({{ msg.client }})
|
||||||
.text(v-html='atHighlight(parseMarkdown(msg.text))')
|
.text(v-html='atHighlight(parseMarkdown(msg.text))', ref='markdownContainer')
|
||||||
.reported(v-if="isMessageReported && (inbox === true)")
|
.reported(v-if="isMessageReported && (inbox === true)")
|
||||||
span(v-once) {{ $t('reportedMessage')}}
|
span(v-once) {{ $t('reportedMessage')}}
|
||||||
br
|
br
|
||||||
@@ -47,7 +47,6 @@ div
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '~client/assets/scss/colors.scss';
|
@import '~client/assets/scss/colors.scss';
|
||||||
@import '~client/assets/scss/tiers.scss';
|
|
||||||
|
|
||||||
.mentioned-icon {
|
.mentioned-icon {
|
||||||
width: 16px;
|
width: 16px;
|
||||||
@@ -217,6 +216,21 @@ export default {
|
|||||||
return 'Message hidden (shadow-muted)';
|
return 'Message hidden (shadow-muted)';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mounted () {
|
||||||
|
const links = this.$refs.markdownContainer.getElementsByTagName('a');
|
||||||
|
for (let i = 0; i < links.length; i++) {
|
||||||
|
const link = links[i];
|
||||||
|
if (links[i].getAttribute('href').startsWith('/profile/')) {
|
||||||
|
links[i].onclick = (ev) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
this.$router.push({ path: link.getAttribute('href')});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.CHAT_FLAG_LIMIT_FOR_HIDING = CHAT_FLAG_LIMIT_FOR_HIDING;
|
||||||
|
this.CHAT_FLAG_FROM_SHADOW_MUTE = CHAT_FLAG_FROM_SHADOW_MUTE;
|
||||||
|
this.$emit('chat-card-mounted', this.msg.id);
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async like () {
|
async like () {
|
||||||
let message = cloneDeep(this.msg);
|
let message = cloneDeep(this.msg);
|
||||||
@@ -279,10 +293,5 @@ export default {
|
|||||||
return habiticaMarkdown.render(String(text));
|
return habiticaMarkdown.render(String(text));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted () {
|
|
||||||
this.CHAT_FLAG_LIMIT_FOR_HIDING = CHAT_FLAG_LIMIT_FOR_HIDING;
|
|
||||||
this.CHAT_FLAG_FROM_SHADOW_MUTE = CHAT_FLAG_FROM_SHADOW_MUTE;
|
|
||||||
this.$emit('chat-card-mounted', this.msg.id);
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -76,7 +76,6 @@
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '~client/assets/scss/colors.scss';
|
@import '~client/assets/scss/colors.scss';
|
||||||
@import '~client/assets/scss/tiers.scss';
|
|
||||||
|
|
||||||
.header-wrap {
|
.header-wrap {
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
|
|||||||
@@ -205,6 +205,10 @@
|
|||||||
"goToSettings": "Go to Settings",
|
"goToSettings": "Go to Settings",
|
||||||
"usernameVerifiedConfirmation": "Your username, <%= username %>, is confirmed!",
|
"usernameVerifiedConfirmation": "Your username, <%= username %>, is confirmed!",
|
||||||
"usernameNotVerified": "Please confirm your username.",
|
"usernameNotVerified": "Please confirm your username.",
|
||||||
"changeUsernameDisclaimer": "We will be transitioning login names to unique, public usernames soon. This username will be used for invitations, @mentions in chat, and messaging.",
|
"changeUsernameDisclaimer": "This username will be used for invitations, @mentions in chat, and messaging.",
|
||||||
"verifyUsernameVeteranPet": "One of these Veteran Pets will be waiting for you after you've finished confirming!"
|
"verifyUsernameVeteranPet": "One of these Veteran Pets will be waiting for you after you've finished confirming!",
|
||||||
|
"mentioning": "Mentioning",
|
||||||
|
"suggestMyUsername": "Suggest my username",
|
||||||
|
"everywhere": "Everywhere",
|
||||||
|
"onlyPrivateSpaces": "Only in private spaces"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import guildsAllowingBannedWords from '../../libs/guildsAllowingBannedWords';
|
|||||||
import { getMatchesByWordArray } from '../../libs/stringUtils';
|
import { getMatchesByWordArray } from '../../libs/stringUtils';
|
||||||
import bannedSlurs from '../../libs/bannedSlurs';
|
import bannedSlurs from '../../libs/bannedSlurs';
|
||||||
import apiError from '../../libs/apiError';
|
import apiError from '../../libs/apiError';
|
||||||
|
import {highlightMentions} from '../../libs/highlightMentions';
|
||||||
|
|
||||||
const FLAG_REPORT_EMAILS = nconf.get('FLAG_REPORT_EMAIL').split(',').map((email) => {
|
const FLAG_REPORT_EMAILS = nconf.get('FLAG_REPORT_EMAIL').split(',').map((email) => {
|
||||||
return { email, canSend: true };
|
return { email, canSend: true };
|
||||||
@@ -90,7 +91,6 @@ function getBannedWordsFromText (message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const mentionRegex = new RegExp('\\B@[-\\w]+', 'g');
|
|
||||||
/**
|
/**
|
||||||
* @api {post} /api/v3/groups/:groupId/chat Post chat message to a group
|
* @api {post} /api/v3/groups/:groupId/chat Post chat message to a group
|
||||||
* @apiName PostChat
|
* @apiName PostChat
|
||||||
@@ -183,6 +183,7 @@ api.postChat = {
|
|||||||
throw new NotAuthorized(res.t('messageGroupChatSpam'));
|
throw new NotAuthorized(res.t('messageGroupChatSpam'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [message, mentions, mentionedMembers] = await highlightMentions(req.body.message);
|
||||||
let client = req.headers['x-client'] || '3rd Party';
|
let client = req.headers['x-client'] || '3rd Party';
|
||||||
if (client) {
|
if (client) {
|
||||||
client = client.replace('habitica-', '');
|
client = client.replace('habitica-', '');
|
||||||
@@ -191,7 +192,6 @@ api.postChat = {
|
|||||||
let flagCount = 0;
|
let flagCount = 0;
|
||||||
if (group.privacy === 'public' && user.flags.chatShadowMuted) {
|
if (group.privacy === 'public' && user.flags.chatShadowMuted) {
|
||||||
flagCount = common.constants.CHAT_FLAG_FROM_SHADOW_MUTE;
|
flagCount = common.constants.CHAT_FLAG_FROM_SHADOW_MUTE;
|
||||||
let message = req.body.message;
|
|
||||||
|
|
||||||
// Email the mods
|
// Email the mods
|
||||||
let authorEmail = getUserInfo(user, ['email']).email;
|
let authorEmail = getUserInfo(user, ['email']).email;
|
||||||
@@ -223,7 +223,14 @@ api.postChat = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const newChatMessage = group.sendChat({message: req.body.message, user, flagCount, metaData: null, client, translate: res.t});
|
const newChatMessage = group.sendChat({message: req.body.message,
|
||||||
|
user,
|
||||||
|
flagCount,
|
||||||
|
metaData: null,
|
||||||
|
client,
|
||||||
|
translate: res.t,
|
||||||
|
mentions,
|
||||||
|
mentionedMembers});
|
||||||
let toSave = [newChatMessage.save()];
|
let toSave = [newChatMessage.save()];
|
||||||
|
|
||||||
if (group.type === 'party') {
|
if (group.type === 'party') {
|
||||||
@@ -231,6 +238,7 @@ api.postChat = {
|
|||||||
toSave.push(user.save());
|
toSave.push(user.save());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
await Promise.all(toSave);
|
await Promise.all(toSave);
|
||||||
|
|
||||||
let analyticsObject = {
|
let analyticsObject = {
|
||||||
@@ -242,7 +250,6 @@ api.postChat = {
|
|||||||
headers: req.headers,
|
headers: req.headers,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mentions = req.body.message.match(mentionRegex);
|
|
||||||
if (mentions) {
|
if (mentions) {
|
||||||
analyticsObject.mentionsCount = mentions.length;
|
analyticsObject.mentionsCount = mentions.length;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
import { sendNotification as sendPushNotification } from '../../libs/pushNotifications';
|
import { sendNotification as sendPushNotification } from '../../libs/pushNotifications';
|
||||||
import { achievements } from '../../../../website/common/';
|
import { achievements } from '../../../../website/common/';
|
||||||
import {sentMessage} from '../../libs/inbox';
|
import {sentMessage} from '../../libs/inbox';
|
||||||
|
import {highlightMentions} from '../../libs/highlightMentions';
|
||||||
|
|
||||||
let api = {};
|
let api = {};
|
||||||
|
|
||||||
@@ -633,7 +634,7 @@ api.sendPrivateMessage = {
|
|||||||
if (validationErrors) throw validationErrors;
|
if (validationErrors) throw validationErrors;
|
||||||
|
|
||||||
const sender = res.locals.user;
|
const sender = res.locals.user;
|
||||||
const message = req.body.message;
|
const message = (await highlightMentions(req.body.message))[0];
|
||||||
|
|
||||||
const receiver = await User.findById(req.body.toUserId).exec();
|
const receiver = await User.findById(req.body.toUserId).exec();
|
||||||
if (!receiver) throw new NotFound(res.t('userNotFound'));
|
if (!receiver) throw new NotFound(res.t('userNotFound'));
|
||||||
|
|||||||
@@ -18,15 +18,18 @@ export async function getAuthorEmailFromMessage (message) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function sendChatPushNotifications (user, group, message, translate) {
|
export async function sendChatPushNotifications (user, group, message, mentions, translate) {
|
||||||
let members = await User.find({
|
let members = await User.find({
|
||||||
'party._id': group._id,
|
'party._id': group._id,
|
||||||
_id: {$ne: user._id},
|
_id: {$ne: user._id},
|
||||||
})
|
})
|
||||||
.select('preferences.pushNotifications preferences.language profile.name pushDevices')
|
.select('preferences.pushNotifications preferences.language profile.name pushDevices auth.local.username')
|
||||||
.exec();
|
.exec();
|
||||||
members.forEach(member => {
|
members.forEach(member => {
|
||||||
if (member.preferences.pushNotifications.partyActivity !== false) {
|
if (member.preferences.pushNotifications.partyActivity !== false) {
|
||||||
|
if (mentions && mentions.includes(`@${member.auth.local.username}`) && member.preferences.pushNotifications.mentionParty !== false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
sendPushNotification(
|
sendPushNotification(
|
||||||
member,
|
member,
|
||||||
{
|
{
|
||||||
|
|||||||
23
website/server/libs/highlightMentions.js
Normal file
23
website/server/libs/highlightMentions.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import {model as User} from '../models/user';
|
||||||
|
|
||||||
|
const mentionRegex = new RegExp('\\B@[-\\w]+', 'g');
|
||||||
|
|
||||||
|
export async function highlightMentions (text) {
|
||||||
|
const mentions = text.match(mentionRegex);
|
||||||
|
let members = [];
|
||||||
|
if (mentions !== null && mentions.length <= 5) {
|
||||||
|
const usernames = mentions.map((mention) => {
|
||||||
|
return mention.substr(1);
|
||||||
|
});
|
||||||
|
members = await User
|
||||||
|
.find({'auth.local.username': {$in: usernames}, 'flags.verifiedUsername': true})
|
||||||
|
.select(['auth.local.username', '_id', 'preferences.pushNotifications', 'pushDevices'])
|
||||||
|
.lean()
|
||||||
|
.exec();
|
||||||
|
members.forEach((member) => {
|
||||||
|
const username = member.auth.local.username;
|
||||||
|
text = text.replace(new RegExp(`@${username}(?![\\-\\w])`, 'g'), `[@${username}](/profile/${member._id})`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return [text, mentions, members];
|
||||||
|
}
|
||||||
@@ -513,7 +513,7 @@ schema.methods.getMemberCount = async function getMemberCount () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
schema.methods.sendChat = function sendChat (options = {}) {
|
schema.methods.sendChat = function sendChat (options = {}) {
|
||||||
const {message, user, metaData, client, flagCount = 0, info = {}, translate} = options;
|
const {message, user, metaData, client, flagCount = 0, info = {}, translate, mentions, mentionedMembers} = options;
|
||||||
let newMessage = messageDefaults(message, user, client, flagCount, info);
|
let newMessage = messageDefaults(message, user, client, flagCount, info);
|
||||||
let newChatMessage = new Chat();
|
let newChatMessage = new Chat();
|
||||||
newChatMessage = Object.assign(newChatMessage, newMessage);
|
newChatMessage = Object.assign(newChatMessage, newMessage);
|
||||||
@@ -576,9 +576,31 @@ schema.methods.sendChat = function sendChat (options = {}) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (this.type === 'party' && user) {
|
if (this.type === 'party' && user) {
|
||||||
sendChatPushNotifications(user, this, newChatMessage, translate);
|
sendChatPushNotifications(user, this, newChatMessage, mentions, translate);
|
||||||
|
}
|
||||||
|
if (mentionedMembers) {
|
||||||
|
mentionedMembers.forEach((member) => {
|
||||||
|
if (member._id === user._id) return;
|
||||||
|
const pushNotifPrefs = member.preferences.pushNotifications;
|
||||||
|
if (this.type === 'party') {
|
||||||
|
if (pushNotifPrefs.mentionParty !== true || !this.isMember(member)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (this.isMember(member)) {
|
||||||
|
if (pushNotifPrefs.mentionJoinedGuild !== true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.privacy !== 'public') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (pushNotifPrefs.mentionUnjoinedGuild !== true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sendPushNotification(member, {identifier: 'chatMention', title: `${user.profile.name} mentioned you in ${this.name}`, message});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return newChatMessage;
|
return newChatMessage;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user