@ethora/chat-component-rn 26.8.3 → 26.8.5

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 (131) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/lib/commonjs/components/Header/HeaderRoomList.js +12 -7
  3. package/lib/commonjs/components/Header/HeaderRoomList.js.map +1 -1
  4. package/lib/commonjs/components/MainComponents/ChatRoom.js +23 -3
  5. package/lib/commonjs/components/MainComponents/ChatRoom.js.map +1 -1
  6. package/lib/commonjs/components/MainComponents/ChatWrapper.js +40 -9
  7. package/lib/commonjs/components/MainComponents/ChatWrapper.js.map +1 -1
  8. package/lib/commonjs/components/MainComponents/KeyboardInputDock.js +40 -0
  9. package/lib/commonjs/components/MainComponents/KeyboardInputDock.js.map +1 -0
  10. package/lib/commonjs/components/MainComponents/ReduxWrapper.js +43 -6
  11. package/lib/commonjs/components/MainComponents/ReduxWrapper.js.map +1 -1
  12. package/lib/commonjs/components/MainComponents/RoomStack.js +202 -0
  13. package/lib/commonjs/components/MainComponents/RoomStack.js.map +1 -0
  14. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js +6 -1
  15. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  16. package/lib/commonjs/components/Modals/Modal/Modal.js +24 -0
  17. package/lib/commonjs/components/Modals/Modal/Modal.js.map +1 -1
  18. package/lib/commonjs/components/Modals/Modal/SwipeBackLayer.js +146 -0
  19. package/lib/commonjs/components/Modals/Modal/SwipeBackLayer.js.map +1 -0
  20. package/lib/commonjs/components/Modals/ProfileHeader/ProfileHeader.js +28 -10
  21. package/lib/commonjs/components/Modals/ProfileHeader/ProfileHeader.js.map +1 -1
  22. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js +9 -3
  23. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  24. package/lib/commonjs/components/Modals/UserSettingsModal/UserSettingsModal.js +136 -3
  25. package/lib/commonjs/components/Modals/UserSettingsModal/UserSettingsModal.js.map +1 -1
  26. package/lib/commonjs/helpers/keyboardLayout.js +23 -1
  27. package/lib/commonjs/helpers/keyboardLayout.js.map +1 -1
  28. package/lib/commonjs/helpers/preferencesStorage.js +44 -0
  29. package/lib/commonjs/helpers/preferencesStorage.js.map +1 -0
  30. package/lib/commonjs/i18n/strings.js +42 -0
  31. package/lib/commonjs/i18n/strings.js.map +1 -1
  32. package/lib/commonjs/main.js +13 -0
  33. package/lib/commonjs/main.js.map +1 -1
  34. package/lib/commonjs/roomStore/chatSettingsSlice.js +17 -4
  35. package/lib/commonjs/roomStore/chatSettingsSlice.js.map +1 -1
  36. package/lib/commonjs/services/pushRegistration.js +13 -2
  37. package/lib/commonjs/services/pushRegistration.js.map +1 -1
  38. package/lib/commonjs/theme/theme.js +28 -4
  39. package/lib/commonjs/theme/theme.js.map +1 -1
  40. package/lib/commonjs/version.js +1 -1
  41. package/lib/module/components/Header/HeaderRoomList.js +12 -7
  42. package/lib/module/components/Header/HeaderRoomList.js.map +1 -1
  43. package/lib/module/components/MainComponents/ChatRoom.js +24 -4
  44. package/lib/module/components/MainComponents/ChatRoom.js.map +1 -1
  45. package/lib/module/components/MainComponents/ChatWrapper.js +41 -10
  46. package/lib/module/components/MainComponents/ChatWrapper.js.map +1 -1
  47. package/lib/module/components/MainComponents/KeyboardInputDock.js +33 -0
  48. package/lib/module/components/MainComponents/KeyboardInputDock.js.map +1 -0
  49. package/lib/module/components/MainComponents/ReduxWrapper.js +47 -10
  50. package/lib/module/components/MainComponents/ReduxWrapper.js.map +1 -1
  51. package/lib/module/components/MainComponents/RoomStack.js +198 -0
  52. package/lib/module/components/MainComponents/RoomStack.js.map +1 -0
  53. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js +6 -1
  54. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  55. package/lib/module/components/Modals/Modal/Modal.js +25 -1
  56. package/lib/module/components/Modals/Modal/Modal.js.map +1 -1
  57. package/lib/module/components/Modals/Modal/SwipeBackLayer.js +140 -0
  58. package/lib/module/components/Modals/Modal/SwipeBackLayer.js.map +1 -0
  59. package/lib/module/components/Modals/ProfileHeader/ProfileHeader.js +29 -11
  60. package/lib/module/components/Modals/ProfileHeader/ProfileHeader.js.map +1 -1
  61. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js +10 -4
  62. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  63. package/lib/module/components/Modals/UserSettingsModal/UserSettingsModal.js +139 -6
  64. package/lib/module/components/Modals/UserSettingsModal/UserSettingsModal.js.map +1 -1
  65. package/lib/module/helpers/keyboardLayout.js +20 -0
  66. package/lib/module/helpers/keyboardLayout.js.map +1 -1
  67. package/lib/module/helpers/preferencesStorage.js +36 -0
  68. package/lib/module/helpers/preferencesStorage.js.map +1 -0
  69. package/lib/module/i18n/strings.js +42 -0
  70. package/lib/module/i18n/strings.js.map +1 -1
  71. package/lib/module/main.js +1 -0
  72. package/lib/module/main.js.map +1 -1
  73. package/lib/module/roomStore/chatSettingsSlice.js +14 -3
  74. package/lib/module/roomStore/chatSettingsSlice.js.map +1 -1
  75. package/lib/module/services/pushRegistration.js +13 -2
  76. package/lib/module/services/pushRegistration.js.map +1 -1
  77. package/lib/module/theme/theme.js +28 -3
  78. package/lib/module/theme/theme.js.map +1 -1
  79. package/lib/module/version.js +1 -1
  80. package/lib/typescript/components/Header/HeaderRoomList.d.ts.map +1 -1
  81. package/lib/typescript/components/MainComponents/ChatRoom.d.ts.map +1 -1
  82. package/lib/typescript/components/MainComponents/ChatWrapper.d.ts.map +1 -1
  83. package/lib/typescript/components/MainComponents/KeyboardInputDock.d.ts +16 -0
  84. package/lib/typescript/components/MainComponents/KeyboardInputDock.d.ts.map +1 -0
  85. package/lib/typescript/components/MainComponents/ReduxWrapper.d.ts.map +1 -1
  86. package/lib/typescript/components/MainComponents/RoomStack.d.ts +37 -0
  87. package/lib/typescript/components/MainComponents/RoomStack.d.ts.map +1 -0
  88. package/lib/typescript/components/Modals/ChatProfileModal/ChatProfileModal.d.ts.map +1 -1
  89. package/lib/typescript/components/Modals/Modal/Modal.d.ts.map +1 -1
  90. package/lib/typescript/components/Modals/Modal/SwipeBackLayer.d.ts +15 -0
  91. package/lib/typescript/components/Modals/Modal/SwipeBackLayer.d.ts.map +1 -0
  92. package/lib/typescript/components/Modals/ProfileHeader/ProfileHeader.d.ts.map +1 -1
  93. package/lib/typescript/components/Modals/UserProfileModal/UserProfileModal.d.ts.map +1 -1
  94. package/lib/typescript/components/Modals/UserSettingsModal/UserSettingsModal.d.ts.map +1 -1
  95. package/lib/typescript/helpers/keyboardLayout.d.ts +6 -0
  96. package/lib/typescript/helpers/keyboardLayout.d.ts.map +1 -1
  97. package/lib/typescript/helpers/preferencesStorage.d.ts +11 -0
  98. package/lib/typescript/helpers/preferencesStorage.d.ts.map +1 -0
  99. package/lib/typescript/i18n/strings.d.ts.map +1 -1
  100. package/lib/typescript/main.d.ts +2 -0
  101. package/lib/typescript/main.d.ts.map +1 -1
  102. package/lib/typescript/roomStore/chatSettingsSlice.d.ts +12 -1
  103. package/lib/typescript/roomStore/chatSettingsSlice.d.ts.map +1 -1
  104. package/lib/typescript/services/pushRegistration.d.ts.map +1 -1
  105. package/lib/typescript/theme/theme.d.ts +17 -3
  106. package/lib/typescript/theme/theme.d.ts.map +1 -1
  107. package/lib/typescript/types/types.d.ts +23 -1
  108. package/lib/typescript/types/types.d.ts.map +1 -1
  109. package/lib/typescript/version.d.ts +1 -1
  110. package/package.json +2 -2
  111. package/src/components/Header/HeaderRoomList.tsx +7 -5
  112. package/src/components/MainComponents/ChatRoom.tsx +29 -3
  113. package/src/components/MainComponents/ChatWrapper.tsx +46 -12
  114. package/src/components/MainComponents/KeyboardInputDock.tsx +47 -0
  115. package/src/components/MainComponents/ReduxWrapper.tsx +52 -11
  116. package/src/components/MainComponents/RoomStack.tsx +236 -0
  117. package/src/components/Modals/ChatProfileModal/ChatProfileModal.tsx +4 -0
  118. package/src/components/Modals/Modal/Modal.tsx +32 -1
  119. package/src/components/Modals/Modal/SwipeBackLayer.tsx +170 -0
  120. package/src/components/Modals/ProfileHeader/ProfileHeader.tsx +29 -8
  121. package/src/components/Modals/UserProfileModal/UserProfileModal.tsx +6 -3
  122. package/src/components/Modals/UserSettingsModal/UserSettingsModal.tsx +157 -4
  123. package/src/helpers/keyboardLayout.ts +26 -0
  124. package/src/helpers/preferencesStorage.ts +40 -0
  125. package/src/i18n/strings.ts +42 -0
  126. package/src/main.ts +2 -0
  127. package/src/roomStore/chatSettingsSlice.ts +24 -2
  128. package/src/services/pushRegistration.ts +15 -2
  129. package/src/theme/theme.ts +39 -5
  130. package/src/types/types.ts +24 -1
  131. package/src/version.ts +1 -1
