@ethora/chat-component-rn 26.10.2 → 26.10.3

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.
Files changed (139) hide show
  1. package/lib/commonjs/components/MainComponents/ChatHeader.js +11 -10
  2. package/lib/commonjs/components/MainComponents/ChatHeader.js.map +1 -1
  3. package/lib/commonjs/components/MainComponents/MessageContainer.js +10 -2
  4. package/lib/commonjs/components/MainComponents/MessageContainer.js.map +1 -1
  5. package/lib/commonjs/components/MainComponents/RoomList.js +8 -18
  6. package/lib/commonjs/components/MainComponents/RoomList.js.map +1 -1
  7. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js +17 -14
  8. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  9. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js +131 -17
  10. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  11. package/lib/commonjs/components/RoomComponents/ChatRoomItem.js +6 -1
  12. package/lib/commonjs/components/RoomComponents/ChatRoomItem.js.map +1 -1
  13. package/lib/commonjs/components/VideoCalls/CallButtons.js +3 -1
  14. package/lib/commonjs/components/VideoCalls/CallButtons.js.map +1 -1
  15. package/lib/commonjs/components/VideoCalls/VideoCallOverlay.js +123 -111
  16. package/lib/commonjs/components/VideoCalls/VideoCallOverlay.js.map +1 -1
  17. package/lib/commonjs/components/VideoCalls/VideoCallSession.js +207 -346
  18. package/lib/commonjs/components/VideoCalls/VideoCallSession.js.map +1 -1
  19. package/lib/commonjs/components/VideoCalls/callActions.js +61 -0
  20. package/lib/commonjs/components/VideoCalls/callActions.js.map +1 -0
  21. package/lib/commonjs/components/VideoCalls/callSessionController.js +431 -0
  22. package/lib/commonjs/components/VideoCalls/callSessionController.js.map +1 -0
  23. package/lib/commonjs/components/VideoCalls/useDraggable.js +59 -0
  24. package/lib/commonjs/components/VideoCalls/useDraggable.js.map +1 -0
  25. package/lib/commonjs/helpers/createRoomFromApi.js +4 -0
  26. package/lib/commonjs/helpers/createRoomFromApi.js.map +1 -1
  27. package/lib/commonjs/helpers/incomingCallFromPush.js +1 -1
  28. package/lib/commonjs/helpers/incomingCallFromPush.js.map +1 -1
  29. package/lib/commonjs/helpers/roomUserCount.js +7 -1
  30. package/lib/commonjs/helpers/roomUserCount.js.map +1 -1
  31. package/lib/commonjs/helpers/uniqueMembers.js +36 -0
  32. package/lib/commonjs/helpers/uniqueMembers.js.map +1 -0
  33. package/lib/commonjs/hooks/useLeaveRoom.js +52 -0
  34. package/lib/commonjs/hooks/useLeaveRoom.js.map +1 -0
  35. package/lib/commonjs/i18n/strings.js +18 -0
  36. package/lib/commonjs/i18n/strings.js.map +1 -1
  37. package/lib/commonjs/networking/api-requests/rooms.api.js +36 -1
  38. package/lib/commonjs/networking/api-requests/rooms.api.js.map +1 -1
  39. package/lib/commonjs/networking/callTokenStanza.js +47 -3
  40. package/lib/commonjs/networking/callTokenStanza.js.map +1 -1
  41. package/lib/commonjs/networking/xmppClient.js +9 -2
  42. package/lib/commonjs/networking/xmppClient.js.map +1 -1
  43. package/lib/commonjs/version.js +1 -1
  44. package/lib/module/components/MainComponents/ChatHeader.js +13 -12
  45. package/lib/module/components/MainComponents/ChatHeader.js.map +1 -1
  46. package/lib/module/components/MainComponents/MessageContainer.js +10 -2
  47. package/lib/module/components/MainComponents/MessageContainer.js.map +1 -1
  48. package/lib/module/components/MainComponents/RoomList.js +8 -18
  49. package/lib/module/components/MainComponents/RoomList.js.map +1 -1
  50. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js +18 -15
  51. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  52. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js +132 -18
  53. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  54. package/lib/module/components/RoomComponents/ChatRoomItem.js +6 -1
  55. package/lib/module/components/RoomComponents/ChatRoomItem.js.map +1 -1
  56. package/lib/module/components/VideoCalls/CallButtons.js +3 -1
  57. package/lib/module/components/VideoCalls/CallButtons.js.map +1 -1
  58. package/lib/module/components/VideoCalls/VideoCallOverlay.js +125 -113
  59. package/lib/module/components/VideoCalls/VideoCallOverlay.js.map +1 -1
  60. package/lib/module/components/VideoCalls/VideoCallSession.js +208 -348
  61. package/lib/module/components/VideoCalls/VideoCallSession.js.map +1 -1
  62. package/lib/module/components/VideoCalls/callActions.js +55 -0
  63. package/lib/module/components/VideoCalls/callActions.js.map +1 -0
  64. package/lib/module/components/VideoCalls/callSessionController.js +420 -0
  65. package/lib/module/components/VideoCalls/callSessionController.js.map +1 -0
  66. package/lib/module/components/VideoCalls/useDraggable.js +55 -0
  67. package/lib/module/components/VideoCalls/useDraggable.js.map +1 -0
  68. package/lib/module/helpers/createRoomFromApi.js +4 -0
  69. package/lib/module/helpers/createRoomFromApi.js.map +1 -1
  70. package/lib/module/helpers/incomingCallFromPush.js +1 -1
  71. package/lib/module/helpers/incomingCallFromPush.js.map +1 -1
  72. package/lib/module/helpers/roomUserCount.js +7 -1
  73. package/lib/module/helpers/roomUserCount.js.map +1 -1
  74. package/lib/module/helpers/uniqueMembers.js +31 -0
  75. package/lib/module/helpers/uniqueMembers.js.map +1 -0
  76. package/lib/module/hooks/useLeaveRoom.js +48 -0
  77. package/lib/module/hooks/useLeaveRoom.js.map +1 -0
  78. package/lib/module/i18n/strings.js +18 -0
  79. package/lib/module/i18n/strings.js.map +1 -1
  80. package/lib/module/networking/api-requests/rooms.api.js +35 -1
  81. package/lib/module/networking/api-requests/rooms.api.js.map +1 -1
  82. package/lib/module/networking/callTokenStanza.js +47 -3
  83. package/lib/module/networking/callTokenStanza.js.map +1 -1
  84. package/lib/module/networking/xmppClient.js +8 -1
  85. package/lib/module/networking/xmppClient.js.map +1 -1
  86. package/lib/module/version.js +1 -1
  87. package/lib/typescript/components/MainComponents/ChatHeader.d.ts.map +1 -1
  88. package/lib/typescript/components/MainComponents/MessageContainer.d.ts.map +1 -1
  89. package/lib/typescript/components/MainComponents/RoomList.d.ts.map +1 -1
  90. package/lib/typescript/components/Modals/ChatProfileModal/ChatProfileModal.d.ts.map +1 -1
  91. package/lib/typescript/components/Modals/UserProfileModal/UserProfileModal.d.ts.map +1 -1
  92. package/lib/typescript/components/RoomComponents/ChatRoomItem.d.ts.map +1 -1
  93. package/lib/typescript/components/VideoCalls/CallButtons.d.ts.map +1 -1
  94. package/lib/typescript/components/VideoCalls/VideoCallOverlay.d.ts +0 -6
  95. package/lib/typescript/components/VideoCalls/VideoCallOverlay.d.ts.map +1 -1
  96. package/lib/typescript/components/VideoCalls/VideoCallSession.d.ts +9 -20
  97. package/lib/typescript/components/VideoCalls/VideoCallSession.d.ts.map +1 -1
  98. package/lib/typescript/components/VideoCalls/callActions.d.ts +15 -0
  99. package/lib/typescript/components/VideoCalls/callActions.d.ts.map +1 -0
  100. package/lib/typescript/components/VideoCalls/callSessionController.d.ts +61 -0
  101. package/lib/typescript/components/VideoCalls/callSessionController.d.ts.map +1 -0
  102. package/lib/typescript/components/VideoCalls/useDraggable.d.ts +19 -0
  103. package/lib/typescript/components/VideoCalls/useDraggable.d.ts.map +1 -0
  104. package/lib/typescript/helpers/createRoomFromApi.d.ts.map +1 -1
  105. package/lib/typescript/helpers/roomUserCount.d.ts +1 -0
  106. package/lib/typescript/helpers/roomUserCount.d.ts.map +1 -1
  107. package/lib/typescript/helpers/uniqueMembers.d.ts +11 -0
  108. package/lib/typescript/helpers/uniqueMembers.d.ts.map +1 -0
  109. package/lib/typescript/hooks/useLeaveRoom.d.ts +9 -0
  110. package/lib/typescript/hooks/useLeaveRoom.d.ts.map +1 -0
  111. package/lib/typescript/i18n/strings.d.ts.map +1 -1
  112. package/lib/typescript/networking/api-requests/rooms.api.d.ts +8 -0
  113. package/lib/typescript/networking/api-requests/rooms.api.d.ts.map +1 -1
  114. package/lib/typescript/networking/callTokenStanza.d.ts.map +1 -1
  115. package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
  116. package/lib/typescript/version.d.ts +1 -1
  117. package/package.json +1 -1
  118. package/src/components/MainComponents/ChatHeader.tsx +10 -8
  119. package/src/components/MainComponents/MessageContainer.tsx +14 -1
  120. package/src/components/MainComponents/RoomList.tsx +8 -14
  121. package/src/components/Modals/ChatProfileModal/ChatProfileModal.tsx +16 -13
  122. package/src/components/Modals/UserProfileModal/UserProfileModal.tsx +167 -36
  123. package/src/components/RoomComponents/ChatRoomItem.tsx +8 -1
  124. package/src/components/VideoCalls/CallButtons.tsx +5 -1
  125. package/src/components/VideoCalls/VideoCallOverlay.tsx +156 -163
  126. package/src/components/VideoCalls/VideoCallSession.tsx +207 -378
  127. package/src/components/VideoCalls/callActions.ts +63 -0
  128. package/src/components/VideoCalls/callSessionController.ts +421 -0
  129. package/src/components/VideoCalls/useDraggable.ts +46 -0
  130. package/src/helpers/createRoomFromApi.tsx +2 -0
  131. package/src/helpers/incomingCallFromPush.ts +2 -2
  132. package/src/helpers/roomUserCount.ts +6 -2
  133. package/src/helpers/uniqueMembers.ts +26 -0
  134. package/src/hooks/useLeaveRoom.ts +42 -0
  135. package/src/i18n/strings.ts +18 -0
  136. package/src/networking/api-requests/rooms.api.ts +28 -1
  137. package/src/networking/callTokenStanza.ts +46 -6
  138. package/src/networking/xmppClient.ts +10 -1
  139. package/src/version.ts +1 -1
