Skip to content

Commit

Permalink
Merge pull request #5700 from Countly/SER-2105-push-bring-back-conten…
Browse files Browse the repository at this point in the history
…t-available

[SER-2105] Push: brought back content-availabe property for ios
  • Loading branch information
ArtursKadikis authored Oct 22, 2024
2 parents 477ae05 + 5d9c1a3 commit 2b827f9
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 156 deletions.
21 changes: 11 additions & 10 deletions plugins/push/api/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ plugins.setConfigs(FEATURE_NAME, {
pool_pushes: 400, // object mode streams high water mark
pool_bytes: 10000, // bytes mode streams high water mark
pool_concurrency: 5, // max number of same type connections
pool_pools: 10 // max number of connections in total
pool_pools: 10, // max number of connections in total
default_content_available: false, // sets content-available: 1 by default for ios
});

plugins.internalEvents.push('[CLY]_push_sent');
Expand Down Expand Up @@ -217,7 +218,7 @@ plugins.register('/i', async ob => {

/**
* Handy function for handling api calls (see apis obj above)
*
*
* @param {object} apisObj apis.i or apis.o
* @param {object} ob object from pluginManager ({params, qstring, ...})
* @returns {boolean} true if the call has been handled
Expand Down Expand Up @@ -264,7 +265,7 @@ function apiCall(apisObj, ob) {

/**
* Wrap push endpoint catching any push-specific errors from it
*
*
* @param {string} method endpoint name
* @param {function} fn actual endpoint returning a promise
* @returns {function} CRUD callback
Expand Down Expand Up @@ -357,7 +358,7 @@ plugins.register('/i/app_users/export', ({app_id, uids, export_commands, dbargs,

/**
* @apiDefine PushMessageBody
*
*
* @apiBody {ObjectID} app Application ID
* @apiBody {String[]} platforms Array of platforms to send to
* @apiBody {String="draft"} [status] Message status, only set to draft when creating or editing a draft message, don't set otherwise
Expand All @@ -376,9 +377,9 @@ plugins.register('/i/app_users/export', ({app_id, uids, export_commands, dbargs,
* @apiBody {Number} [triggers.time] [only for event, cohort triggers] Time in ms since 00:00 in case event or cohort message is to be sent in users' timezones
* @apiBody {Boolean} [triggers.reschedule] [only for event, cohort triggers] Allow rescheduling to next day if it's too late to send on scheduled day
* @apiBody {Number} [triggers.delay] [only for event, cohort triggers] Milliseconds to delay sending of event or cohort message
* @apiBody {Number} [triggers.cap] [only for event, cohort & api triggers] Set maximum number of notifications sent to a particular user
* @apiBody {Number} [triggers.cap] [only for event, cohort & api triggers] Set maximum number of notifications sent to a particular user
* @apiBody {Number} [triggers.sleep] [only for event, cohort & api triggers] Set minimum time in ms between two notifications for a particular user (a notification is discarded if it's less than that)
* @apiBody {String[]} [triggers.events] [only for event trigger] Event keys
* @apiBody {String[]} [triggers.events] [only for event trigger] Event keys
* @apiBody {String[]} [triggers.cohorts] [only for cohort trigger] Cohort ids
* @apiBody {Boolean} [triggers.entry] [only for cohort trigger] Send on cohort entry (true) or exit (false)
* @apiBody {Boolean} [triggers.cancels] [only for cohort trigger] A notification is to be discarded if user exits cohort (when entry = true) before notification is sent
Expand All @@ -405,7 +406,7 @@ plugins.register('/i/app_users/export', ({app_id, uids, export_commands, dbargs,

/**
* @apiDefine PushMessage
*
*
* @apiSuccess {ObjectID} _id Message ID
* @apiSuccess {ObjectID} app Application ID
* @apiSuccess {String[]} platforms Array of platforms to send to
Expand All @@ -426,9 +427,9 @@ plugins.register('/i/app_users/export', ({app_id, uids, export_commands, dbargs,
* @apiSuccess {Number} [triggers.time] [only for event, cohort triggers] Time in ms since 00:00 in case event or cohort message is to be sent in users' timezones
* @apiSuccess {Boolean} [triggers.reschedule] [only for event, cohort triggers] Allow rescheduling to next day if it's too late to send on scheduled day
* @apiSuccess {Number} [triggers.delay] [only for event, cohort triggers] Milliseconds to delay sending of event or cohort message
* @apiSuccess {Number} [triggers.cap] [only for event, cohort & api triggers] Set maximum number of notifications sent to a particular user
* @apiSuccess {Number} [triggers.cap] [only for event, cohort & api triggers] Set maximum number of notifications sent to a particular user
* @apiSuccess {Number} [triggers.sleep] [only for event, cohort & api triggers] Set minimum time in ms between two notifications for a particular user (a notification is discarded if it's less than that)
* @apiSuccess {String[]} [triggers.events] [only for event trigger] Event keys
* @apiSuccess {String[]} [triggers.events] [only for event trigger] Event keys
* @apiSuccess {String[]} [triggers.cohorts] [only for cohort trigger] Cohort ids
* @apiSuccess {Boolean} [triggers.entry] [only for cohort trigger] Send on cohort entry (true) or exit (false)
* @apiSuccess {Boolean} [triggers.cancels] [only for cohort trigger] A notification is to be discarded if user exits cohort (when entry = true) before notification is sent
Expand Down Expand Up @@ -457,7 +458,7 @@ plugins.register('/i/app_users/export', ({app_id, uids, export_commands, dbargs,
* @apiSuccess {Object} [result.sent] Number notifications sent successfully
* @apiSuccess {Object} [result.actioned] Number notifications with positive user reactions (notification taps & button clicks)
* @apiSuccess {Object} [result.errored] Number notifications which weren't sent due to various errors
* @apiSuccess {Object[]} [result.lastErrors] Array of last 10 errors
* @apiSuccess {Object[]} [result.lastErrors] Array of last 10 errors
* @apiSuccess {Object[]} [result.lastRuns] Array of last 10 sending runs
* @apiSuccess {Date} [result.next] Next sending date
* @apiSuccess {Object} [result.subs] Sub results - a map of subresult key to Result object. Subresults are used to store platform and locale specific results.
Expand Down
22 changes: 21 additions & 1 deletion plugins/push/api/send/platforms/i.js
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,9 @@ const map = {
template.result.aps.alert = template.result.aps.alert || {};
template.result.aps.alert.subtitle = specific.subtitle;
}
if (specific.setContentAvailable) {
template.result.aps["content-available"] = 1;
}
}
},
};
Expand Down Expand Up @@ -549,6 +552,8 @@ class APN extends Base {
':method': 'POST',
':scheme': 'https',
':authority': authority,
// this is being added before the actual request depending on weather message have setContentAvailable
// "apns-priority": 5,
[HTTP2.sensitiveHeaders]: ['authorization', ':path', 'apns-id', 'apns-expiration', 'apns-collapse-id']
};
this.headersSecondWithToken = token => {
Expand Down Expand Up @@ -660,7 +665,22 @@ class APN extends Base {
}

let content = this.template(p.m).compile(p),
stream = this.session.request(this.headersSecondWithToken(p.t)),
reqHeaders = self.headersSecondWithToken(p.t);
// find if we need to add the priority header (check if content-available set)
delete reqHeaders["apns-priority"];
const message = self.messages[p.m];
if (message && Array.isArray(message.contents)) {
const contentItem = message.contents.find(cont => Array.isArray(cont.specific));
if (contentItem) {
const obj = contentItem.specific.find(cont => cont.setContentAvailable !== undefined);
if (obj && obj.setContentAvailable) {
reqHeaders["apns-priority"] = 5;
}
}
}
// =======0========000=================0========000=================0========0
console.log(JSON.stringify(reqHeaders, null, 2), JSON.stringify(content, null, 2));
let stream = this.session.request(reqHeaders),
status,
data = '';
stream.on('error', err => {
Expand Down
26 changes: 23 additions & 3 deletions plugins/push/frontend/public/javascripts/countly.models.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,8 @@
badgeNumber: "",
onClickURL: "",
json: null,
userData: []
userData: [],
setContentAvailable: false,
},
android: {
mediaURL: "",
Expand Down Expand Up @@ -1157,9 +1158,19 @@
};
},
mapIOSSettings: function(iosSettingsDto) {
let subtitle, setContentAvailable;
if (iosSettingsDto && Array.isArray(iosSettingsDto.specific)) {
let subtitleItem = iosSettingsDto.specific.find(i => i.subtitle !== undefined);
if (subtitleItem) {
subtitle = countlyPushNotification.helper.decodeMessage(subtitleItem.subtitle || "");
}
let contentAvailableItem = iosSettingsDto.specific.find(i => i.setContentAvailable !== undefined);
setContentAvailable = contentAvailableItem.setContentAvailable;
}
return {
// NOte: subtitle will reside at index zero for now. There are no other platform specifics
subtitle: iosSettingsDto && iosSettingsDto.specific && iosSettingsDto.specific[0] && countlyPushNotification.helper.decodeMessage(iosSettingsDto.specific[0].subtitle || ""),
subtitle,
setContentAvailable,
soundFilename: iosSettingsDto && iosSettingsDto.sound || "",
badgeNumber: iosSettingsDto && iosSettingsDto.badge && iosSettingsDto.badge.toString(),
json: iosSettingsDto && iosSettingsDto.data || null,
Expand Down Expand Up @@ -1756,12 +1767,21 @@
result.url = countlyCommon.decodeHtml(iosSettings.onClickURL);
}
if (iosSettings.subtitle && options.settings[PlatformEnum.IOS].isSubtitleEnabled) {
result.specific = [{subtitle: iosSettings.subtitle}];
if (!result.specific) {
result.specific = [];
}
result.specific.push({ subtitle: iosSettings.subtitle });
}
if (model.settings[PlatformEnum.IOS].mediaURL && options.settings[PlatformEnum.IOS].isMediaURLEnabled && model.messageType === MessageTypeEnum.CONTENT) {
result.media = countlyCommon.decodeHtml(model.settings[PlatformEnum.IOS].mediaURL);
result.mediaMime = model.settings[PlatformEnum.IOS].mediaMime;
}
if (options.settings[PlatformEnum.IOS].isContentAvailableSet) {
if (!result.specific) {
result.specific = [];
}
result.specific.push({ setContentAvailable: options.settings[PlatformEnum.IOS].isContentAvailableSet });
}
return result;
},
mapAndroidSettings: function(model, options) {
Expand Down
20 changes: 15 additions & 5 deletions plugins/push/frontend/public/javascripts/countly.views.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable no-console */
/* global countlyVue,app,CV,countlyPushNotification,countlyPushNotificationComponent,CountlyHelpers,countlyCommon,countlyGlobal,countlyAuth,countlyGraphNotesCommon*/
/* global countlyVue,countlyPlugins,app,CV,countlyPushNotification,countlyPushNotificationComponent,CountlyHelpers,countlyCommon,countlyGlobal,countlyAuth,countlyGraphNotesCommon*/

(function() {

Expand Down Expand Up @@ -51,6 +51,7 @@
isOnClickURLEnabled: false,
isJsonEnabled: false,
isUserDataEnabled: false,
isContentAvailableSet: false,
},
android: {
isMediaURLEnabled: false,
Expand Down Expand Up @@ -134,7 +135,7 @@
selectedLocalizationFilter: countlyPushNotification.service.DEFAULT_LOCALIZATION_VALUE,
isConfirmed: false,
expandedPlatformSettings: [],
settings: JSON.parse(JSON.stringify(InitialPushNotificationDrawerSettingsState)),
settings: this.getInitialPushNotificationDrawerSettingsState(),
userPropertiesIdCounter: 0,
selectedUserPropertyId: null,
isAddUserPropertyPopoverOpen: {
Expand Down Expand Up @@ -331,6 +332,14 @@
}
},
methods: {
getInitialPushNotificationDrawerSettingsState: function() {
const _InitialPushNotificationDrawerSettingsState = JSON.parse(JSON.stringify(InitialPushNotificationDrawerSettingsState));
const settings = countlyPlugins.getConfigsData();
if (settings.push && settings.push.default_content_available) {
_InitialPushNotificationDrawerSettingsState.ios.isContentAvailableSet = true;
}
return _InitialPushNotificationDrawerSettingsState;
},
previewCohorts: function(cohorts) {
var selectedCohorts = this.cohortOptions.filter(function(cohort) {
return cohorts.some(function(selectedCohortId) {
Expand Down Expand Up @@ -646,7 +655,7 @@
title: false,
content: false
};
this.settings = JSON.parse(JSON.stringify(InitialPushNotificationDrawerSettingsState));
this.settings = this.getInitialPushNotificationDrawerSettingsState();
this.pushNotificationUnderEdit = JSON.parse(JSON.stringify(countlyPushNotification.helper.getInitialModel(this.type)));
},
onClose: function() {
Expand Down Expand Up @@ -1046,6 +1055,7 @@
this.settings[this.PlatformEnum.IOS].isJsonEnabled = Boolean(this.pushNotificationUnderEdit.settings[this.PlatformEnum.IOS].json);
this.settings[this.PlatformEnum.IOS].isUserDataEnabled = Boolean(this.pushNotificationUnderEdit.settings[this.PlatformEnum.IOS].userData.length);
this.settings[this.PlatformEnum.IOS].isSubtitleEnabled = Boolean(this.pushNotificationUnderEdit.settings[this.PlatformEnum.IOS].subtitle);
this.settings[this.PlatformEnum.IOS].isContentAvailableSet = Boolean(this.pushNotificationUnderEdit.settings[this.PlatformEnum.IOS].setContentAvailable);
}
},
updateAndroidPlatformSettingsStateIfFound: function() {
Expand Down Expand Up @@ -2796,7 +2806,7 @@


/**
*
*
* @returns {Object} container data with create new message event handler
*/
function getCreateNewMessageEventContainerData() {
Expand All @@ -2812,7 +2822,7 @@
};
}
/**
*
*
* @returns {Object} container data with push notification drawer
*/
function getDrawerContainerData() {
Expand Down
7 changes: 5 additions & 2 deletions plugins/push/frontend/public/localization/push.properties
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ push-notification.media-url-platform-description = Add the URL for media specifi
push-notification.subtitle = Subtitle
push-notification.enter-your-subtitle = Enter your subtitle
push-notification.subtitle-description = Add a subheading for your message.
push-notification.set-content-available = Set content-available
push-notification.set-content-available-description = Sets the apns-priority header to 5 and content-available property of request body to 1 for the IOS application to receive notifications while the app is in the background
push-notification.on-click-url = On Click URL
push-notification.enter-on-click-url = Enter on click URL
push-notification.on-click-url-description = Add URL link that is opened when user taps a message in drawer.
Expand Down Expand Up @@ -307,7 +309,7 @@ push-notification-details.delivery-sub-header = Delivery
push-notification-details.delivery-type = Delivery type
push-notification-details.scheduled-for = Scheduled for
push-notification-details.expiration-time = Expiration time
push-notification-details.message-expires-after = Message expires after {0} day(s) and {1} hour(s)
push-notification-details.message-expires-after = Message expires after {0} day(s) and {1} hour(s)
push-notification-details.no-errors-found = No errors were found
push-notification.users-targeted = Users Targeted
push-notification.users-targeted-description = Total number of users targeted to receive the selected notification.
Expand Down Expand Up @@ -389,6 +391,7 @@ push.pool_pushes = Number of notifications in stream batches
push.pool_bytes = Bytes in binary stream batches
push.pool_concurrency = Maximum number of same type connections
push.pool_pools = Maximum number of connections in total
push.default_content_available = Set content-available to 1 by default for IOS

#Drawer from other views
push-notification.send-message-to-users = Send message to users
Expand All @@ -414,7 +417,7 @@ push-notification.error-code.ExpiredCreds.desc = Push Notification credentials h
push-notification.error-code.BadDeviceToken.desc = The push token received from your app by Countly Server was rejected by APNS as invalid. Please make sure you set `pushTestMode` property on the SDK's initial configuration correctly. Also please make sure provisioning profile (Development or Distribution) is valid, bundle ID is correct and entitlements are properly set.
push-notification.error-code.MissingTopic.desc = The server failed to parse the certificate, please ensure you use universal certificate and contact support if you do.
push-notification.error-code.DeviceTokenNotForTopic.desc = APNS certificate doesn't correspond to the Bundle ID of your application.
push-notification.error-code.TopicDisallowed.desc = Sending Push Notifications to this topic is not allowed. Apple rejects sending Push Notifications to this topic. Most probably there is no such app in Certificates, Identifiers & Profiles portal.
push-notification.error-code.TopicDisallowed.desc = Sending Push Notifications to this topic is not allowed. Apple rejects sending Push Notifications to this topic. Most probably there is no such app in Certificates, Identifiers & Profiles portal.
push-notification.error-code.InvalidProviderToken.desc = Please check your Auth key file, Team ID & Bundle ID - some of those is invalid.
push-notification.error-code.MissingRegistration.desc = Please contact customer support.
push-notification.error-code.InvalidRegistration.desc = Probably you modified the way SDK handles FCM tokens. Please ensure you do it right or contact support.
Expand Down
Loading

0 comments on commit 2b827f9

Please sign in to comment.