@ethora/chat-component-rn 26.10.7 → 26.10.8
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 +4 -0
- package/lib/commonjs/components/MessageBubble/Message.js +16 -6
- package/lib/commonjs/components/MessageBubble/Message.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/VideoCallOverlay.js +16 -2
- package/lib/commonjs/components/VideoCalls/VideoCallOverlay.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/callActions.js +32 -2
- package/lib/commonjs/components/VideoCalls/callActions.js.map +1 -1
- package/lib/commonjs/components/VideoCalls/systemCall.js +83 -0
- package/lib/commonjs/components/VideoCalls/systemCall.js.map +1 -0
- package/lib/commonjs/components/VideoCalls/useCallKeep.js +83 -66
- package/lib/commonjs/components/VideoCalls/useCallKeep.js.map +1 -1
- package/lib/commonjs/helpers/incomingCallFromPush.js +95 -21
- package/lib/commonjs/helpers/incomingCallFromPush.js.map +1 -1
- package/lib/commonjs/main.js +33 -0
- package/lib/commonjs/main.js.map +1 -1
- package/lib/commonjs/networking/callTokenStanza.js +6 -0
- package/lib/commonjs/networking/callTokenStanza.js.map +1 -1
- package/lib/commonjs/networking/xmpp/sendMessageReaction.xmpp.js +22 -6
- package/lib/commonjs/networking/xmpp/sendMessageReaction.xmpp.js.map +1 -1
- package/lib/commonjs/networking/xmppClient.js.map +1 -1
- package/lib/commonjs/push/sealedPush.js +13 -2
- package/lib/commonjs/push/sealedPush.js.map +1 -1
- package/lib/commonjs/push/sealedPushTask.js +43 -17
- package/lib/commonjs/push/sealedPushTask.js.map +1 -1
- package/lib/commonjs/version.js +1 -1
- package/lib/module/components/MessageBubble/Message.js +16 -6
- package/lib/module/components/MessageBubble/Message.js.map +1 -1
- package/lib/module/components/VideoCalls/VideoCallOverlay.js +17 -3
- package/lib/module/components/VideoCalls/VideoCallOverlay.js.map +1 -1
- package/lib/module/components/VideoCalls/callActions.js +29 -0
- package/lib/module/components/VideoCalls/callActions.js.map +1 -1
- package/lib/module/components/VideoCalls/systemCall.js +73 -0
- package/lib/module/components/VideoCalls/systemCall.js.map +1 -0
- package/lib/module/components/VideoCalls/useCallKeep.js +77 -64
- package/lib/module/components/VideoCalls/useCallKeep.js.map +1 -1
- package/lib/module/helpers/incomingCallFromPush.js +92 -22
- package/lib/module/helpers/incomingCallFromPush.js.map +1 -1
- package/lib/module/main.js +3 -0
- package/lib/module/main.js.map +1 -1
- package/lib/module/networking/callTokenStanza.js +6 -0
- package/lib/module/networking/callTokenStanza.js.map +1 -1
- package/lib/module/networking/xmpp/sendMessageReaction.xmpp.js +21 -6
- package/lib/module/networking/xmpp/sendMessageReaction.xmpp.js.map +1 -1
- package/lib/module/networking/xmppClient.js.map +1 -1
- package/lib/module/push/sealedPush.js +11 -1
- package/lib/module/push/sealedPush.js.map +1 -1
- package/lib/module/push/sealedPushTask.js +42 -17
- package/lib/module/push/sealedPushTask.js.map +1 -1
- package/lib/module/version.js +1 -1
- package/lib/typescript/components/MessageBubble/Message.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/VideoCallOverlay.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/callActions.d.ts +6 -0
- package/lib/typescript/components/VideoCalls/callActions.d.ts.map +1 -1
- package/lib/typescript/components/VideoCalls/systemCall.d.ts +25 -0
- package/lib/typescript/components/VideoCalls/systemCall.d.ts.map +1 -0
- package/lib/typescript/components/VideoCalls/useCallKeep.d.ts +8 -23
- package/lib/typescript/components/VideoCalls/useCallKeep.d.ts.map +1 -1
- package/lib/typescript/helpers/incomingCallFromPush.d.ts +5 -13
- package/lib/typescript/helpers/incomingCallFromPush.d.ts.map +1 -1
- package/lib/typescript/main.d.ts +5 -0
- package/lib/typescript/main.d.ts.map +1 -1
- package/lib/typescript/networking/callTokenStanza.d.ts.map +1 -1
- package/lib/typescript/networking/xmpp/sendMessageReaction.xmpp.d.ts +13 -1
- package/lib/typescript/networking/xmpp/sendMessageReaction.xmpp.d.ts.map +1 -1
- package/lib/typescript/networking/xmppClient.d.ts +2 -4
- package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
- package/lib/typescript/push/sealedPush.d.ts +6 -0
- package/lib/typescript/push/sealedPush.d.ts.map +1 -1
- package/lib/typescript/push/sealedPushTask.d.ts +18 -7
- package/lib/typescript/push/sealedPushTask.d.ts.map +1 -1
- package/lib/typescript/types/models/xmpp.model.d.ts +2 -4
- package/lib/typescript/types/models/xmpp.model.d.ts.map +1 -1
- package/lib/typescript/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/MessageBubble/Message.tsx +16 -5
- package/src/components/VideoCalls/VideoCallOverlay.tsx +18 -2
- package/src/components/VideoCalls/callActions.ts +28 -0
- package/src/components/VideoCalls/systemCall.ts +109 -0
- package/src/components/VideoCalls/useCallKeep.ts +89 -97
- package/src/helpers/incomingCallFromPush.ts +97 -26
- package/src/main.ts +8 -0
- package/src/networking/callTokenStanza.ts +10 -0
- package/src/networking/xmpp/sendMessageReaction.xmpp.ts +38 -6
- package/src/networking/xmppClient.ts +2 -2
- package/src/push/sealedPush.ts +11 -0
- package/src/push/sealedPushTask.ts +61 -15
- package/src/types/models/xmpp.model.ts +2 -1
- package/src/version.ts +1 -1
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { AppState, Platform } from 'react-native';
|
|
2
2
|
import { pushLog } from '../utils/devLogger';
|
|
3
|
+
import { isCallPush } from '../helpers/callPush';
|
|
3
4
|
import { loadPushSecretKey } from './pushKeyStore';
|
|
4
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
isReactionPush,
|
|
7
|
+
openSealedPush,
|
|
8
|
+
sealedPushEnvelopeOf,
|
|
9
|
+
type OpenSealedPushResult,
|
|
10
|
+
} from './sealedPush';
|
|
5
11
|
|
|
6
12
|
// Android half of encrypted pushes. A sealed push is data-only, so the OS
|
|
7
13
|
// shows nothing by itself: expo-notifications hands it to a background
|
|
@@ -18,12 +24,26 @@ export const SEALED_PUSH_TASK = 'ETHORA_SEALED_PUSH';
|
|
|
18
24
|
export interface SealedPushTaskOptions {
|
|
19
25
|
/** Android notification channel; the OS default channel when omitted. */
|
|
20
26
|
channelId?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Channel for a reaction to the user's message (a silent one, Telegram's
|
|
29
|
+
* way); `channelId` when omitted. Reactions carry no sound of their own
|
|
30
|
+
* either way.
|
|
31
|
+
*/
|
|
32
|
+
reactionChannelId?: string;
|
|
21
33
|
/** Shown when the box does not open (no key, another key, damaged). Default "New message". */
|
|
22
34
|
placeholderTitle?: string;
|
|
23
35
|
placeholderBody?: string;
|
|
36
|
+
/**
|
|
37
|
+
* An incoming-call push (data-only, high priority) while the app is not
|
|
38
|
+
* in the foreground; the host shows its own incoming-call notification
|
|
39
|
+
* (full-screen intent, Answer / Decline). Not called in the foreground,
|
|
40
|
+
* where the XMPP invite rings the in-app call screen.
|
|
41
|
+
*/
|
|
42
|
+
onCallPush?: (data: Record<string, unknown>) => void | Promise<void>;
|
|
24
43
|
}
|
|
25
44
|
|
|
26
45
|
const PLACEHOLDER_TITLE = 'New message';
|
|
46
|
+
const PLACEHOLDER_REACTION_TITLE = 'New reaction';
|
|
27
47
|
|
|
28
48
|
let registered = false;
|
|
29
49
|
|
|
@@ -49,9 +69,9 @@ const loadNotifications = (): any => {
|
|
|
49
69
|
* itself, `data.dataString` carries the same as a JSON string on some
|
|
50
70
|
* versions, and a Firebase `RemoteMessage` keeps them under `data.data`.
|
|
51
71
|
*/
|
|
52
|
-
|
|
72
|
+
const pushDataCandidates = (payload: unknown): unknown[] => {
|
|
53
73
|
const root = payload && typeof payload === 'object' ? (payload as Record<string, unknown>) : null;
|
|
54
|
-
if (!root) {return
|
|
74
|
+
if (!root) {return [];}
|
|
55
75
|
const candidates: unknown[] = [root.data, root];
|
|
56
76
|
const data = root.data && typeof root.data === 'object' ? (root.data as Record<string, unknown>) : null;
|
|
57
77
|
if (data) {
|
|
@@ -64,30 +84,39 @@ export const sealedPushDataOf = (payload: unknown): Record<string, unknown> | nu
|
|
|
64
84
|
}
|
|
65
85
|
candidates.push(data.data);
|
|
66
86
|
}
|
|
67
|
-
|
|
68
|
-
if (sealedPushEnvelopeOf(candidate as Record<string, unknown>)) {
|
|
69
|
-
return candidate as Record<string, unknown>;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return null;
|
|
87
|
+
return candidates.filter((c) => c && typeof c === 'object');
|
|
73
88
|
};
|
|
74
89
|
|
|
90
|
+
export const sealedPushDataOf = (payload: unknown): Record<string, unknown> | null =>
|
|
91
|
+
(pushDataCandidates(payload).find((candidate) =>
|
|
92
|
+
sealedPushEnvelopeOf(candidate as Record<string, unknown>)
|
|
93
|
+
) as Record<string, unknown> | undefined) ?? null;
|
|
94
|
+
|
|
95
|
+
/** The call fields of an incoming-call push in the task payload, if it is one. */
|
|
96
|
+
export const callPushDataOf = (payload: unknown): Record<string, unknown> | null =>
|
|
97
|
+
(pushDataCandidates(payload).find((candidate) =>
|
|
98
|
+
isCallPush(candidate as Record<string, unknown>)
|
|
99
|
+
) as Record<string, unknown> | undefined) ?? null;
|
|
100
|
+
|
|
75
101
|
const notificationContent = (result: OpenSealedPushResult, options: SealedPushTaskOptions) => {
|
|
76
102
|
const { envelope } = result;
|
|
77
103
|
// What the tap handler (`handlePushPayload` through the host's response
|
|
78
104
|
// listener) needs: the same ids a plain push carries on `data`.
|
|
105
|
+
const reaction = isReactionPush(envelope);
|
|
79
106
|
const data = {
|
|
80
107
|
jid: envelope.jid,
|
|
81
108
|
msgID: envelope.msgID,
|
|
82
109
|
userJid: envelope.userJid,
|
|
83
110
|
sealed: true,
|
|
111
|
+
...(reaction ? { type: 'reaction' } : {}),
|
|
84
112
|
};
|
|
113
|
+
const sound = reaction ? false : 'default';
|
|
85
114
|
if (!result.ok) {
|
|
86
115
|
return {
|
|
87
|
-
title: options.placeholderTitle || PLACEHOLDER_TITLE,
|
|
116
|
+
title: options.placeholderTitle || (reaction ? PLACEHOLDER_REACTION_TITLE : PLACEHOLDER_TITLE),
|
|
88
117
|
...(options.placeholderBody ? { body: options.placeholderBody } : {}),
|
|
89
118
|
data,
|
|
90
|
-
sound
|
|
119
|
+
sound,
|
|
91
120
|
};
|
|
92
121
|
}
|
|
93
122
|
const { content } = result;
|
|
@@ -95,14 +124,16 @@ const notificationContent = (result: OpenSealedPushResult, options: SealedPushTa
|
|
|
95
124
|
title: content.title || content.sender || content.chat || PLACEHOLDER_TITLE,
|
|
96
125
|
body: content.body,
|
|
97
126
|
data: { ...data, chatJid: content.chatJid, senderJid: content.senderJid },
|
|
98
|
-
sound
|
|
127
|
+
sound,
|
|
99
128
|
};
|
|
100
129
|
};
|
|
101
130
|
|
|
102
131
|
/**
|
|
103
132
|
* Open a sealed push and show it. One notification per chat: the room JID
|
|
104
133
|
* is the identifier, so a newer message replaces the older one the way the
|
|
105
|
-
* service's collapse key does for plain pushes.
|
|
134
|
+
* service's collapse key does for plain pushes. A reaction has its own
|
|
135
|
+
* identifier per chat (and its own channel), so it never replaces the
|
|
136
|
+
* chat's new-message notification.
|
|
106
137
|
*/
|
|
107
138
|
export async function presentSealedPush(
|
|
108
139
|
data: Record<string, unknown>,
|
|
@@ -122,10 +153,14 @@ export async function presentSealedPush(
|
|
|
122
153
|
console.warn(`[push] sealed push kid=${envelope.kid} not opened (${result.reason}), showing the placeholder`);
|
|
123
154
|
pushLog('warn', `push: sealed push not opened (${result.reason}), showing the placeholder`);
|
|
124
155
|
}
|
|
156
|
+
const reaction = isReactionPush(envelope);
|
|
157
|
+
const base = envelope.jid || envelope.msgID || '';
|
|
158
|
+
const identifier = base ? (reaction ? `${base}#reaction` : base) : undefined;
|
|
159
|
+
const channelId = (reaction && options.reactionChannelId) || options.channelId;
|
|
125
160
|
await notifications.scheduleNotificationAsync({
|
|
126
|
-
identifier
|
|
161
|
+
identifier,
|
|
127
162
|
content: notificationContent(result, options),
|
|
128
|
-
trigger:
|
|
163
|
+
trigger: channelId ? { channelId } : null,
|
|
129
164
|
});
|
|
130
165
|
return result;
|
|
131
166
|
}
|
|
@@ -150,6 +185,17 @@ export function registerSealedPushTask(options: SealedPushTaskOptions = {}): boo
|
|
|
150
185
|
}
|
|
151
186
|
taskManager.defineTask(SEALED_PUSH_TASK, async ({ data, error }: { data: unknown; error: unknown }) => {
|
|
152
187
|
if (error) {return;}
|
|
188
|
+
const call = callPushDataOf(data);
|
|
189
|
+
if (call) {
|
|
190
|
+
if (AppState.currentState === 'active' || !options.onCallPush) {return;}
|
|
191
|
+
try {
|
|
192
|
+
console.log(`[push] incoming call push ${String(call.callId ?? '')}`);
|
|
193
|
+
await options.onCallPush(call);
|
|
194
|
+
} catch (e) {
|
|
195
|
+
pushLog('warn', 'push: showing an incoming call failed', e);
|
|
196
|
+
}
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
153
199
|
const map = sealedPushDataOf(data);
|
|
154
200
|
if (!map) {return;}
|
|
155
201
|
// In the foreground the room is live over XMPP; the host's
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// methods) rather than duplicating here.
|
|
6
6
|
|
|
7
7
|
import { Client } from '@xmpp/client';
|
|
8
|
+
import type { ReactionStanzaData } from '../../networking/xmpp/sendMessageReaction.xmpp';
|
|
8
9
|
import XmppClient, {
|
|
9
10
|
HistoryPage,
|
|
10
11
|
HistoryPageCursor,
|
|
@@ -153,7 +154,7 @@ export interface XmppClientInterface {
|
|
|
153
154
|
messageId: string,
|
|
154
155
|
roomJid: string,
|
|
155
156
|
reactionsList: string[],
|
|
156
|
-
data?:
|
|
157
|
+
data?: ReactionStanzaData
|
|
157
158
|
): void;
|
|
158
159
|
sendTextMessageWithTranslateTagStanza?(
|
|
159
160
|
roomJID: string,
|
package/src/version.ts
CHANGED