@@ -12,6 +12,7 @@ import {
12
12
  import { Iso639_1Codes } from '../types/models/language.model';
13
13
  import type { TranslateMode } from '../utils/translateModePolicy';
14
14
  import { secureUserStorage } from '../helpers/secureUserStorage';
15
+ import { applyThemePreference, type ThemePreference } from '../theme/theme';
15
16
 
16
17
  export interface ChatState {
17
18
  user: User;
@@ -29,6 +30,14 @@ export interface ChatState {
29
30
  * See utils/translateModePolicy.
30
31
  */
31
32
  translateMode?: TranslateMode;
33
+ /**
34
+ * Appearance picked in Settings (light / dark / system). Persisted per
35
+ * device; folded into `config.dark` by setConfig / setThemePreference so
36
+ * every consumer of `config` sees the effective value.
37
+ */
38
+ themePreference?: ThemePreference;
39
+ /** Push notifications toggle from Settings. `undefined` = on. */
40
+ pushEnabled?: boolean;
32
41
  }
33
42
 
34
43
  export const unpackAndTransform = (input?: User): User => {
@@ -126,11 +135,22 @@ const reducers = {
126
135
  };
127
136
  }
128
137
  },
138
+ setThemePreference: (
139
+ state: WritableDraft<ChatState>,
140
+ action: PayloadAction<ThemePreference | undefined>
141
+ ) => {
142
+ state.themePreference = action.payload;
143
+ state.config = applyThemePreference(state.config as IConfig | undefined, action.payload) as any;
144
+ },
145
+ setPushEnabled: (state: WritableDraft<ChatState>, action: PayloadAction<boolean>) => {
146
+ state.pushEnabled = action.payload;
147
+ },
129
148
  setConfig: (state: WritableDraft<ChatState>, action: PayloadAction<IConfig | undefined>) => {
130
149
  // Cast away immer's WritableDraft — IConfig contains callable
131
150
  // fields (eventHandlers, customComponent) that confuse the draft
132
- // type inference but are inert at write-time.
133
- state.config = action.payload as any;
151
+ // type inference but are inert at write-time. The user's in-app
152
+ // appearance choice survives a host re-dispatch.
153
+ state.config = applyThemePreference(action.payload, state.themePreference) as any;
134
154
  },
135
155
  setActiveModal: (state: WritableDraft<ChatState>, action: PayloadAction<ModalType | undefined>) => {
136
156
  state.activeModal = action.payload;
@@ -220,6 +240,8 @@ export const {
220
240
  setStoreClient,
221
241
  setLangSource,
222
242
  setTranslateMode,
243
+ setThemePreference,
244
+ setPushEnabled,
223
245
  } = chatSlice.actions;
224
246
 
225
247
  export default chatSlice.reducer;
@@ -70,7 +70,10 @@ const readSession = (): Session => {
70
70
  xmppUsername: String(user?.xmppUsername || ''),
71
71
  projectName: String(config?.projectName || '').trim(),
72
72
  gatewayUrl: String(config?.pushNotifications?.apiUrl || '').trim(),
73
- pushDisabled: config?.pushNotifications?.enabled === false,
73
+ // Off by config (host) or by the user's Settings toggle.
74
+ pushDisabled:
75
+ config?.pushNotifications?.enabled === false ||
76
+ slice?.pushEnabled === false,
74
77
  };
75
78
  };
76
79
 
@@ -233,7 +236,17 @@ const ensureWatching = (): void => {
233
236
  hookIdentity = null;
234
237
  return;
235
238
  }
236
- if (session.pushDisabled || !isReady(session)) {
239
+ if (session.pushDisabled) {
240
+ // Switched off (Settings toggle or config): take the device off the
241
+ // backend but keep the tokens, so switching back on re-registers
242
+ // without the host doing anything. `registered` empties at once,
243
+ // so later store updates do not repeat the release.
244
+ if (registered.size > 0) {
245
+ releasePushRegistration().catch(() => undefined);
246
+ }
247
+ return;
248
+ }
249
+ if (!isReady(session)) {
237
250
  return;
238
251
  }
239
252
  runTokenHook(session);
@@ -1,5 +1,7 @@
1
1
  /** @format */
2
2
 
3
+ import { Appearance } from 'react-native';
4
+
3
5
  /**
4
6
  * Chat theme.
5
7
  *
@@ -161,7 +163,10 @@ export const DARK_THEME: ChatThemeColors = {
161
163
  /** The subset of IConfig the theme depends on (kept structural so the
162
164
  * helpers can be called with partial configs in tests). */
163
165
  export interface ThemeConfigInput {
164
- dark?: boolean;
166
+ /** `true` forces dark, `'system'` follows the OS appearance (pass the
167
+ * current scheme as `systemDark` to resolveTheme / isDarkTheme; omitted,
168
+ * the OS scheme is read directly). */
169
+ dark?: boolean | 'system';
165
170
  darkColors?: ChatThemeOverrides;
166
171
  colors?: {
167
172
  primary?: string;
@@ -179,8 +184,16 @@ export interface ThemeConfigInput {
179
184
  backgroundChat?: { color?: string };
180
185
  }
181
186
 
182
- export const isDarkTheme = (config?: ThemeConfigInput | null): boolean =>
183
- !!config?.dark;
187
+ /** The OS appearance right now — the fallback when a caller has no hook. */
188
+ // Optional chaining on `Appearance` itself: tests that mock react-native
189
+ // partially leave it undefined, and a missing module must read as light.
190
+ const systemIsDark = (): boolean => Appearance?.getColorScheme?.() === 'dark';
191
+
192
+ export const isDarkTheme = (
193
+ config?: ThemeConfigInput | null,
194
+ systemDark: boolean = systemIsDark()
195
+ ): boolean =>
196
+ config?.dark === 'system' ? systemDark : config?.dark === true;
184
197
 
185
198
  const pick = <T>(...values: (T | undefined | null | '')[]): T | undefined =>
186
199
  values.find((v) => v !== undefined && v !== null && v !== '') as
@@ -196,8 +209,11 @@ const stripUndefined = (o: ChatThemeOverrides): ChatThemeOverrides =>
196
209
  * Build the effective palette for a config. Pure; memoize at the call
197
210
  * site (useTheme does).
198
211
  */
199
- export const resolveTheme = (config?: ThemeConfigInput | null): ChatTheme => {
200
- if (isDarkTheme(config)) {
212
+ export const resolveTheme = (
213
+ config?: ThemeConfigInput | null,
214
+ systemDark?: boolean
215
+ ): ChatTheme => {
216
+ if (isDarkTheme(config, systemDark)) {
201
217
  const overrides = stripUndefined(config?.darkColors ?? {});
202
218
  const primary = pick(overrides.primary, DARK_THEME.primary)!;
203
219
  return {
@@ -241,3 +257,21 @@ export const resolveTheme = (config?: ThemeConfigInput | null): ChatTheme => {
241
257
  statusBarStyle: 'dark-content',
242
258
  };
243
259
  };
260
+
261
+ /** In-app appearance choice from Settings; `undefined` = follow the host's config.dark. */
262
+ export type ThemePreference = 'light' | 'dark' | 'system';
263
+
264
+ /**
265
+ * Fold the user's in-app appearance choice into a config: the choice wins
266
+ * over the host's `dark` (a host that wants to lock the theme hides the
267
+ * setting with `settings.hideAppearance`). Returns the same object when
268
+ * nothing changes, so memoised consumers do not re-render.
269
+ */
270
+ export const applyThemePreference = <T extends { dark?: boolean | 'system' }>(
271
+ config: T | undefined,
272
+ preference?: ThemePreference
273
+ ): T | undefined => {
274
+ if (!preference || !config) {return config;}
275
+ const dark = preference === 'system' ? ('system' as const) : preference === 'dark';
276
+ return config.dark === dark ? config : { ...config, dark };
277
+ };
@@ -386,8 +386,10 @@ export interface IConfig {
386
386
  * not applied — pass brand colours via `darkColors` instead.
387
387
  *
388
388
  * config: { dark: true, darkColors: { primary: '#7C3AED', surface: '#111' } }
389
+ *
390
+ * `'system'` follows the device appearance (light/dark) automatically.
389
391
  */
390
- dark?: boolean;
392
+ dark?: boolean | 'system';
391
393
  /** Per-entry overrides of the dark palette; see ChatThemeColors. */
392
394
  darkColors?: ChatThemeOverrides;
393
395
  /** Configurable font family / weights for the chat UI. See TypographyConfig. */
@@ -787,8 +789,29 @@ export interface IConfig {
787
789
  firebaseConfig?: FBConfig;
788
790
  };
789
791
 
792
+ /** Settings screen switches. */
793
+ settings?: {
794
+ /** Hide the Light / Dark / System appearance picker (host locks the theme via `dark`). */
795
+ hideAppearance?: boolean;
796
+ /** Hide the push-notifications toggle. */
797
+ hidePushToggle?: boolean;
798
+ };
799
+
790
800
  // ----- event hooks -----
791
801
  eventHandlers?: {
802
+ /**
803
+ * Fired when the user picks an appearance in Settings (and once on
804
+ * mount with the persisted choice). `isDark` is the resolved value
805
+ * for 'system'. Hosts use it to sync their own chrome (status bar).
806
+ */
807
+ onThemeChange?: (preference: 'light' | 'dark' | 'system', isDark: boolean) => void;
808
+ /**
809
+ * Fired when the user toggles push notifications in Settings. The SDK
810
+ * itself stops delivering: off releases every device-token
811
+ * registration on the backend (tokens are kept), on registers them
812
+ * again. A host that also owns native push should mirror the choice.
813
+ */
814
+ onPushNotificationsToggle?: (enabled: boolean) => void | Promise<void>;
792
815
  onMessageSent?: (event: {
793
816
  message: string;
794
817
  roomJID: string;
package/src/version.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  // Generated by scripts/sync-version.mjs on `npm version`.
2
2
  // Edit package.json's version instead of this file.
3
- export const ETHORA_CHAT_COMPONENT_VERSION = '26.8.2';
3
+ export const ETHORA_CHAT_COMPONENT_VERSION = '26.8.4';