Skip to content

Commit c62eb3c

Browse files
committed
Fix FCM custom data handling
1 parent 4282462 commit c62eb3c

File tree

3 files changed

+76
-6
lines changed

3 files changed

+76
-6
lines changed

src/utils/fcmMessage.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ class FcmMessage {
2727
}
2828

2929
static buildAndroidMessage(params, options) {
30-
const message = buildGcmMessage(params, options);
30+
// Mark as FCM so buildGcmMessage doesn't pollute custom data
31+
const fcmOptions = { ...options, fcm: true };
32+
const message = buildGcmMessage(params, fcmOptions);
3133

3234
const androidMessage = message.toJson();
3335

src/utils/tools.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,15 @@ const buildGcmMessage = (data, options) => {
124124
};
125125
}
126126

127-
custom.title = custom.title || data.title;
128-
custom.message = custom.message || data.body;
129-
custom.sound = custom.sound || data.sound;
130-
custom.icon = custom.icon || data.icon;
131-
custom.msgcnt = custom.msgcnt || data.badge;
127+
// Only add notification fields to custom data for GCM (not FCM)
128+
// FCM uses separate notification and data fields
129+
if (!options.fcm) {
130+
custom.title = custom.title || data.title;
131+
custom.message = custom.message || data.body;
132+
custom.sound = custom.sound || data.sound;
133+
custom.icon = custom.icon || data.icon;
134+
custom.msgcnt = custom.msgcnt || data.badge;
135+
}
132136
if (options.phonegap === true && data.contentAvailable) {
133137
custom["content-available"] = 1;
134138
}

test/send/sendFCM.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,68 @@ describe("push-notifications-fcm", () => {
7575
.catch(done);
7676
});
7777
});
78+
79+
describe('send push notifications with custom data', () => {
80+
const customDataMessage = {
81+
title: 'Notification Title',
82+
body: 'Notification Body',
83+
custom: {
84+
userId: '12345',
85+
actionId: 'action-001',
86+
deepLink: 'app://section/item',
87+
},
88+
};
89+
90+
let customDataSendMethod;
91+
92+
function sendCustomDataMethod() {
93+
return sinon.stub(
94+
fbMessaging.prototype,
95+
'sendEachForMulticast',
96+
function sendFCMWithCustomData(firebaseMessage) {
97+
const { custom } = customDataMessage;
98+
99+
// Verify custom data is preserved in top-level data field
100+
expect(firebaseMessage.data).to.deep.equal(custom);
101+
102+
// Verify custom data does NOT pollute the notification
103+
// Note: normalizeDataParams converts all values to strings (FCM requirement)
104+
expect(firebaseMessage.android.data).to.deep.equal(custom);
105+
expect(firebaseMessage.android.data).to.not.have.property('title');
106+
expect(firebaseMessage.android.data).to.not.have.property('body');
107+
108+
// Verify notification has proper fields (separate from data)
109+
expect(firebaseMessage.android.notification).to.include({
110+
title: customDataMessage.title,
111+
body: customDataMessage.body,
112+
});
113+
114+
return Promise.resolve({
115+
successCount: 1,
116+
failureCount: 0,
117+
responses: [{ error: null }],
118+
});
119+
}
120+
);
121+
}
122+
123+
before(() => {
124+
customDataSendMethod = sendCustomDataMethod();
125+
});
126+
127+
after(() => {
128+
customDataSendMethod.restore();
129+
});
130+
131+
it('custom data should be preserved and not mixed with notification fields', (done) => {
132+
pn.send(regIds, customDataMessage)
133+
.then((results) => {
134+
expect(results).to.be.an('array');
135+
expect(results[0].method).to.equal('fcm');
136+
expect(results[0].success).to.equal(1);
137+
done();
138+
})
139+
.catch(done);
140+
});
141+
});
78142
});

0 commit comments

Comments
 (0)