@ethora/chat-component-rn 26.6.13 → 26.7.1

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 (115) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/lib/commonjs/components/AuthForms/Login.js +2 -3
  3. package/lib/commonjs/components/AuthForms/Login.js.map +1 -1
  4. package/lib/commonjs/components/MainComponents/ProfileImagePlaceholder.js +9 -8
  5. package/lib/commonjs/components/MainComponents/ProfileImagePlaceholder.js.map +1 -1
  6. package/lib/commonjs/components/Modals/NewChatModal/ModalContent.js +2 -1
  7. package/lib/commonjs/components/Modals/NewChatModal/ModalContent.js.map +1 -1
  8. package/lib/commonjs/components/Modals/NewChatModal/UpdNewChatModal.js +8 -0
  9. package/lib/commonjs/components/Modals/NewChatModal/UpdNewChatModal.js.map +1 -1
  10. package/lib/commonjs/components/Modals/UserProfileModal/EditUserModal.js +7 -21
  11. package/lib/commonjs/components/Modals/UserProfileModal/EditUserModal.js.map +1 -1
  12. package/lib/commonjs/context/xmppProvider.js +2 -3
  13. package/lib/commonjs/context/xmppProvider.js.map +1 -1
  14. package/lib/commonjs/helpers/ensureScopedChatCache.js +2 -2
  15. package/lib/commonjs/helpers/ensureScopedChatCache.js.map +1 -1
  16. package/lib/commonjs/helpers/persistCrypto.js +122 -0
  17. package/lib/commonjs/helpers/persistCrypto.js.map +1 -0
  18. package/lib/commonjs/helpers/pickImageAsset.js +68 -0
  19. package/lib/commonjs/helpers/pickImageAsset.js.map +1 -0
  20. package/lib/commonjs/helpers/resolveInitBeforeLoadUser.js +5 -6
  21. package/lib/commonjs/helpers/resolveInitBeforeLoadUser.js.map +1 -1
  22. package/lib/commonjs/helpers/secureKeyValue.js +83 -0
  23. package/lib/commonjs/helpers/secureKeyValue.js.map +1 -0
  24. package/lib/commonjs/helpers/secureStoreRuntime.js +59 -0
  25. package/lib/commonjs/helpers/secureStoreRuntime.js.map +1 -0
  26. package/lib/commonjs/helpers/secureUserStorage.js +75 -0
  27. package/lib/commonjs/helpers/secureUserStorage.js.map +1 -0
  28. package/lib/commonjs/hooks/useLogout.js +15 -7
  29. package/lib/commonjs/hooks/useLogout.js.map +1 -1
  30. package/lib/commonjs/networking/authRefresh.js +4 -5
  31. package/lib/commonjs/networking/authRefresh.js.map +1 -1
  32. package/lib/commonjs/roomStore/chatSettingsSlice.js +3 -4
  33. package/lib/commonjs/roomStore/chatSettingsSlice.js.map +1 -1
  34. package/lib/commonjs/roomStore/persistence.js +29 -15
  35. package/lib/commonjs/roomStore/persistence.js.map +1 -1
  36. package/lib/commonjs/roomStore/roomsSlice.js +2 -0
  37. package/lib/commonjs/roomStore/roomsSlice.js.map +1 -1
  38. package/lib/module/components/AuthForms/Login.js +2 -3
  39. package/lib/module/components/AuthForms/Login.js.map +1 -1
  40. package/lib/module/components/MainComponents/ProfileImagePlaceholder.js +9 -8
  41. package/lib/module/components/MainComponents/ProfileImagePlaceholder.js.map +1 -1
  42. package/lib/module/components/Modals/NewChatModal/ModalContent.js +2 -1
  43. package/lib/module/components/Modals/NewChatModal/ModalContent.js.map +1 -1
  44. package/lib/module/components/Modals/NewChatModal/UpdNewChatModal.js +8 -0
  45. package/lib/module/components/Modals/NewChatModal/UpdNewChatModal.js.map +1 -1
  46. package/lib/module/components/Modals/UserProfileModal/EditUserModal.js +9 -23
  47. package/lib/module/components/Modals/UserProfileModal/EditUserModal.js.map +1 -1
  48. package/lib/module/context/xmppProvider.js +2 -3
  49. package/lib/module/context/xmppProvider.js.map +1 -1
  50. package/lib/module/helpers/ensureScopedChatCache.js +2 -2
  51. package/lib/module/helpers/ensureScopedChatCache.js.map +1 -1
  52. package/lib/module/helpers/persistCrypto.js +115 -0
  53. package/lib/module/helpers/persistCrypto.js.map +1 -0
  54. package/lib/module/helpers/pickImageAsset.js +63 -0
  55. package/lib/module/helpers/pickImageAsset.js.map +1 -0
  56. package/lib/module/helpers/resolveInitBeforeLoadUser.js +5 -6
  57. package/lib/module/helpers/resolveInitBeforeLoadUser.js.map +1 -1
  58. package/lib/module/helpers/secureKeyValue.js +75 -0
  59. package/lib/module/helpers/secureKeyValue.js.map +1 -0
  60. package/lib/module/helpers/secureStoreRuntime.js +52 -0
  61. package/lib/module/helpers/secureStoreRuntime.js.map +1 -0
  62. package/lib/module/helpers/secureUserStorage.js +72 -0
  63. package/lib/module/helpers/secureUserStorage.js.map +1 -0
  64. package/lib/module/hooks/useLogout.js +15 -7
  65. package/lib/module/hooks/useLogout.js.map +1 -1
  66. package/lib/module/networking/authRefresh.js +4 -5
  67. package/lib/module/networking/authRefresh.js.map +1 -1
  68. package/lib/module/roomStore/chatSettingsSlice.js +3 -4
  69. package/lib/module/roomStore/chatSettingsSlice.js.map +1 -1
  70. package/lib/module/roomStore/persistence.js +30 -15
  71. package/lib/module/roomStore/persistence.js.map +1 -1
  72. package/lib/module/roomStore/roomsSlice.js +2 -0
  73. package/lib/module/roomStore/roomsSlice.js.map +1 -1
  74. package/lib/typescript/components/AuthForms/Login.d.ts.map +1 -1
  75. package/lib/typescript/components/MainComponents/ProfileImagePlaceholder.d.ts.map +1 -1
  76. package/lib/typescript/components/Modals/NewChatModal/ModalContent.d.ts +4 -2
  77. package/lib/typescript/components/Modals/NewChatModal/ModalContent.d.ts.map +1 -1
  78. package/lib/typescript/components/Modals/NewChatModal/UpdNewChatModal.d.ts.map +1 -1
  79. package/lib/typescript/components/Modals/UserProfileModal/EditUserModal.d.ts.map +1 -1
  80. package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
  81. package/lib/typescript/helpers/persistCrypto.d.ts +13 -0
  82. package/lib/typescript/helpers/persistCrypto.d.ts.map +1 -0
  83. package/lib/typescript/helpers/pickImageAsset.d.ts +27 -0
  84. package/lib/typescript/helpers/pickImageAsset.d.ts.map +1 -0
  85. package/lib/typescript/helpers/resolveInitBeforeLoadUser.d.ts.map +1 -1
  86. package/lib/typescript/helpers/secureKeyValue.d.ts +13 -0
  87. package/lib/typescript/helpers/secureKeyValue.d.ts.map +1 -0
  88. package/lib/typescript/helpers/secureStoreRuntime.d.ts +29 -0
  89. package/lib/typescript/helpers/secureStoreRuntime.d.ts.map +1 -0
  90. package/lib/typescript/helpers/secureUserStorage.d.ts +23 -0
  91. package/lib/typescript/helpers/secureUserStorage.d.ts.map +1 -0
  92. package/lib/typescript/hooks/useLogout.d.ts.map +1 -1
  93. package/lib/typescript/networking/authRefresh.d.ts.map +1 -1
  94. package/lib/typescript/roomStore/chatSettingsSlice.d.ts.map +1 -1
  95. package/lib/typescript/roomStore/persistence.d.ts.map +1 -1
  96. package/lib/typescript/roomStore/roomsSlice.d.ts.map +1 -1
  97. package/package.json +8 -1
  98. package/src/components/AuthForms/Login.tsx +2 -3
  99. package/src/components/MainComponents/ProfileImagePlaceholder.tsx +16 -9
  100. package/src/components/Modals/NewChatModal/ModalContent.tsx +6 -3
  101. package/src/components/Modals/NewChatModal/UpdNewChatModal.tsx +12 -1
  102. package/src/components/Modals/UserProfileModal/EditUserModal.tsx +9 -25
  103. package/src/context/xmppProvider.tsx +2 -3
  104. package/src/helpers/ensureScopedChatCache.ts +2 -2
  105. package/src/helpers/persistCrypto.ts +127 -0
  106. package/src/helpers/pickImageAsset.ts +68 -0
  107. package/src/helpers/resolveInitBeforeLoadUser.ts +5 -10
  108. package/src/helpers/secureKeyValue.ts +80 -0
  109. package/src/helpers/secureStoreRuntime.ts +62 -0
  110. package/src/helpers/secureUserStorage.ts +80 -0
  111. package/src/hooks/useLogout.tsx +15 -9
  112. package/src/networking/authRefresh.ts +4 -9
  113. package/src/roomStore/chatSettingsSlice.ts +3 -6
  114. package/src/roomStore/persistence.ts +39 -19
  115. package/src/roomStore/roomsSlice.ts +2 -0
