@ethora/chat-component-rn 26.10.8 → 26.10.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/lib/commonjs/components/Menu/HeaderRoomListMenu.js +3 -3
- package/lib/commonjs/components/Menu/HeaderRoomListMenu.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/useCallKeep.js +1 -3
- package/lib/commonjs/components/VideoCalls/useCallKeep.js.map +1 -1
- package/lib/commonjs/networking/callTokenStanza.js +13 -1
- package/lib/commonjs/networking/callTokenStanza.js.map +1 -1
- package/lib/commonjs/push/sealedPushTask.js +20 -1
- package/lib/commonjs/push/sealedPushTask.js.map +1 -1
- package/lib/commonjs/version.js +1 -1
- package/lib/module/components/Menu/HeaderRoomListMenu.js +3 -3
- package/lib/module/components/Menu/HeaderRoomListMenu.js.map +1 -1
- package/lib/module/components/VideoCalls/useCallKeep.js +2 -4
- package/lib/module/components/VideoCalls/useCallKeep.js.map +1 -1
- package/lib/module/networking/callTokenStanza.js +13 -1
- package/lib/module/networking/callTokenStanza.js.map +1 -1
- package/lib/module/push/sealedPushTask.js +20 -1
- package/lib/module/push/sealedPushTask.js.map +1 -1
- package/lib/module/version.js +1 -1
- package/lib/typescript/components/Menu/HeaderRoomListMenu.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/useCallKeep.d.ts.map +1 -1
- package/lib/typescript/main.d.ts +1 -1
- package/lib/typescript/main.d.ts.map +1 -1
- package/lib/typescript/networking/callTokenStanza.d.ts.map +1 -1
- package/lib/typescript/push/sealedPushTask.d.ts +13 -0
- package/lib/typescript/push/sealedPushTask.d.ts.map +1 -1
- package/lib/typescript/version.d.ts +1 -1
- package/package.json +1 -1
- package/plugins/notification-service/NotificationService.swift +105 -19
- package/plugins/withNotificationServiceExtension.js +44 -4
- package/src/components/Menu/HeaderRoomListMenu.tsx +13 -8
- package/src/components/VideoCalls/useCallKeep.ts +2 -4
- package/src/main.ts +1 -1
- package/src/networking/callTokenStanza.ts +10 -1
- package/src/push/sealedPushTask.ts +34 -1
- package/src/version.ts +1 -1
|
@@ -5,7 +5,15 @@
|
|
|
5
5
|
* the keychain and shows the real title and text in place of the
|
|
6
6
|
* "New message" placeholder;
|
|
7
7
|
* - shows the attachment thumbnail (photo / video preview) the push service
|
|
8
|
-
* sends as `previewUrl
|
|
8
|
+
* sends as `previewUrl`;
|
|
9
|
+
* - shows the sender's avatar (`image`, unless the app switched it off on
|
|
10
|
+
* the push service) next to the text with the app icon as the small
|
|
11
|
+
* badge: a communication notification. For that the app gets the
|
|
12
|
+
* Communication Notifications capability
|
|
13
|
+
* (com.apple.developer.usernotifications.communication) and
|
|
14
|
+
* INSendMessageIntent in NSUserActivityTypes. With manual signing the
|
|
15
|
+
* App ID needs the capability switched on in the developer portal; EAS
|
|
16
|
+
* and Xcode's automatic signing do that themselves.
|
|
9
17
|
*
|
|
10
18
|
* An extension is a separate target signed with the app, so it cannot ship
|
|
11
19
|
* inside the npm package as code: this plugin writes it into ios/ on every
|
|
@@ -25,11 +33,19 @@
|
|
|
25
33
|
* Options:
|
|
26
34
|
* appleTeamId signing team of the extension; default: ios.appleTeamId,
|
|
27
35
|
* else the team already set on the app target in Xcode.
|
|
36
|
+
* communicationNotifications
|
|
37
|
+
* false leaves the capability out (the avatar is then never
|
|
38
|
+
* shown, pushes look as before); default true.
|
|
28
39
|
*/
|
|
29
40
|
|
|
30
41
|
const fs = require('fs');
|
|
31
42
|
const path = require('path');
|
|
32
|
-
const {
|
|
43
|
+
const {
|
|
44
|
+
withDangerousMod,
|
|
45
|
+
withEntitlementsPlist,
|
|
46
|
+
withInfoPlist,
|
|
47
|
+
withXcodeProject,
|
|
48
|
+
} = require('expo/config-plugins');
|
|
33
49
|
|
|
34
50
|
const TARGET_NAME = 'EthoraNotificationService';
|
|
35
51
|
const SOURCE_DIR = path.join(__dirname, 'notification-service');
|
|
@@ -37,6 +53,8 @@ const SOURCES = ['NotificationService.swift', 'SealedBox.swift'];
|
|
|
37
53
|
const FILES = [...SOURCES, 'Info.plist'];
|
|
38
54
|
const ENTITLEMENTS_FILE = `${TARGET_NAME}.entitlements`;
|
|
39
55
|
const KEYCHAIN_GROUPS_KEY = 'keychain-access-groups';
|
|
56
|
+
const COMMUNICATION_KEY = 'com.apple.developer.usernotifications.communication';
|
|
57
|
+
const SEND_MESSAGE_INTENT = 'INSendMessageIntent';
|
|
40
58
|
|
|
41
59
|
const appBundleId = (config) => {
|
|
42
60
|
const bundleId = config.ios && config.ios.bundleIdentifier;
|
|
@@ -93,6 +111,25 @@ const withAppKeychainGroup = (config) =>
|
|
|
93
111
|
return cfg;
|
|
94
112
|
});
|
|
95
113
|
|
|
114
|
+
// The sender's avatar: iOS shows a notification as a message from a person
|
|
115
|
+
// only for an app with this capability that declares the intent.
|
|
116
|
+
const withCommunicationNotifications = (config) =>
|
|
117
|
+
withInfoPlist(
|
|
118
|
+
withEntitlementsPlist(config, (cfg) => {
|
|
119
|
+
cfg.modResults[COMMUNICATION_KEY] = true;
|
|
120
|
+
return cfg;
|
|
121
|
+
}),
|
|
122
|
+
(cfg) => {
|
|
123
|
+
const types = Array.isArray(cfg.modResults.NSUserActivityTypes)
|
|
124
|
+
? cfg.modResults.NSUserActivityTypes
|
|
125
|
+
: [];
|
|
126
|
+
if (!types.includes(SEND_MESSAGE_INTENT)) {
|
|
127
|
+
cfg.modResults.NSUserActivityTypes = [...types, SEND_MESSAGE_INTENT];
|
|
128
|
+
}
|
|
129
|
+
return cfg;
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
|
|
96
133
|
const entitlementsPlist = (group) => `<?xml version="1.0" encoding="UTF-8"?>
|
|
97
134
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
98
135
|
<plist version="1.0">
|
|
@@ -183,7 +220,10 @@ const withExtensionTarget = (config, { appleTeamId } = {}) =>
|
|
|
183
220
|
return cfg;
|
|
184
221
|
});
|
|
185
222
|
|
|
186
|
-
const withNotificationServiceExtension = (config, props = {}) =>
|
|
187
|
-
|
|
223
|
+
const withNotificationServiceExtension = (config, props = {}) => {
|
|
224
|
+
let next = withAppKeychainGroup(withEasAppExtension(config));
|
|
225
|
+
if (props.communicationNotifications !== false) next = withCommunicationNotifications(next);
|
|
226
|
+
return withExtensionTarget(withExtensionFiles(next), props);
|
|
227
|
+
};
|
|
188
228
|
|
|
189
229
|
module.exports = withNotificationServiceExtension;
|
|
@@ -273,14 +273,18 @@ export const HeaderRoomListMenu: FC<HeaderRoomListMenuProps> = ({
|
|
|
273
273
|
onClick: () => void;
|
|
274
274
|
styles?: { color: string };
|
|
275
275
|
}[] = [
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
276
|
+
...(!config?.chatHeaderSettings?.disableCreate
|
|
277
|
+
? [
|
|
278
|
+
{
|
|
279
|
+
label: t('menu.newChat'),
|
|
280
|
+
icon: <AddNewIcon color={iconColor} />,
|
|
281
|
+
onClick: () => {
|
|
282
|
+
dispatch(setActiveModal(MODAL_TYPES.NEW_CHAT));
|
|
283
|
+
},
|
|
284
|
+
styles: { color: labelColor },
|
|
285
|
+
},
|
|
286
|
+
]
|
|
287
|
+
: []),
|
|
284
288
|
{
|
|
285
289
|
label: t('menu.profile'),
|
|
286
290
|
icon: <ProfileIcon color={iconColor} />,
|
|
@@ -329,6 +333,7 @@ export const HeaderRoomListMenu: FC<HeaderRoomListMenuProps> = ({
|
|
|
329
333
|
iconColor,
|
|
330
334
|
labelColor,
|
|
331
335
|
config?.disablePublicChatsDirectory,
|
|
336
|
+
config?.chatHeaderSettings?.disableCreate,
|
|
332
337
|
t,
|
|
333
338
|
]);
|
|
334
339
|
|
|
@@ -2,8 +2,7 @@ import { useEffect, useMemo, useRef, useState } from 'react';
|
|
|
2
2
|
import { Platform } from 'react-native';
|
|
3
3
|
import { useDispatch, useSelector } from 'react-redux';
|
|
4
4
|
import { RootState, store } from '../../roomStore';
|
|
5
|
-
import { acceptIncomingCall
|
|
6
|
-
import { sendCallStateSignal } from '../../networking/callTokenStanza';
|
|
5
|
+
import { acceptIncomingCall } from '../../roomStore/callSlice';
|
|
7
6
|
import { declineActiveCallWhenOnline, terminateActiveCall } from './callActions';
|
|
8
7
|
import { setCallMicEnabled, useCallSession } from './callSessionController';
|
|
9
8
|
import { loadLiveKit } from './livekitRuntime';
|
|
@@ -162,8 +161,7 @@ export const useCallKeep = (): void => {
|
|
|
162
161
|
snapshot.direction === 'incoming' &&
|
|
163
162
|
snapshot.phase === 'ringing-incoming'
|
|
164
163
|
) {
|
|
165
|
-
|
|
166
|
-
dispatch(declineIncomingCall());
|
|
164
|
+
declineActiveCallWhenOnline();
|
|
167
165
|
return;
|
|
168
166
|
}
|
|
169
167
|
// Ended from the Dynamic Island, the lock screen or a headset.
|
package/src/main.ts
CHANGED
|
@@ -81,7 +81,7 @@ export {
|
|
|
81
81
|
presentSealedPush,
|
|
82
82
|
SEALED_PUSH_TASK,
|
|
83
83
|
} from './push/sealedPushTask';
|
|
84
|
-
export type { SealedPushTaskOptions } from './push/sealedPushTask';
|
|
84
|
+
export type { SealedPushTaskOptions, ShownMessagePush } from './push/sealedPushTask';
|
|
85
85
|
export {
|
|
86
86
|
openSealedPush,
|
|
87
87
|
isSealedPush,
|
|
@@ -310,6 +310,8 @@ const handleCallStateData = (
|
|
|
310
310
|
!!currentCall.callId &&
|
|
311
311
|
currentCall.callId === stanzaCallId;
|
|
312
312
|
|
|
313
|
+
if (stanzaCallId && currentCall.callId && !callIdMatches) {return;}
|
|
314
|
+
|
|
313
315
|
const isTerminalState =
|
|
314
316
|
state === 'ended' ||
|
|
315
317
|
state === 'declined' ||
|
|
@@ -381,7 +383,7 @@ export const onCallTokenMessage = (stanza: Element): boolean => {
|
|
|
381
383
|
.split('/')[0]
|
|
382
384
|
.split('@')[0]
|
|
383
385
|
.trim();
|
|
384
|
-
handleCallStateData(
|
|
386
|
+
handleCallStateData(data, fromRoom);
|
|
385
387
|
|
|
386
388
|
// Server call-logs (carry callerXmppUsername / durationMs) should
|
|
387
389
|
// flow on to onRealtimeMessage / onMessageHistory where they become
|
|
@@ -394,6 +396,13 @@ export const onCallTokenMessage = (stanza: Element): boolean => {
|
|
|
394
396
|
return true;
|
|
395
397
|
}
|
|
396
398
|
|
|
399
|
+
if (dataType === 'call-state-declined') {
|
|
400
|
+
handleCallStateData({
|
|
401
|
+
attrs: { ...data.attrs, state: 'declined' },
|
|
402
|
+
} as unknown as Element);
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
405
|
+
|
|
397
406
|
if (dataType === 'call-invite') {
|
|
398
407
|
const inviteKind = String(data.attrs?.kind || '').toLowerCase();
|
|
399
408
|
const normalized: 'audio' | 'video' =
|
|
@@ -40,6 +40,20 @@ export interface SealedPushTaskOptions {
|
|
|
40
40
|
* where the XMPP invite rings the in-app call screen.
|
|
41
41
|
*/
|
|
42
42
|
onCallPush?: (data: Record<string, unknown>) => void | Promise<void>;
|
|
43
|
+
onMessageShown?: (shown: ShownMessagePush) => void | Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface ShownMessagePush {
|
|
47
|
+
identifier: string;
|
|
48
|
+
channelId?: string;
|
|
49
|
+
title: string;
|
|
50
|
+
body: string;
|
|
51
|
+
sender: string;
|
|
52
|
+
senderJid: string;
|
|
53
|
+
chat: string;
|
|
54
|
+
chatJid: string;
|
|
55
|
+
image: string;
|
|
56
|
+
reaction: boolean;
|
|
43
57
|
}
|
|
44
58
|
|
|
45
59
|
const PLACEHOLDER_TITLE = 'New message';
|
|
@@ -157,11 +171,30 @@ export async function presentSealedPush(
|
|
|
157
171
|
const base = envelope.jid || envelope.msgID || '';
|
|
158
172
|
const identifier = base ? (reaction ? `${base}#reaction` : base) : undefined;
|
|
159
173
|
const channelId = (reaction && options.reactionChannelId) || options.channelId;
|
|
174
|
+
const content = notificationContent(result, options);
|
|
160
175
|
await notifications.scheduleNotificationAsync({
|
|
161
176
|
identifier,
|
|
162
|
-
content
|
|
177
|
+
content,
|
|
163
178
|
trigger: channelId ? { channelId } : null,
|
|
164
179
|
});
|
|
180
|
+
if (result.ok && result.content.image && identifier && options.onMessageShown) {
|
|
181
|
+
try {
|
|
182
|
+
await options.onMessageShown({
|
|
183
|
+
identifier,
|
|
184
|
+
channelId,
|
|
185
|
+
title: content.title,
|
|
186
|
+
body: content.body ?? '',
|
|
187
|
+
sender: result.content.sender,
|
|
188
|
+
senderJid: result.content.senderJid,
|
|
189
|
+
chat: result.content.chat,
|
|
190
|
+
chatJid: result.content.chatJid,
|
|
191
|
+
image: result.content.image,
|
|
192
|
+
reaction,
|
|
193
|
+
});
|
|
194
|
+
} catch (e) {
|
|
195
|
+
pushLog('warn', 'push: drawing the sender avatar failed', e);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
165
198
|
return result;
|
|
166
199
|
}
|
|
167
200
|
|
package/src/version.ts
CHANGED