@@ -44,15 +44,25 @@ import { runLogoutFlow } from '../../Menu/HeaderRoomListMenu';
44
44
  import { useLogout } from '../../../hooks/useLogout';
45
45
  import {
46
46
  ApiRoom,
47
+ createChatCall,
47
48
  ownAppId,
48
49
  postPrivateRoom,
49
50
  } from '../../../networking/api-requests/rooms.api';
50
51
  import EncryptionCard from '../../MainComponents/EncryptionCard';
52
+ import { useSelector } from 'react-redux';
53
+ import { store, type RootState } from '../../../roomStore';
54
+ import { setCallError, startOutgoingCall } from '../../../roomStore/callSlice';
55
+ import { sendCallInviteSignal } from '../../../networking/callTokenStanza';
56
+ import { AudioCallIcon, VideoCallIcon } from '../../VideoCalls/CallIcons';
51
57
  import { accountNameOf } from '../../../helpers/accountName';
52
58
  import { setChatLanguage } from '../../../services/languageSettings';
53
59
  import { composeName } from '../../../helpers/displayName';
54
60
  import { isUnresolvedSenderId } from '../../../helpers/isUnresolvedSenderId';
55
61
 
62
+ /** The user id part of `${appId}_${userId}[@host]` (or a bare id). */
63
+ const userIdOf = (value?: string | null): string =>
64
+ String(value || '').split('@')[0].split('_').pop() || '';
65
+
56
66
  /** A name, unless it is only the account's id standing in for one. */
