@ethora/chat-component-rn 26.6.13 → 26.7.2
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 +16 -0
- package/README.md +22 -0
- package/lib/commonjs/assets/icons.js +2 -2
- package/lib/commonjs/assets/icons.js.map +1 -1
- package/lib/commonjs/components/AuthForms/Login.js +2 -3
- package/lib/commonjs/components/AuthForms/Login.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ProfileImagePlaceholder.js +9 -8
- package/lib/commonjs/components/MainComponents/ProfileImagePlaceholder.js.map +1 -1
- package/lib/commonjs/components/Menu/HeaderRoomListMenu.js +110 -37
- package/lib/commonjs/components/Menu/HeaderRoomListMenu.js.map +1 -1
- package/lib/commonjs/components/Modals/AttachSheet/AttachSheet.js +16 -14
- package/lib/commonjs/components/Modals/AttachSheet/AttachSheet.js.map +1 -1
- package/lib/commonjs/components/Modals/NewChatModal/ModalContent.js +2 -1
- package/lib/commonjs/components/Modals/NewChatModal/ModalContent.js.map +1 -1
- package/lib/commonjs/components/Modals/NewChatModal/UpdNewChatModal.js +8 -0
- package/lib/commonjs/components/Modals/NewChatModal/UpdNewChatModal.js.map +1 -1
- package/lib/commonjs/components/Modals/UserProfileModal/EditUserModal.js +7 -21
- package/lib/commonjs/components/Modals/UserProfileModal/EditUserModal.js.map +1 -1
- package/lib/commonjs/components/styled/SendInput.js +0 -10
- package/lib/commonjs/components/styled/SendInput.js.map +1 -1
- package/lib/commonjs/context/xmppProvider.js +2 -3
- package/lib/commonjs/context/xmppProvider.js.map +1 -1
- package/lib/commonjs/helpers/ensureScopedChatCache.js +2 -2
- package/lib/commonjs/helpers/ensureScopedChatCache.js.map +1 -1
- package/lib/commonjs/helpers/persistCrypto.js +122 -0
- package/lib/commonjs/helpers/persistCrypto.js.map +1 -0
- package/lib/commonjs/helpers/pickImageAsset.js +68 -0
- package/lib/commonjs/helpers/pickImageAsset.js.map +1 -0
- package/lib/commonjs/helpers/resolveInitBeforeLoadUser.js +5 -6
- package/lib/commonjs/helpers/resolveInitBeforeLoadUser.js.map +1 -1
- package/lib/commonjs/helpers/secureKeyValue.js +83 -0
- package/lib/commonjs/helpers/secureKeyValue.js.map +1 -0
- package/lib/commonjs/helpers/secureStoreRuntime.js +59 -0
- package/lib/commonjs/helpers/secureStoreRuntime.js.map +1 -0
- package/lib/commonjs/helpers/secureUserStorage.js +75 -0
- package/lib/commonjs/helpers/secureUserStorage.js.map +1 -0
- package/lib/commonjs/hooks/useLogout.js +15 -7
- package/lib/commonjs/hooks/useLogout.js.map +1 -1
- package/lib/commonjs/main.js +6 -0
- package/lib/commonjs/main.js.map +1 -1
- package/lib/commonjs/networking/authRefresh.js +4 -5
- package/lib/commonjs/networking/authRefresh.js.map +1 -1
- package/lib/commonjs/roomStore/chatSettingsSlice.js +3 -4
- package/lib/commonjs/roomStore/chatSettingsSlice.js.map +1 -1
- package/lib/commonjs/roomStore/persistence.js +29 -15
- package/lib/commonjs/roomStore/persistence.js.map +1 -1
- package/lib/commonjs/roomStore/roomsSlice.js +2 -0
- package/lib/commonjs/roomStore/roomsSlice.js.map +1 -1
- package/lib/module/assets/icons.js +1 -1
- package/lib/module/assets/icons.js.map +1 -1
- package/lib/module/components/AuthForms/Login.js +2 -3
- package/lib/module/components/AuthForms/Login.js.map +1 -1
- package/lib/module/components/MainComponents/ProfileImagePlaceholder.js +9 -8
- package/lib/module/components/MainComponents/ProfileImagePlaceholder.js.map +1 -1
- package/lib/module/components/Menu/HeaderRoomListMenu.js +110 -38
- package/lib/module/components/Menu/HeaderRoomListMenu.js.map +1 -1
- package/lib/module/components/Modals/AttachSheet/AttachSheet.js +16 -14
- package/lib/module/components/Modals/AttachSheet/AttachSheet.js.map +1 -1
- package/lib/module/components/Modals/NewChatModal/ModalContent.js +2 -1
- package/lib/module/components/Modals/NewChatModal/ModalContent.js.map +1 -1
- package/lib/module/components/Modals/NewChatModal/UpdNewChatModal.js +8 -0
- package/lib/module/components/Modals/NewChatModal/UpdNewChatModal.js.map +1 -1
- package/lib/module/components/Modals/UserProfileModal/EditUserModal.js +9 -23
- package/lib/module/components/Modals/UserProfileModal/EditUserModal.js.map +1 -1
- package/lib/module/components/styled/SendInput.js +1 -11
- package/lib/module/components/styled/SendInput.js.map +1 -1
- package/lib/module/context/xmppProvider.js +2 -3
- package/lib/module/context/xmppProvider.js.map +1 -1
- package/lib/module/helpers/ensureScopedChatCache.js +2 -2
- package/lib/module/helpers/ensureScopedChatCache.js.map +1 -1
- package/lib/module/helpers/persistCrypto.js +115 -0
- package/lib/module/helpers/persistCrypto.js.map +1 -0
- package/lib/module/helpers/pickImageAsset.js +63 -0
- package/lib/module/helpers/pickImageAsset.js.map +1 -0
- package/lib/module/helpers/resolveInitBeforeLoadUser.js +5 -6
- package/lib/module/helpers/resolveInitBeforeLoadUser.js.map +1 -1
- package/lib/module/helpers/secureKeyValue.js +75 -0
- package/lib/module/helpers/secureKeyValue.js.map +1 -0
- package/lib/module/helpers/secureStoreRuntime.js +52 -0
- package/lib/module/helpers/secureStoreRuntime.js.map +1 -0
- package/lib/module/helpers/secureUserStorage.js +72 -0
- package/lib/module/helpers/secureUserStorage.js.map +1 -0
- package/lib/module/hooks/useLogout.js +15 -7
- package/lib/module/hooks/useLogout.js.map +1 -1
- package/lib/module/main.js +1 -1
- package/lib/module/main.js.map +1 -1
- package/lib/module/networking/authRefresh.js +4 -5
- package/lib/module/networking/authRefresh.js.map +1 -1
- package/lib/module/roomStore/chatSettingsSlice.js +3 -4
- package/lib/module/roomStore/chatSettingsSlice.js.map +1 -1
- package/lib/module/roomStore/persistence.js +30 -15
- package/lib/module/roomStore/persistence.js.map +1 -1
- package/lib/module/roomStore/roomsSlice.js +2 -0
- package/lib/module/roomStore/roomsSlice.js.map +1 -1
- package/lib/typescript/assets/icons.d.ts +4 -0
- package/lib/typescript/assets/icons.d.ts.map +1 -1
- package/lib/typescript/components/AuthForms/Login.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/ProfileImagePlaceholder.d.ts.map +1 -1
- package/lib/typescript/components/Menu/HeaderRoomListMenu.d.ts +3 -0
- package/lib/typescript/components/Menu/HeaderRoomListMenu.d.ts.map +1 -1
- package/lib/typescript/components/Modals/AttachSheet/AttachSheet.d.ts.map +1 -1
- package/lib/typescript/components/Modals/NewChatModal/ModalContent.d.ts +4 -2
- package/lib/typescript/components/Modals/NewChatModal/ModalContent.d.ts.map +1 -1
- package/lib/typescript/components/Modals/NewChatModal/UpdNewChatModal.d.ts.map +1 -1
- package/lib/typescript/components/Modals/UserProfileModal/EditUserModal.d.ts.map +1 -1
- package/lib/typescript/components/styled/SendInput.d.ts.map +1 -1
- package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
- package/lib/typescript/helpers/persistCrypto.d.ts +13 -0
- package/lib/typescript/helpers/persistCrypto.d.ts.map +1 -0
- package/lib/typescript/helpers/pickImageAsset.d.ts +27 -0
- package/lib/typescript/helpers/pickImageAsset.d.ts.map +1 -0
- package/lib/typescript/helpers/resolveInitBeforeLoadUser.d.ts.map +1 -1
- package/lib/typescript/helpers/secureKeyValue.d.ts +13 -0
- package/lib/typescript/helpers/secureKeyValue.d.ts.map +1 -0
- package/lib/typescript/helpers/secureStoreRuntime.d.ts +29 -0
- package/lib/typescript/helpers/secureStoreRuntime.d.ts.map +1 -0
- package/lib/typescript/helpers/secureUserStorage.d.ts +23 -0
- package/lib/typescript/helpers/secureUserStorage.d.ts.map +1 -0
- package/lib/typescript/hooks/useLogout.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/authRefresh.d.ts.map +1 -1
- package/lib/typescript/roomStore/chatSettingsSlice.d.ts.map +1 -1
- package/lib/typescript/roomStore/persistence.d.ts.map +1 -1
- package/lib/typescript/roomStore/roomsSlice.d.ts.map +1 -1
- package/lib/typescript/types/types.d.ts +12 -0
- package/lib/typescript/types/types.d.ts.map +1 -1
- package/package.json +8 -1
- package/src/assets/icons.tsx +19 -0
- package/src/components/AuthForms/Login.tsx +2 -3
- package/src/components/MainComponents/ProfileImagePlaceholder.tsx +16 -9
- package/src/components/Menu/HeaderRoomListMenu.tsx +105 -11
- package/src/components/Modals/AttachSheet/AttachSheet.tsx +16 -14
- package/src/components/Modals/NewChatModal/ModalContent.tsx +6 -3
- package/src/components/Modals/NewChatModal/UpdNewChatModal.tsx +12 -1
- package/src/components/Modals/UserProfileModal/EditUserModal.tsx +9 -25
- package/src/components/styled/SendInput.tsx +0 -10
- package/src/context/xmppProvider.tsx +2 -3
- package/src/helpers/ensureScopedChatCache.ts +2 -2
- package/src/helpers/persistCrypto.ts +127 -0
- package/src/helpers/pickImageAsset.ts +68 -0
- package/src/helpers/resolveInitBeforeLoadUser.ts +5 -10
- package/src/helpers/secureKeyValue.ts +80 -0
- package/src/helpers/secureStoreRuntime.ts +62 -0
- package/src/helpers/secureUserStorage.ts +80 -0
- package/src/hooks/useLogout.tsx +15 -9
- package/src/main.ts +1 -1
- package/src/networking/authRefresh.ts +4 -9
- package/src/roomStore/chatSettingsSlice.ts +3 -6
- package/src/roomStore/persistence.ts +39 -19
- package/src/roomStore/roomsSlice.ts +2 -0
- package/src/types/types.ts +14 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,22 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this pr
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **`config.logout` — built-in "Sign out" item in the room-list header menu.** `{ enabled, label?, confirm?, onBeforeLogout?, onAfterLogout? }`. Rendered last (after New Chat / Profile / Settings) with a logout icon tinted `colors.primary`; hidden unless `enabled: true`, so existing menus are unchanged. Tap flow: close drawer → native confirm (`confirm` defaults to `true`; pass an object to customise title/message/button copy, `false` to skip) → `await onBeforeLogout?.()` (return `false` to cancel) → `await logoutService.performLogout()` (full teardown: XMPP, redux, persisted slices, AsyncStorage, REST cache) → `await onAfterLogout?.()` for the host's own session/navigation logout. Host callback errors are caught and logged via `console.warn` — a throwing `onBeforeLogout` cancels, a throwing `onAfterLogout` is swallowed after the teardown has landed. Documented in README ("Logging out") and `instructions.md`. Tests in `headerRoomListMenuLogout.test.tsx`.
|
|
11
|
+
- **`useLogout` is now exported from the package root** (previously only `logoutService`), so a host can build its own button with the same awaitable teardown.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **iOS: attach-sheet rows (Take photo / Photo or video / Document) did nothing, and the next tap threw `PickingInProgressException: Different document picking in progress`.** The picker handler was invoked from the exit-animation completion callback in the same tick as `setMounted(false)` — but that only *starts* RN `<Modal>`'s native dismiss, so the sheet's UIViewController was still on screen when `expo-image-picker` / `expo-document-picker` called `currentViewController().present(picker)`. UIKit silently dropped the present, the promise never resolved, and expo-document-picker kept its `pickingContext` stuck. `AttachSheet` now fires the pending handler from `<Modal onDismiss>` on iOS (the only signal that the view controller is really gone); Android, where `onDismiss` never fires, keeps running it from the animation callback. The handler is nulled before invocation so it runs at most once, and an interrupted animation (`finished: false`) still does nothing. Tests in `attachSheetPendingHandler.test.tsx`. On iOS, picker rows also skip the sheet's slide-down (the modal is hidden immediately, so `onDismiss` arrives ~50 ms after the tap instead of ~300 ms); Cancel and backdrop taps keep the animated close. The old 350 ms iOS `setTimeout` in `SendInput.handleFileSelection` that tried to paper over the same race is removed — the Document picker now opens as soon as the sheet is gone instead of noticeably later.
|
|
16
|
+
|
|
17
|
+
### Security
|
|
18
|
+
|
|
19
|
+
- **Chat tokens, the XMPP password, and message history are no longer stored in plaintext AsyncStorage.** All four bearer credentials on `User` (REST access + refresh token, XMPP password, secure-media fileToken) now go through the platform Keychain (iOS) / Keystore-backed EncryptedSharedPreferences (Android) via the optional `expo-secure-store` peer dependency, instead of sitting in AsyncStorage's plain SQLite/plist storage. The persisted message-history and room-list cache (`roomStore/persistence.ts`) is now AES-256-CBC encrypted at rest, with the symmetric key itself held in that same secure store — envelope encryption, since bulk history is too large (and unbounded) for direct Keychain storage. `expo-secure-store` is optional (mirrors `expo-video`/`expo-audio` etc.): a host that hasn't added it falls back to namespaced-but-still-unencrypted AsyncStorage with one logged warning, so nothing breaks for existing consumers, but the secure path is what ships by default for any host on the standard Expo dependency set.
|
|
20
|
+
- New `helpers/secureStoreRuntime.ts` (lazy `expo-secure-store` bridge, same pattern as `livekitRuntime.ts`), `helpers/secureKeyValue.ts` (get/set/remove with the AsyncStorage fallback), `helpers/secureUserStorage.ts` (splits `User` into a secret-free AsyncStorage profile + four Keychain-backed secret keys behind the same `{get,set,remove}` shape the old `asyncLocalStorage<User>(ETHORA_USER)` had, so every call site — `chatSettingsSlice`, `authRefresh`, `resolveInitBeforeLoadUser`, `xmppProvider`, `ensureScopedChatCache`, `useLogout`, the testbed's `Login.tsx` — swapped in without changing behavior), `helpers/persistCrypto.ts` (AES-256-CBC envelope encryption for the bulk cache, random IV per write via `react-native-get-random-values`, key generated once and cached).
|
|
21
|
+
- A pre-encryption plaintext cache from an older install decrypts to `null` (not a crash) and is treated as an empty cache — the app just re-hydrates from the server, same as any cold start.
|
|
22
|
+
- Verified on-device (iOS Simulator, real `expo-secure-store` linked via CocoaPods, not the jest mock): no fallback warning logged through a full login → XMPP connect → room list → message render cycle; the raw `@ethora/chat-component-user` AsyncStorage blob has all four secret fields as empty strings with no JWT-shaped substring anywhere in it; the raw `@ethora/persist:rooms` blob is a genuine `{v,iv,ct}` AES envelope containing none of the actual on-screen message text; logout leaves only the non-secret tenant-scope key on disk. 18 new unit tests (`secureStorage.test.ts`) plus updated coverage in `authRefresh.test.ts`, `resolveInitBeforeLoadUser.test.ts`, and both `persistence*.test.ts` suites (760 total, all green); `tsc` clean; real `bob build` (the exact step `npm publish`'s `prepack` runs) succeeds.
|
|
23
|
+
|
|
8
24
|
The repo itself now lives on **Expo SDK 57 (RN 0.86.2, React 19.2.8, New Architecture)** — until now the SDK was only *compatible* with 57 (peer ranges verified in 26.6.3) while developing and testing against 54. Published peer ranges are unchanged, so SDK 54 consumers are unaffected; both lines are now proven by the CI consumer-smoke matrix.
|
|
9
25
|
|
|
10
26
|
### Changed
|
package/README.md
CHANGED
|
@@ -271,6 +271,28 @@ function SignOutButton() {
|
|
|
271
271
|
}
|
|
272
272
|
```
|
|
273
273
|
|
|
274
|
+
### Built-in "Sign out" menu item
|
|
275
|
+
|
|
276
|
+
Don't want to build your own button? Enable the item in the room-list header menu (the drawer with New Chat / Profile / Settings). It renders last, tinted with `config.colors.primary`, and runs the same awaitable teardown as `useLogout()`:
|
|
277
|
+
|
|
278
|
+
```tsx
|
|
279
|
+
<Chat
|
|
280
|
+
config={{
|
|
281
|
+
logout: {
|
|
282
|
+
enabled: true,
|
|
283
|
+
label: 'Sign out', // default
|
|
284
|
+
confirm: { message: 'Sign out of chat?' }, // `true` (default) uses stock copy, `false` skips the dialog
|
|
285
|
+
onBeforeLogout: async () => {
|
|
286
|
+
// return false to cancel (e.g. unsaved draft guard)
|
|
287
|
+
},
|
|
288
|
+
onAfterLogout: () => navigation.replace('SignIn'), // runs AFTER the full teardown
|
|
289
|
+
},
|
|
290
|
+
}}
|
|
291
|
+
/>
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Tap flow: close drawer → confirmation (native `Alert`) → `await onBeforeLogout?.()` (`false` cancels) → `await logoutService.performLogout()` → `await onAfterLogout?.()`. The host-side session/navigation logout belongs in `onAfterLogout` — by the time it runs, XMPP is disconnected and every persisted key is gone. Errors thrown by either callback are caught and logged via `console.warn`; a throwing `onBeforeLogout` cancels the logout. With `enabled: false` (or the option omitted) the menu is unchanged.
|
|
295
|
+
|
|
274
296
|
Why awaitable: the persistence layer debounces writes by 200 ms, and the chat slice removes its persisted user fire-and-forget. If the host navigated / re-mounted `<Chat>` immediately after a non-awaited call, the next bootstrap could occasionally rehydrate stale state ("old chats reappear"). Awaiting the returned promise eliminates that race. The function never rejects — any internal failure is logged via `console.warn`, so a non-awaited call still won't crash the host. For non-React contexts you can call `logoutService.performLogout()` directly (same Promise).
|
|
275
297
|
|
|
276
298
|
## Customization flags worth knowing
|