Skip to content

Commit 2b827f9

Browse files
Merge pull request #5700 from Countly/SER-2105-push-bring-back-content-available
[SER-2105] Push: brought back content-availabe property for ios
2 parents 477ae05 + 5d9c1a3 commit 2b827f9

File tree

8 files changed

+217
-156
lines changed

8 files changed

+217
-156
lines changed

plugins/push/api/api.js

+11-10
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ plugins.setConfigs(FEATURE_NAME, {
6363
pool_pushes: 400, // object mode streams high water mark
6464
pool_bytes: 10000, // bytes mode streams high water mark
6565
pool_concurrency: 5, // max number of same type connections
66-
pool_pools: 10 // max number of connections in total
66+
pool_pools: 10, // max number of connections in total
67+
default_content_available: false, // sets content-available: 1 by default for ios
6768
});
6869

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

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

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

358359
/**
359360
* @apiDefine PushMessageBody
360-
*
361+
*
361362
* @apiBody {ObjectID} app Application ID
362363
* @apiBody {String[]} platforms Array of platforms to send to
363364
* @apiBody {String="draft"} [status] Message status, only set to draft when creating or editing a draft message, don't set otherwise
@@ -376,9 +377,9 @@ plugins.register('/i/app_users/export', ({app_id, uids, export_commands, dbargs,
376377
* @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
377378
* @apiBody {Boolean} [triggers.reschedule] [only for event, cohort triggers] Allow rescheduling to next day if it's too late to send on scheduled day
378379
* @apiBody {Number} [triggers.delay] [only for event, cohort triggers] Milliseconds to delay sending of event or cohort message
379-
* @apiBody {Number} [triggers.cap] [only for event, cohort & api triggers] Set maximum number of notifications sent to a particular user
380+
* @apiBody {Number} [triggers.cap] [only for event, cohort & api triggers] Set maximum number of notifications sent to a particular user
380381
* @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)
381-
* @apiBody {String[]} [triggers.events] [only for event trigger] Event keys
382+
* @apiBody {String[]} [triggers.events] [only for event trigger] Event keys
382383
* @apiBody {String[]} [triggers.cohorts] [only for cohort trigger] Cohort ids
383384
* @apiBody {Boolean} [triggers.entry] [only for cohort trigger] Send on cohort entry (true) or exit (false)
384385
* @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
@@ -405,7 +406,7 @@ plugins.register('/i/app_users/export', ({app_id, uids, export_commands, dbargs,
405406

406407
/**
407408
* @apiDefine PushMessage
408-
*
409+
*
409410
* @apiSuccess {ObjectID} _id Message ID
410411
* @apiSuccess {ObjectID} app Application ID
411412
* @apiSuccess {String[]} platforms Array of platforms to send to
@@ -426,9 +427,9 @@ plugins.register('/i/app_users/export', ({app_id, uids, export_commands, dbargs,
426427
* @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
427428
* @apiSuccess {Boolean} [triggers.reschedule] [only for event, cohort triggers] Allow rescheduling to next day if it's too late to send on scheduled day
428429
* @apiSuccess {Number} [triggers.delay] [only for event, cohort triggers] Milliseconds to delay sending of event or cohort message
429-
* @apiSuccess {Number} [triggers.cap] [only for event, cohort & api triggers] Set maximum number of notifications sent to a particular user
430+
* @apiSuccess {Number} [triggers.cap] [only for event, cohort & api triggers] Set maximum number of notifications sent to a particular user
430431
* @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)
431-
* @apiSuccess {String[]} [triggers.events] [only for event trigger] Event keys
432+
* @apiSuccess {String[]} [triggers.events] [only for event trigger] Event keys
432433
* @apiSuccess {String[]} [triggers.cohorts] [only for cohort trigger] Cohort ids
433434
* @apiSuccess {Boolean} [triggers.entry] [only for cohort trigger] Send on cohort entry (true) or exit (false)
434435
* @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
@@ -457,7 +458,7 @@ plugins.register('/i/app_users/export', ({app_id, uids, export_commands, dbargs,
457458
* @apiSuccess {Object} [result.sent] Number notifications sent successfully
458459
* @apiSuccess {Object} [result.actioned] Number notifications with positive user reactions (notification taps & button clicks)
459460
* @apiSuccess {Object} [result.errored] Number notifications which weren't sent due to various errors
460-
* @apiSuccess {Object[]} [result.lastErrors] Array of last 10 errors
461+
* @apiSuccess {Object[]} [result.lastErrors] Array of last 10 errors
461462
* @apiSuccess {Object[]} [result.lastRuns] Array of last 10 sending runs
462463
* @apiSuccess {Date} [result.next] Next sending date
463464
* @apiSuccess {Object} [result.subs] Sub results - a map of subresult key to Result object. Subresults are used to store platform and locale specific results.

plugins/push/api/send/platforms/i.js

+21-1
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,9 @@ const map = {
505505
template.result.aps.alert = template.result.aps.alert || {};
506506
template.result.aps.alert.subtitle = specific.subtitle;
507507
}
508+
if (specific.setContentAvailable) {
509+
template.result.aps["content-available"] = 1;
510+
}
508511
}
509512
},
510513
};
@@ -549,6 +552,8 @@ class APN extends Base {
549552
':method': 'POST',
550553
':scheme': 'https',
551554
':authority': authority,
555+
// this is being added before the actual request depending on weather message have setContentAvailable
556+
// "apns-priority": 5,
552557
[HTTP2.sensitiveHeaders]: ['authorization', ':path', 'apns-id', 'apns-expiration', 'apns-collapse-id']
553558
};
554559
this.headersSecondWithToken = token => {
@@ -660,7 +665,22 @@ class APN extends Base {
660665
}
661666

662667
let content = this.template(p.m).compile(p),
663-
stream = this.session.request(this.headersSecondWithToken(p.t)),
668+
reqHeaders = self.headersSecondWithToken(p.t);
669+
// find if we need to add the priority header (check if content-available set)
670+
delete reqHeaders["apns-priority"];
671+
const message = self.messages[p.m];
672+
if (message && Array.isArray(message.contents)) {
673+
const contentItem = message.contents.find(cont => Array.isArray(cont.specific));
674+
if (contentItem) {
675+
const obj = contentItem.specific.find(cont => cont.setContentAvailable !== undefined);
676+
if (obj && obj.setContentAvailable) {
677+
reqHeaders["apns-priority"] = 5;
678+
}
679+
}
680+
}
681+
// =======0========000=================0========000=================0========0
682+
console.log(JSON.stringify(reqHeaders, null, 2), JSON.stringify(content, null, 2));
683+
let stream = this.session.request(reqHeaders),
664684
status,
665685
data = '';
666686
stream.on('error', err => {

plugins/push/frontend/public/javascripts/countly.models.js

+23-3
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,8 @@
246246
badgeNumber: "",
247247
onClickURL: "",
248248
json: null,
249-
userData: []
249+
userData: [],
250+
setContentAvailable: false,
250251
},
251252
android: {
252253
mediaURL: "",
@@ -1157,9 +1158,19 @@
11571158
};
11581159
},
11591160
mapIOSSettings: function(iosSettingsDto) {
1161+
let subtitle, setContentAvailable;
1162+
if (iosSettingsDto && Array.isArray(iosSettingsDto.specific)) {
1163+
let subtitleItem = iosSettingsDto.specific.find(i => i.subtitle !== undefined);
1164+
if (subtitleItem) {
1165+
subtitle = countlyPushNotification.helper.decodeMessage(subtitleItem.subtitle || "");
1166+
}
1167+
let contentAvailableItem = iosSettingsDto.specific.find(i => i.setContentAvailable !== undefined);
1168+
setContentAvailable = contentAvailableItem.setContentAvailable;
1169+
}
11601170
return {
11611171
// NOte: subtitle will reside at index zero for now. There are no other platform specifics
1162-
subtitle: iosSettingsDto && iosSettingsDto.specific && iosSettingsDto.specific[0] && countlyPushNotification.helper.decodeMessage(iosSettingsDto.specific[0].subtitle || ""),
1172+
subtitle,
1173+
setContentAvailable,
11631174
soundFilename: iosSettingsDto && iosSettingsDto.sound || "",
11641175
badgeNumber: iosSettingsDto && iosSettingsDto.badge && iosSettingsDto.badge.toString(),
11651176
json: iosSettingsDto && iosSettingsDto.data || null,
@@ -1756,12 +1767,21 @@
17561767
result.url = countlyCommon.decodeHtml(iosSettings.onClickURL);
17571768
}
17581769
if (iosSettings.subtitle && options.settings[PlatformEnum.IOS].isSubtitleEnabled) {
1759-
result.specific = [{subtitle: iosSettings.subtitle}];
1770+
if (!result.specific) {
1771+
result.specific = [];
1772+
}
1773+
result.specific.push({ subtitle: iosSettings.subtitle });
17601774
}
17611775
if (model.settings[PlatformEnum.IOS].mediaURL && options.settings[PlatformEnum.IOS].isMediaURLEnabled && model.messageType === MessageTypeEnum.CONTENT) {
17621776
result.media = countlyCommon.decodeHtml(model.settings[PlatformEnum.IOS].mediaURL);
17631777
result.mediaMime = model.settings[PlatformEnum.IOS].mediaMime;
17641778
}
1779+
if (options.settings[PlatformEnum.IOS].isContentAvailableSet) {
1780+
if (!result.specific) {
1781+
result.specific = [];
1782+
}
1783+
result.specific.push({ setContentAvailable: options.settings[PlatformEnum.IOS].isContentAvailableSet });
1784+
}
17651785
return result;
17661786
},
17671787
mapAndroidSettings: function(model, options) {

plugins/push/frontend/public/javascripts/countly.views.js

+15-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* eslint-disable no-console */
2-
/* global countlyVue,app,CV,countlyPushNotification,countlyPushNotificationComponent,CountlyHelpers,countlyCommon,countlyGlobal,countlyAuth,countlyGraphNotesCommon*/
2+
/* global countlyVue,countlyPlugins,app,CV,countlyPushNotification,countlyPushNotificationComponent,CountlyHelpers,countlyCommon,countlyGlobal,countlyAuth,countlyGraphNotesCommon*/
33