57
67
  const nameOrNothing = (name?: string | null): string => {
58
68
  const trimmed = String(name ?? '').trim();
@@ -239,7 +249,7 @@ const UserProfileModal: React.FC<UserProfileModalProps> = ({
239
249
  usersArrayLength
240
250
  );
241
251
 
242
- if (!normalizedChat || !client) {return;}
252
+ if (!normalizedChat || !client) {return null;}
243
253
 
244
254
  dispatch(addRoomViaApi({ room: normalizedChat, xmpp: client }));
245
255
  dispatch(setCurrentRoom({ roomJID: normalizedChat?.jid || '' }));
@@ -251,11 +261,93 @@ const UserProfileModal: React.FC<UserProfileModalProps> = ({
251
261
  type: 'success',
252
262
  duration: 3000,
253
263
  });
264
+ return normalizedChat;
254
265
  } catch (error) {
255
266
  console.error('Error handling room creation:', error);
267
+ return null;
256
268
  }
257
269
  };
258
270
 
271
+ // Open (or create) the 1:1 room with the profile's user. Shared by
272
+ // "Message" and the call buttons, which need the room's JID to dial.
273
+ const ensurePrivateRoom = useCallback(async (
274
+ e2ee = false
275
+ ): Promise<{
276
+ jid: string;
277
+ bareName: string;
278
+ peerXmppUsername: string | null;
279
+ peerDisplay: string;
280
+ } | null> => {
281
+ const peerDisplay = nameOrNothing(selectedUser?.name);
282
+ if (config?.newArch) {
283
+ const target = selectedUser?.userJID ?? (selectedUser?.id || '');
284
+ // Reuse the 1:1 room we already have with this user instead of
285
+ // POSTing /chats/private again: repeating it for an existing room
286
+ // has been seen to inflate the server's usersCnt and member list.
287
+ const peerLocal = String(target).split('@')[0];
288
+ const existing = Object.values(store.getState().rooms.rooms || {}).find(
289
+ (room: any) =>
290
+ room?.type === 'private' &&
291
+ !!peerLocal &&
292
+ [...(room.roomMembers || []), ...(room.members || [])].some(
293
+ // REST rows carry the bare user id, stanza rows `${appId}_${id}`:
294
+ // compare on the user id part.
295
+ (m: any) => userIdOf(m?.xmppUsername || m?._id) === userIdOf(peerLocal)
296
+ )
297
+ ) as any;
298
+ if (existing?.jid && !e2ee) {
299
+ dispatch(setCurrentRoom({ roomJID: existing.jid }));
300
+ return {
301
+ jid: existing.jid,
302
+ bareName: existing.jid.split('@')[0],
303
+ peerXmppUsername: peerLocal || null,
304
+ peerDisplay: peerDisplay || nameOrNothing(existing.title),
305
+ };
306
+ }
307
+ const newRoom = await postPrivateRoom(target, e2ee);
308
+ const created = await handleRoomCreation(newRoom, 2);
309
+ if (!created?.jid) {return null;}
310
+ return {
311
+ jid: created.jid,
312
+ bareName: created.jid.split('@')[0],
313
+ peerXmppUsername: String(target).split('@')[0] || null,
314
+ peerDisplay: peerDisplay || nameOrNothing((created as any).title),
315
+ };
316
+ }
317
+
318
+ const selectedUserUsername = walletToUsername(selectedUser?.id || '');
319
+ const myUsername = walletToUsername(user.defaultWallet.walletAddress);
320
+
321
+ const roomJid = [myUsername, selectedUserUsername]
322
+ .sort()
323
+ .join('.')
324
+ .toLowerCase();
325
+
326
+ const combinedUsersName = [
327
+ user.firstName,
328
+ selectedUser?.name?.split(' ')?.[0] || '',
329
+ ]
330
+ .sort()
331
+ .join(' and ');
332
+
333
+ const newRoomJid =
334
+ (await client?.createPrivateRoomStanza(
335
+ combinedUsersName,
336
+ `Private chat ${combinedUsersName}`,
337
+ roomJid
338
+ )) || '';
339
+
340
+ if (!newRoomJid) {return null;}
341
+ await client?.inviteRoomRequestStanza(selectedUserUsername, newRoomJid);
342
+ await client?.getRoomsStanza();
343
+ return {
344
+ jid: newRoomJid,
345
+ bareName: newRoomJid.split('@')[0],
346
+ peerXmppUsername: selectedUserUsername || null,
347
+ peerDisplay,
348
+ };
349
+ }, [selectedUser, config?.newArch, client, user]); // eslint-disable-line react-hooks/exhaustive-deps
350
+
259
351
  const openPrivateRoom = useCallback(async (e2ee: boolean) => {
260
352
  showToast({
261
353
  id: Date.now().toString(),
@@ -264,43 +356,62 @@ const UserProfileModal: React.FC<UserProfileModalProps> = ({
264
356
  type: 'info',
265
357
  duration: 3000,
266
358
  });
267
- if (config?.newArch) {
268
- const newRoom = await postPrivateRoom(
269
- selectedUser?.userJID ?? (selectedUser?.id || ''),
270
- e2ee
271
- );
272
- handleRoomCreation(newRoom, 2);
273
- } else {
274
- const selectedUserUsername = walletToUsername(selectedUser?.id || '');
275
- const myUsername = walletToUsername(user.defaultWallet.walletAddress);
276
-
277
- const roomJid = [myUsername, selectedUserUsername]
278
- .sort()
279
- .join('.')
280
- .toLowerCase();
281
-
282
- const combinedUsersName = [
283
- user.firstName,
284
- selectedUser?.name?.split(' ')?.[0] || '',
285
- ]
286
- .sort()
287
- .join(' and ');
288
-
289
- const newRoomJid =
290
- (await client?.createPrivateRoomStanza(
291
- combinedUsersName,
292
- `Private chat ${combinedUsersName}`,
293
- roomJid
294
- )) || '';
295
-
296
- if (newRoomJid) {
297
- await client?.inviteRoomRequestStanza(selectedUserUsername, newRoomJid);
298
- await client?.getRoomsStanza();
299
- }
359
+ await ensurePrivateRoom(e2ee);
360
+ dispatch(setActiveModal(undefined));
361
+ }, [ensurePrivateRoom, t]); // eslint-disable-line react-hooks/exhaustive-deps
362
+
363
+ // Calls from the profile, as on web: open the 1:1 room, then dial it the
364
+ // same way the chat header does.
365
+ const videoCallsConfig = config?.videoCalls;
366
+ const canCall =
367
+ videoCallsConfig?.enabled === true &&
368
+ Boolean(videoCallsConfig?.livekitUrl?.trim()) &&
369
+ (videoCallsConfig?.allowedRoomTypes || ['private']).includes('private');
370
+ const isAudioCallsEnabled = canCall && videoCallsConfig?.enableAudioCalls === true;
371
+ const isCallBusy = useSelector((state: RootState) => state.call.phase !== 'idle');
372
+
373
+ const handleCall = useCallback(async (kind: 'audio' | 'video') => {
374
+ if (isCallBusy) {return;}
375
+ let room: Awaited<ReturnType<typeof ensurePrivateRoom>> = null;
376
+ try {
377
+ room = await ensurePrivateRoom();
378
+ } catch (error) {
379
+ console.warn('Could not open the private chat for a call:', error);
380
+ }
381
+ if (!room) {return;}
382
+
383
+ dispatch(
384
+ startOutgoingCall({
385
+ roomJid: room.jid,
386
+ roomName: room.peerDisplay || room.bareName,
387
+ roomBareName: room.bareName,
388
+ kind,
389
+ peerXmppUsername: room.peerXmppUsername,
390
+ })
391
+ );
392
+
393
+ // The server drops `kind` on the relayed call-token, so signal the peer
394
+ // directly first, as CallButtons does.
395
+ if (room.peerXmppUsername) {
396
+ sendCallInviteSignal(kind, {
397
+ peerXmppUsername: room.peerXmppUsername,
398
+ roomBareName: room.bareName,
399
+ });
300
400
  }
301
401
 
402
+ // Close the profile so the call screen isn't behind it.
302
403
  dispatch(setActiveModal(undefined));
303
- }, [selectedUser, t]); // eslint-disable-line react-hooks/exhaustive-deps
404
+
405
+ try {
406
+ await createChatCall(room.bareName, { kind });
407
+ } catch (error) {
408
+ dispatch(
409
+ setCallError(
410
+ error instanceof Error ? error.message : t('call.error.connectFailed')
411
+ )
412
+ );
413
+ }
414
+ }, [ensurePrivateRoom, isCallBusy, t]); // eslint-disable-line react-hooks/exhaustive-deps
304
415
 
305
416
  const handlePrivateMessage = useCallback(
306
417
  () => openPrivateRoom(false),
@@ -374,6 +485,26 @@ const UserProfileModal: React.FC<UserProfileModalProps> = ({
374
485
  ]
375
486
  : []),
376
487
  ]),
