mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 22:57:21 +01:00
Push Notifications Fixes - Part 2 (#12092)
* push notifications: handle some more error codes and when the user is loaded using .lean() * fix lint * do not send push notification if message is missing
This commit is contained in:
@@ -301,7 +301,7 @@
|
|||||||
<h4 class="popover-content-title">{{ item.text }}</h4>
|
<h4 class="popover-content-title">{{ item.text }}</h4>
|
||||||
<questInfo
|
<questInfo
|
||||||
:quest="item"
|
:quest="item"
|
||||||
:popoverVersion="true"
|
:popover-version="true"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { model as User } from '../models/user'; // eslint-disable-line import/no-cycle
|
import { model as User } from '../models/user'; // eslint-disable-line import/no-cycle
|
||||||
import { getUserInfo } from './email'; // eslint-disable-line import/no-cycle
|
import { getUserInfo } from './email'; // eslint-disable-line import/no-cycle
|
||||||
import { sendNotification as sendPushNotification } from './pushNotifications';
|
import { sendNotification as sendPushNotification } from './pushNotifications'; // eslint-disable-line import/no-cycle
|
||||||
|
|
||||||
export async function getAuthorEmailFromMessage (message) {
|
export async function getAuthorEmailFromMessage (message) {
|
||||||
const authorId = message.uuid;
|
const authorId = message.uuid;
|
||||||
@@ -30,6 +30,9 @@ export async function sendChatPushNotifications (user, group, message, mentions,
|
|||||||
if (mentions && mentions.includes(`@${member.auth.local.username}`) && member.preferences.pushNotifications.mentionParty !== false) {
|
if (mentions && mentions.includes(`@${member.auth.local.username}`) && member.preferences.pushNotifications.mentionParty !== false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!message.unformattedText) return;
|
||||||
|
|
||||||
sendPushNotification(
|
sendPushNotification(
|
||||||
member,
|
member,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { mapInboxMessage, inboxModel as Inbox } from '../../models/message';
|
import { mapInboxMessage, inboxModel as Inbox } from '../../models/message';
|
||||||
import { getUserInfo, sendTxn as sendTxnEmail } from '../email'; // eslint-disable-line import/no-cycle
|
import { getUserInfo, sendTxn as sendTxnEmail } from '../email'; // eslint-disable-line import/no-cycle
|
||||||
import { sendNotification as sendPushNotification } from '../pushNotifications';
|
import { sendNotification as sendPushNotification } from '../pushNotifications'; // eslint-disable-line import/no-cycle
|
||||||
|
|
||||||
const PM_PER_PAGE = 10;
|
const PM_PER_PAGE = 10;
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ export async function sentMessage (sender, receiver, message, translate) {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (receiver.preferences.pushNotifications.newPM !== false) {
|
if (receiver.preferences.pushNotifications.newPM !== false && messageSent.unformattedText) {
|
||||||
sendPushNotification(
|
sendPushNotification(
|
||||||
receiver,
|
receiver,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { // eslint-disable-line import/no-cycle
|
|||||||
getUserInfo,
|
getUserInfo,
|
||||||
sendTxn as txnEmail,
|
sendTxn as txnEmail,
|
||||||
} from '../email';
|
} from '../email';
|
||||||
import { sendNotification as sendPushNotification } from '../pushNotifications';
|
import { sendNotification as sendPushNotification } from '../pushNotifications'; // eslint-disable-line import/no-cycle
|
||||||
import shared from '../../../common';
|
import shared from '../../../common';
|
||||||
|
|
||||||
function getGiftMessage (data, byUsername, gemAmount, language) {
|
function getGiftMessage (data, byUsername, gemAmount, language) {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
NotFound,
|
NotFound,
|
||||||
} from '../errors';
|
} from '../errors';
|
||||||
import shared from '../../../common';
|
import shared from '../../../common';
|
||||||
import { sendNotification as sendPushNotification } from '../pushNotifications';
|
import { sendNotification as sendPushNotification } from '../pushNotifications'; // eslint-disable-line import/no-cycle
|
||||||
|
|
||||||
// @TODO: Abstract to shared/constant
|
// @TODO: Abstract to shared/constant
|
||||||
const JOINED_GROUP_PLAN = 'joined group plan';
|
const JOINED_GROUP_PLAN = 'joined group plan';
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ import nconf from 'nconf';
|
|||||||
import apn from 'apn';
|
import apn from 'apn';
|
||||||
import gcmLib from 'node-gcm'; // works with FCM notifications too
|
import gcmLib from 'node-gcm'; // works with FCM notifications too
|
||||||
import logger from './logger';
|
import logger from './logger';
|
||||||
|
import { // eslint-disable-line import/no-cycle
|
||||||
|
model as User,
|
||||||
|
} from '../models/user';
|
||||||
|
|
||||||
const FCM_API_KEY = nconf.get('PUSH_CONFIGS_FCM_SERVER_API_KEY');
|
const FCM_API_KEY = nconf.get('PUSH_CONFIGS_FCM_SERVER_API_KEY');
|
||||||
const fcmSender = FCM_API_KEY ? new gcmLib.Sender(FCM_API_KEY) : undefined;
|
const fcmSender = FCM_API_KEY ? new gcmLib.Sender(FCM_API_KEY) : undefined;
|
||||||
@@ -18,7 +21,7 @@ const apnProvider = APN_ENABLED ? new apn.Provider({
|
|||||||
}) : undefined;
|
}) : undefined;
|
||||||
|
|
||||||
function removePushDevice (user, pushDevice) {
|
function removePushDevice (user, pushDevice) {
|
||||||
return user.update({
|
return User.update({ _id: user._id }, {
|
||||||
$pull: { pushDevices: { regId: pushDevice.regId } },
|
$pull: { pushDevices: { regId: pushDevice.regId } },
|
||||||
}).exec().catch(err => {
|
}).exec().catch(err => {
|
||||||
logger.error(err, `Error removing pushDevice ${pushDevice.regId} for user ${user._id}`);
|
logger.error(err, `Error removing pushDevice ${pushDevice.regId} for user ${user._id}`);
|
||||||
@@ -80,10 +83,18 @@ export function sendNotification (user, details = {}) {
|
|||||||
// The regId is not valid anymore, remove it
|
// The regId is not valid anymore, remove it
|
||||||
if (failed === 'NotRegistered') {
|
if (failed === 'NotRegistered') {
|
||||||
removePushDevice(user, pushDevice);
|
removePushDevice(user, pushDevice);
|
||||||
logger.error(new Error('FCM error, invalid pushDevice'), {
|
logger.error(new Error('FCM error, unregistered pushDevice'), {
|
||||||
response, regId: pushDevice.regId, userId: user._id,
|
regId: pushDevice.regId, userId: user._id,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
// An invalid token was registered by mistake
|
||||||
|
// Remove it but log the error differently so that it can be distinguished
|
||||||
|
// from when failed === NotRegistered
|
||||||
|
// Blacklisted can happen in some rare cases,
|
||||||
|
// see https://stackoverflow.com/questions/42136122/why-does-firebase-push-token-return-blacklisted
|
||||||
|
if (failed === 'InvalidRegistration' || pushDevice.regId === 'BLACKLISTED') {
|
||||||
|
removePushDevice(user, pushDevice);
|
||||||
|
}
|
||||||
logger.error(new Error('FCM error'), {
|
logger.error(new Error('FCM error'), {
|
||||||
response, regId: pushDevice.regId, userId: user._id,
|
response, regId: pushDevice.regId, userId: user._id,
|
||||||
});
|
});
|
||||||
@@ -110,7 +121,7 @@ export function sendNotification (user, details = {}) {
|
|||||||
// Handle failed push notifications deliveries
|
// Handle failed push notifications deliveries
|
||||||
response.failed.forEach(failure => {
|
response.failed.forEach(failure => {
|
||||||
if (failure.error) { // generic error
|
if (failure.error) { // generic error
|
||||||
logger.error(new Error('APN error'), {
|
logger.error(new Error('Unhandled APN error'), {
|
||||||
response, regId: pushDevice.regId, userId: user._id,
|
response, regId: pushDevice.regId, userId: user._id,
|
||||||
});
|
});
|
||||||
} else { // rejected
|
} else { // rejected
|
||||||
@@ -119,10 +130,16 @@ export function sendNotification (user, details = {}) {
|
|||||||
const { reason } = failure.response;
|
const { reason } = failure.response;
|
||||||
if (reason === 'Unregistered') {
|
if (reason === 'Unregistered') {
|
||||||
removePushDevice(user, pushDevice);
|
removePushDevice(user, pushDevice);
|
||||||
logger.error(new Error('APN error, invalid pushDevice'), {
|
logger.error(new Error('APN error, unregistered pushDevice'), {
|
||||||
response, regId: pushDevice.regId, userId: user._id,
|
regId: pushDevice.regId, userId: user._id,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
if (reason === 'BadDeviceToken') {
|
||||||
|
// An invalid token was registered by mistake
|
||||||
|
// Remove it but log the error differently so that it can be distinguished
|
||||||
|
// from when reason === Unregistered
|
||||||
|
removePushDevice(user, pushDevice);
|
||||||
|
}
|
||||||
logger.error(new Error('APN error'), {
|
logger.error(new Error('APN error'), {
|
||||||
response, regId: pushDevice.regId, userId: user._id,
|
response, regId: pushDevice.regId, userId: user._id,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { // eslint-disable-line import/no-cycle
|
|||||||
import { removeFromArray } from '../libs/collectionManipulators';
|
import { removeFromArray } from '../libs/collectionManipulators';
|
||||||
import shared from '../../common';
|
import shared from '../../common';
|
||||||
import { sendTxn as txnEmail } from '../libs/email'; // eslint-disable-line import/no-cycle
|
import { sendTxn as txnEmail } from '../libs/email'; // eslint-disable-line import/no-cycle
|
||||||
import { sendNotification as sendPushNotification } from '../libs/pushNotifications';
|
import { sendNotification as sendPushNotification } from '../libs/pushNotifications'; // eslint-disable-line import/no-cycle
|
||||||
import { syncableAttrs, setNextDue } from '../libs/taskManager';
|
import { syncableAttrs, setNextDue } from '../libs/taskManager';
|
||||||
|
|
||||||
const { Schema } = mongoose;
|
const { Schema } = mongoose;
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import {
|
|||||||
} from '../libs/errors';
|
} from '../libs/errors';
|
||||||
import baseModel from '../libs/baseModel';
|
import baseModel from '../libs/baseModel';
|
||||||
import { sendTxn as sendTxnEmail } from '../libs/email'; // eslint-disable-line import/no-cycle
|
import { sendTxn as sendTxnEmail } from '../libs/email'; // eslint-disable-line import/no-cycle
|
||||||
import { sendNotification as sendPushNotification } from '../libs/pushNotifications';
|
import { sendNotification as sendPushNotification } from '../libs/pushNotifications'; // eslint-disable-line import/no-cycle
|
||||||
import {
|
import {
|
||||||
syncableAttrs,
|
syncableAttrs,
|
||||||
} from '../libs/taskManager';
|
} from '../libs/taskManager';
|
||||||
@@ -637,12 +637,15 @@ schema.methods.sendChat = function sendChat (options = {}) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sendPushNotification(member, {
|
|
||||||
identifier: 'chatMention',
|
if (newChatMessage.unformattedText) {
|
||||||
title: `${user.profile.name} mentioned you in ${this.name}`,
|
sendPushNotification(member, {
|
||||||
message: newChatMessage.unformattedText,
|
identifier: 'chatMention',
|
||||||
payload: { type: this.type },
|
title: `${user.profile.name} mentioned you in ${this.name}`,
|
||||||
});
|
message: newChatMessage.unformattedText,
|
||||||
|
payload: { type: this.type },
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return newChatMessage;
|
return newChatMessage;
|
||||||
|
|||||||
Reference in New Issue
Block a user