44
(function() {
55

@@ -51,6 +51,7 @@
5151
isOnClickURLEnabled: false,
5252
isJsonEnabled: false,
5353
isUserDataEnabled: false,
54+
isContentAvailableSet: false,
5455
},
5556
android: {
5657
isMediaURLEnabled: false,
@@ -134,7 +135,7 @@
134135
selectedLocalizationFilter: countlyPushNotification.service.DEFAULT_LOCALIZATION_VALUE,
135136
isConfirmed: false,
136137
expandedPlatformSettings: [],
137-
settings: JSON.parse(JSON.stringify(InitialPushNotificationDrawerSettingsState)),
138+
settings: this.getInitialPushNotificationDrawerSettingsState(),
138139
userPropertiesIdCounter: 0,
139140
selectedUserPropertyId: null,
140141
isAddUserPropertyPopoverOpen: {
@@ -331,6 +332,14 @@
331332
}
332333
},
333334
methods: {
335+
getInitialPushNotificationDrawerSettingsState: function() {
336+
const _InitialPushNotificationDrawerSettingsState = JSON.parse(JSON.stringify(InitialPushNotificationDrawerSettingsState));
337+
const settings = countlyPlugins.getConfigsData();
338+
if (settings.push && settings.push.default_content_available) {
339+
_InitialPushNotificationDrawerSettingsState.ios.isContentAvailableSet = true;
340+
}
341+
return _InitialPushNotificationDrawerSettingsState;
342+
},
334343
previewCohorts: function(cohorts) {
335344
var selectedCohorts = this.cohortOptions.filter(function(cohort) {
336345
return cohorts.some(function(selectedCohortId) {
@@ -646,7 +655,7 @@
646655
title: false,
647656
content: false
648657
};
649-
this.settings = JSON.parse(JSON.stringify(InitialPushNotificationDrawerSettingsState));
658+
this.settings = this.getInitialPushNotificationDrawerSettingsState();
650659
this.pushNotificationUnderEdit = JSON.parse(JSON.stringify(countlyPushNotification.helper.getInitialModel(this.type)));
651660
},
652661
onClose: function() {
@@ -1046,6 +1055,7 @@
10461055
this.settings[this.PlatformEnum.IOS].isJsonEnabled = Boolean(this.pushNotificationUnderEdit.settings[this.PlatformEnum.IOS].json);
10471056
this.settings[this.PlatformEnum.IOS].isUserDataEnabled = Boolean(this.pushNotificationUnderEdit.settings[this.PlatformEnum.IOS].userData.length);
10481057
this.settings[this.PlatformEnum.IOS].isSubtitleEnabled = Boolean(this.pushNotificationUnderEdit.settings[this.PlatformEnum.IOS].subtitle);
1058+
this.settings[this.PlatformEnum.IOS].isContentAvailableSet = Boolean(this.pushNotificationUnderEdit.settings[this.PlatformEnum.IOS].setContentAvailable);
10491059
}
10501060
},
10511061
updateAndroidPlatformSettingsStateIfFound: function() {
@@ -2796,7 +2806,7 @@
27962806

27972807

27982808
/**
2799-
*
2809+
*
28002810
* @returns {Object} container data with create new message event handler
28012811
*/
28022812
function getCreateNewMessageEventContainerData() {
@@ -2812,7 +2822,7 @@
28122822
};
28132823
}
28142824
/**
2815-
*
2825+
*
28162826
* @returns {Object} container data with push notification drawer
28172827
*/
28182828
function getDrawerContainerData() {

plugins/push/frontend/public/localization/push.properties

+5-2
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,8 @@ push-notification.media-url-platform-description = Add the URL for media specifi
201201
push-notification.subtitle = Subtitle
202202
push-notification.enter-your-subtitle = Enter your subtitle
203203
push-notification.subtitle-description = Add a subheading for your message.
204+
push-notification.set-content-available = Set content-available
205+
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
204206
push-notification.on-click-url = On Click URL
205207
push-notification.enter-on-click-url = Enter on click URL
206208
push-notification.on-click-url-description = Add URL link that is opened when user taps a message in drawer.
@@ -307,7 +309,7 @@ push-notification-details.delivery-sub-header = Delivery
307309
push-notification-details.delivery-type = Delivery type
308310
push-notification-details.scheduled-for = Scheduled for
309311
push-notification-details.expiration-time = Expiration time
310-
push-notification-details.message-expires-after = Message expires after {0} day(s) and {1} hour(s)
312+
push-notification-details.message-expires-after = Message expires after {0} day(s) and {1} hour(s)
311313
push-notification-details.no-errors-found = No errors were found
312314
push-notification.users-targeted = Users Targeted
313315
push-notification.users-targeted-description = Total number of users targeted to receive the selected notification.
@@ -389,6 +391,7 @@ push.pool_pushes = Number of notifications in stream batches
389391
push.pool_bytes = Bytes in binary stream batches
390392
push.pool_concurrency = Maximum number of same type connections
391393
push.pool_pools = Maximum number of connections in total
394+
push.default_content_available = Set content-available to 1 by default for IOS
392395

393396
#Drawer from other views
394397
push-notification.send-message-to-users = Send message to users
@@ -414,7 +417,7 @@ push-notification.error-code.ExpiredCreds.desc = Push Notification credentials h
414417
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.
415418
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.
416419
push-notification.error-code.DeviceTokenNotForTopic.desc = APNS certificate doesn't correspond to the Bundle ID of your application.
417-
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.
420+
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.
418421
push-notification.error-code.InvalidProviderToken.desc = Please check your Auth key file, Team ID & Bundle ID - some of those is invalid.
419422
push-notification.error-code.MissingRegistration.desc = Please contact customer support.
420423
push-notification.error-code.InvalidRegistration.desc = Probably you modified the way SDK handles FCM tokens. Please ensure you do it right or contact support.

0 commit comments

Comments
 (0)