488
+ ...(canCall && !isCallBusy
489
+ ? [
490
+ {
491
+ key: 'video-call',
492
+ label: isAudioCallsEnabled ? t('action.videoCall') : t('action.call'),
493
+ icon: (color: string) => <VideoCallIcon color={color} />,
494
+ onPress: () => void handleCall('video'),
495
+ },
496
+ ...(isAudioCallsEnabled
497
+ ? [
498
+ {
499
+ key: 'audio-call',
500
+ label: t('action.audioCall'),
501
+ icon: (color: string) => <AudioCallIcon color={color} />,
502
+ onPress: () => void handleCall('audio'),
503
+ },
504
+ ]
505
+ : []),
506
+ ]
507
+ : []),
377
508
  {
378
509
  key: 'share',
379
510
  label: t('action.share'),
@@ -381,7 +512,7 @@ const UserProfileModal: React.FC<UserProfileModalProps> = ({
381
512
  onPress: handleShare,
382
513
  },
383
514
  ],
384
- [isOwnProfile, t, handleShare, handlePrivateMessage, handleEncryptedPrivateMessage, canStartEncrypted, handleLogout] // eslint-disable-line react-hooks/exhaustive-deps
515
+ [isOwnProfile, t, handleShare, handlePrivateMessage, handleEncryptedPrivateMessage, canStartEncrypted, handleLogout, canCall, isCallBusy, isAudioCallsEnabled, handleCall] // eslint-disable-line react-hooks/exhaustive-deps
385
516
  );