@@ -11,8 +11,7 @@ import {
11
11
  } from '../types/types';
12
12
  import { Iso639_1Codes } from '../types/models/language.model';
13
13
  import type { TranslateMode } from '../utils/translateModePolicy';
14
- import { localStorageConstants } from '../helpers/constants/LOCAL_STORAGE';
15
- import { asyncLocalStorage } from '../hooks/useLocalStorage';
14
+ import { secureUserStorage } from '../helpers/secureUserStorage';
16
15
 
17
16
  export interface ChatState {
18
17
  user: User;
@@ -113,9 +112,7 @@ const initialState: ChatState = {
113
112
  const reducers = {
114
113
  setUser: (state: WritableDraft<ChatState>, action: PayloadAction<User>) => {
115
114
  state.user = unpackAndTransform(action.payload);
116
- asyncLocalStorage(localStorageConstants.ETHORA_USER).set(
117
- unpackAndTransform(action.payload)
118
- );
115
+ secureUserStorage().set(unpackAndTransform(action.payload));
119
116
  },
120
117
  updateUser(state: WritableDraft<ChatState>, action: PayloadAction<{ updates: Partial<User> }>) {
121
118
  const { updates } = action.payload;
@@ -186,7 +183,7 @@ const reducers = {
186
183
  state.client = undefined;
187
184
  state.langSource = undefined;
188
185
  state.translateMode = undefined;
189
- asyncLocalStorage(localStorageConstants.ETHORA_USER).remove();
186
+ secureUserStorage().remove();
190
187
  },
191
188
  setLangSource: (
192
189
  state: WritableDraft<ChatState>,
@@ -1,6 +1,7 @@
1
1
  import AsyncStorage from '@react-native-async-storage/async-storage';
2
2
  import { Middleware } from '@reduxjs/toolkit';
3
3
  import { IMessage, IRoom, User } from '../types/types';
4
+ import { encryptForPersist, decryptFromPersist } from '../helpers/persistCrypto';
4
5
 
5
6
  // -------------------------------------------------------------------
6
7
  // Lightweight RN persistence layer. Mirrors what redux-persist gives
@@ -11,7 +12,11 @@ import { IMessage, IRoom, User } from '../types/types';
11
12
  // - cap each room's messages to the most recent MESSAGE_LIMIT (100)
12
13
  // - debounced writes (200ms) on relevant action types
13
14
  //
14
- // Not encrypted. We rely on AsyncStorage's per-app sandbox.
15
+ // AES-256-CBC at rest (see `helpers/persistCrypto.ts`): the JSON blob
16
+ // under each key below is encrypted before it reaches AsyncStorage, with
17
+ // the symmetric key held in the platform Keychain/Keystore. `user` here
18
+ // is already secret-free (see `sanitizeUser`) — the encryption is for
19
+ // the message bodies in `rooms`, which aren't.
15
20
  // -------------------------------------------------------------------
16
21
 
17
22
  const KEY_CHAT = '@ethora/persist:chatSettingStore';
@@ -199,21 +204,27 @@ export const persistenceMiddleware: Middleware = (storeAPI) => (next) => (
199
204
 
200
205
  if (writeTimer) {clearTimeout(writeTimer);}
201
206
  writeTimer = setTimeout(() => {
202
- try {
203
- const state = storeAPI.getState();
204
- const chatPayload: PersistedChatState = {
205
- user: sanitizeUser(state.chatSettingStore?.user) as User,
206
- };
207
- const roomsPayload: PersistedRoomsState = {
208
- rooms: sanitizeRooms(state.rooms?.rooms || {}),
209
- };
210
- AsyncStorage.multiSet([
211
- [KEY_CHAT, JSON.stringify(chatPayload)],
212
- [KEY_ROOMS, JSON.stringify(roomsPayload)],
213
- ]).catch((e) => console.warn('persist write failed', e));
214
- } catch (e) {
215
- console.warn('persist serialize failed', e);
216
- }
207
+ (async () => {
208
+ try {
209
+ const state = storeAPI.getState();
210
+ const chatPayload: PersistedChatState = {
211
+ user: sanitizeUser(state.chatSettingStore?.user) as User,
212
+ };
213
+ const roomsPayload: PersistedRoomsState = {
214
+ rooms: sanitizeRooms(state.rooms?.rooms || {}),
215
+ };
216
+ const [chatCipher, roomsCipher] = await Promise.all([
217
+ encryptForPersist(JSON.stringify(chatPayload)),
218
+ encryptForPersist(JSON.stringify(roomsPayload)),
219
+ ]);
220
+ await AsyncStorage.multiSet([
221
+ [KEY_CHAT, chatCipher],
222
+ [KEY_ROOMS, roomsCipher],
223
+ ]);
224
+ } catch (e) {
225
+ console.warn('persist write failed', e);
226
+ }
227
+ })();
217
228
  }, 200);
218
229
 
219
230
  return result;
@@ -233,9 +244,18 @@ export async function readPersistedState(): Promise<{
233
244
  KEY_CHAT,
234
245
  KEY_ROOMS,
235
246
  ]);
236
- const chat = chatRaw[1] ? (JSON.parse(chatRaw[1]) as PersistedChatState) : null;
237
- const rooms = roomsRaw[1]
238
- ? (JSON.parse(roomsRaw[1]) as PersistedRoomsState)
247
+ const [chatPlain, roomsPlain] = await Promise.all([
248
+ chatRaw[1] ? decryptFromPersist(chatRaw[1]) : Promise.resolve(null),
249
+ roomsRaw[1] ? decryptFromPersist(roomsRaw[1]) : Promise.resolve(null),
250
+ ]);
251
+ // `decryptFromPersist` returns null both for "nothing stored" and
252
+ // for "stored value isn't a valid envelope for the current key" —
253
+ // notably a plaintext blob left over from a pre-encryption install.
254
+ // Either way there is nothing safe to parse, so the caller treats it
255
+ // as a cold start and re-hydrates from the server.
256
+ const chat = chatPlain ? (JSON.parse(chatPlain) as PersistedChatState) : null;
257
+ const rooms = roomsPlain
258
+ ? (JSON.parse(roomsPlain) as PersistedRoomsState)
239
259
  : null;
240
260
  return { chat, rooms };
241
261
  } catch (e) {
@@ -317,6 +317,8 @@ const reducers = {
317
317
  : [];
318
318
  state.rooms[roomData.jid] = {
319
319
  ...roomData,
320
+ icon: roomData.icon !== undefined ? roomData.icon : existing?.icon,
321
+ roomBg: roomData.roomBg !== undefined ? roomData.roomBg : existing?.roomBg,
320
322
  messages:
321
323
  existingMessages.length > 0 ? existingMessages : incomingMessages,
322
324
  lastViewedTimestamp: lastViewed,