Skip to content

Commit aea9c87

Browse files
committed
Adds support for badging on iOS
1 parent c7503fc commit aea9c87

File tree

2 files changed

+169
-1
lines changed

2 files changed

+169
-1
lines changed

spec/PushController.spec.js

+118
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
var PushController = require('../src/Controllers/PushController').PushController;
22

3+
var Config = require('../src/Config');
4+
35
describe('PushController', () => {
46
it('can check valid master key of request', (done) => {
57
// Make mock request
@@ -127,5 +129,121 @@ describe('PushController', () => {
127129
}).toThrow();
128130
done();
129131
});
132+
133+
it('properly increment badges', (done) => {
134+
135+
var payload = {
136+
alert: "Hello World!",
137+
badge: "Increment",
138+
}
139+
var installations = [];
140+
while(installations.length != 10) {
141+
var installation = new Parse.Object("_Installation");
142+
installation.set("installationId", "installation_"+installations.length);
143+
installation.set("deviceToken","device_token_"+installations.length)
144+
installation.set("badge", installations.length);
145+
installation.set("originalBadge", installations.length);
146+
installation.set("deviceType", "ios");
147+
installations.push(installation);
148+
}
149+
150+
while(installations.length != 15) {
151+
var installation = new Parse.Object("_Installation");
152+
installation.set("installationId", "installation_"+installations.length);
153+
installation.set("deviceToken","device_token_"+installations.length)
154+
installation.set("deviceType", "android");
155+
installations.push(installation);
156+
}
157+
158+
var pushAdapter = {
159+
send: function(body, installations) {
160+
var badge = body.badge;
161+
installations.forEach((installation) => {
162+
if (installation.deviceType == "ios") {
163+
expect(installation.badge).toEqual(badge);
164+
expect(installation.originalBadge+1).toEqual(installation.badge);
165+
} else {
166+
expect(installation.badge).toBeUndefined();
167+
}
168+
})
169+
return Promise.resolve({
170+
body: body,
171+
installations: installations
172+
})
173+
},
174+
getValidPushTypes: function() {
175+
return ["ios", "android"];
176+
}
177+
}
178+
179+
var config = new Config(Parse.applicationId);
180+
var auth = {
181+
isMaster: true
182+
}
183+
184+
var pushController = new PushController(pushAdapter, Parse.applicationId);
185+
Parse.Object.saveAll(installations).then((installations) => {
186+
return pushController.sendPush(payload, {}, config, auth);
187+
}).then((result) => {
188+
done();
189+
}, (err) => {
190+
console.error(err);
191+
fail("should not fail");
192+
done();
193+
});
194+
195+
});
196+
197+
it('properly set badges to 1', (done) => {
198+
199+
var payload = {
200+
alert: "Hello World!",
201+
badge: 1,
202+
}
203+
var installations = [];
204+
while(installations.length != 10) {
205+
var installation = new Parse.Object("_Installation");
206+
installation.set("installationId", "installation_"+installations.length);
207+
installation.set("deviceToken","device_token_"+installations.length)
208+
installation.set("badge", installations.length);
209+
installation.set("originalBadge", installations.length);
210+
installation.set("deviceType", "ios");
211+
installations.push(installation);
212+
}
213+
214+
var pushAdapter = {
215+
send: function(body, installations) {
216+
var badge = body.badge;
217+
installations.forEach((installation) => {
218+
expect(installation.badge).toEqual(badge);
219+
expect(1).toEqual(installation.badge);
220+
})
221+
return Promise.resolve({
222+
body: body,
223+
installations: installations
224+
})
225+
},
226+
getValidPushTypes: function() {
227+
return ["ios"];
228+
}
229+
}
230+
231+
var config = new Config(Parse.applicationId);
232+
var auth = {
233+
isMaster: true
234+
}
235+
236+
var pushController = new PushController(pushAdapter, Parse.applicationId);
237+
Parse.Object.saveAll(installations).then((installations) => {
238+
return pushController.sendPush(payload, {}, config, auth);
239+
}).then((result) => {
240+
done();
241+
}, (err) => {
242+
console.error(err);
243+
fail("should not fail");
244+
done();
245+
});
246+
247+
})
130248

131249
});

src/Controllers/PushController.js

+51-1
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ import PromiseRouter from '../PromiseRouter';
33
import rest from '../rest';
44
import AdaptableController from './AdaptableController';
55
import { PushAdapter } from '../Adapters/Push/PushAdapter';
6+
import deepcopy from 'deepcopy';
67
import features from '../features';
78

89
const FEATURE_NAME = 'push';
10+
const UNSUPPORTED_BADGE_KEY = "unsupported";
911

1012
export class PushController extends AdaptableController {
1113

@@ -58,7 +60,55 @@ export class PushController extends AdaptableController {
5860
body['expiration_time'] = PushController.getExpirationTime(body);
5961
// TODO: If the req can pass the checking, we return immediately instead of waiting
6062
// pushes to be sent. We probably change this behaviour in the future.
61-
rest.find(config, auth, '_Installation', where).then(function(response) {
63+
let badgeUpdate = Promise.resolve();
64+
65+
if (body.badge) {
66+
var op = {};
67+
if (body.badge == "Increment") {
68+
op = {'$inc': {'badge': 1}}
69+
} else if (Number(body.badge)) {
70+
op = {'$set': {'badge': body.badge } }
71+
} else {
72+
throw "Invalid value for badge, expected number or 'Increment'";
73+
}
74+
let updateWhere = deepcopy(where);
75+
76+
// Only on iOS!
77+
updateWhere.deviceType = 'ios';
78+
79+
// Raw update to bypass the hooks
80+
badgeUpdate = config.database.rawCollection("_Installation").then((coll) => {
81+
return coll.update(updateWhere, op, { multi: true });
82+
});
83+
}
84+
85+
return badgeUpdate.then(() => {
86+
return rest.find(config, auth, '_Installation', where)
87+
}).then((response) => {
88+
if (body.badge && body.badge == "Increment") {
89+
// Collect the badges to reduce the # of calls
90+
let badgeInstallationsMap = response.results.reduce((map, installation) => {
91+
let badge = installation.badge;
92+
if (installation.deviceType != "ios") {
93+
badge = UNSUPPORTED_BADGE_KEY;
94+
}
95+
map[badge] = map[badge] || [];
96+
map[badge].push(installation);
97+
return map;
98+
}, {});
99+
100+
// Map the on the badges count and return the send result
101+
let promises = Object.keys(badgeInstallationsMap).map((badge) => {
102+
let payload = deepcopy(body);
103+
if (badge == UNSUPPORTED_BADGE_KEY) {
104+
delete payload.badge;
105+
} else {
106+
payload.badge = parseInt(badge);
107+
}
108+
return pushAdapter.send(payload, badgeInstallationsMap[badge]);
109+
});
110+
return Promise.all(promises);
111+
}
62112
return pushAdapter.send(body, response.results);
63113
});
64114
}

0 commit comments

Comments
 (0)