386
517
 
387
518
  // Collapsed, the hero's round buttons are gone — the "…" menu carries
@@ -22,6 +22,7 @@ import { useTheme } from '../../hooks/useTheme';
22
22
  import { resolveTheme } from '../../theme/theme';
23
23
  import { BellOffIcon, LockIcon } from '../../assets/icons';
24
24
  import { useT } from '../../i18n/useT';
25
+ import { formatCallLogLabel } from '../../helpers/callLogMessage';
25
26
  import { composeName } from '../../helpers/displayName';
26
27
  import { isUnresolvedSenderId } from '../../helpers/isUnresolvedSenderId';
27
28
 
@@ -75,13 +76,19 @@ const ChatRoomItem: React.FC<ChatRoomItemProps> = ({
75
76
 
76
77
  return {
77
78
  ...message,
79
+ // A call-log entry's body is English frozen at receive time; rebuild
80
+ // it from the callLog meta so the preview follows the current
81
+ // language like the transcript does (mirrors web's ChatRoomItem).
82
+ body: message.callLog
83
+ ? formatCallLogLabel(message.callLog, t, message.body)
84
+ : message.body,
78
85
  user: {
79
86
  ...message.user,
80
87
  name: safeName,
81
88
  },
82
89
  };
83
90
  },
84
- [usersSet]
91
+ [usersSet, t]
85
92
  );
86
93
 
87
94
  const lastMessage = useMemo(() => {
@@ -71,7 +71,11 @@ export const CallButtons: FC = () => {
71
71
  // Don't show "deleted" / "Deleted User" / "Unknown" as the dial
72
72
  // screen title when the chat title is one of the server sentinels.
73
73
  // Prefer the peer's first+last name, falling back to the bare chat name.
74
- const rawTitle = String(currentRoom.name || '').trim();
74
+ // `title` is what the header shows ("John Pauliller"); on new-arch
75
+ // private rooms `name` is the bare room id.
76
+ const rawTitle = String(
77
+ (currentRoom as any).title || currentRoom.name || ''
78
+ ).trim();
75
79
  const isBadTitle =
76
80
  !rawTitle ||
77
81
  ['deleted', 'deleted user', 'unknown', 'null'].includes(