@ethora/chat-component-rn 26.8.7 → 26.8.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.
Files changed (195) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +25 -3
  3. package/lib/commonjs/components/MainComponents/ChatWrapper.js +5 -3
  4. package/lib/commonjs/components/MainComponents/ChatWrapper.js.map +1 -1
  5. package/lib/commonjs/components/MainComponents/ReduxWrapper.js +8 -0
  6. package/lib/commonjs/components/MainComponents/ReduxWrapper.js.map +1 -1
  7. package/lib/commonjs/components/Menu/HeaderRoomListMenu.js +26 -13
  8. package/lib/commonjs/components/Menu/HeaderRoomListMenu.js.map +1 -1
  9. package/lib/commonjs/components/MenuRoom/MenuRoom.js +5 -3
  10. package/lib/commonjs/components/MenuRoom/MenuRoom.js.map +1 -1
  11. package/lib/commonjs/components/MessageBubble/Message.js +3 -3
  12. package/lib/commonjs/components/MessageBubble/Message.js.map +1 -1
  13. package/lib/commonjs/components/MessageBubble/MessageInteractions.js +5 -3
  14. package/lib/commonjs/components/MessageBubble/MessageInteractions.js.map +1 -1
  15. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js +1 -1
  16. package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  17. package/lib/commonjs/components/Modals/ChatProfileModal/ReportChatModal.js +2 -2
  18. package/lib/commonjs/components/Modals/ChatProfileModal/ReportChatModal.js.map +1 -1
  19. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js +7 -5
  20. package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
  21. package/lib/commonjs/components/Modals/Modal/Modal.js +33 -11
  22. package/lib/commonjs/components/Modals/Modal/Modal.js.map +1 -1
  23. package/lib/commonjs/components/Modals/NewChatModal/NewChatModal.js +8 -8
  24. package/lib/commonjs/components/Modals/NewChatModal/NewChatModal.js.map +1 -1
  25. package/lib/commonjs/components/Modals/SettingsModals/ChangePassword/ChangePasswordModal.js +279 -0
  26. package/lib/commonjs/components/Modals/SettingsModals/ChangePassword/ChangePasswordModal.js.map +1 -0
  27. package/lib/commonjs/components/Modals/SettingsModals/ManageDataModal/ManageDataModal.js +10 -8
  28. package/lib/commonjs/components/Modals/SettingsModals/ManageDataModal/ManageDataModal.js.map +1 -1
  29. package/lib/commonjs/components/Modals/SettingsModals/Visibility/VisibilityModal.js +13 -11
  30. package/lib/commonjs/components/Modals/SettingsModals/Visibility/VisibilityModal.js.map +1 -1
  31. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js +21 -9
  32. package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  33. package/lib/commonjs/components/Modals/UserSettingsModal/LanguageCard.js +121 -0
  34. package/lib/commonjs/components/Modals/UserSettingsModal/LanguageCard.js.map +1 -0
  35. package/lib/commonjs/components/Modals/UserSettingsModal/UserSettingsModal.js +11 -6
  36. package/lib/commonjs/components/Modals/UserSettingsModal/UserSettingsModal.js.map +1 -1
  37. package/lib/commonjs/components/Thread/ThreadWrapper.js +4 -2
  38. package/lib/commonjs/components/Thread/ThreadWrapper.js.map +1 -1
  39. package/lib/commonjs/e2ee/stanza.js +1 -1
  40. package/lib/commonjs/e2ee/stanza.js.map +1 -1
  41. package/lib/commonjs/helpers/constants/MODAL_COMPONENTS.js +2 -0
  42. package/lib/commonjs/helpers/constants/MODAL_COMPONENTS.js.map +1 -1
  43. package/lib/commonjs/helpers/constants/MODAL_TYPES.js +1 -0
  44. package/lib/commonjs/helpers/constants/MODAL_TYPES.js.map +1 -1
  45. package/lib/commonjs/helpers/preferencesStorage.js.map +1 -1
  46. package/lib/commonjs/hooks/useChatSettingState.js +2 -0
  47. package/lib/commonjs/hooks/useChatSettingState.js.map +1 -1
  48. package/lib/commonjs/hooks/useChatWrapperInit.js +4 -0
  49. package/lib/commonjs/hooks/useChatWrapperInit.js.map +1 -1
  50. package/lib/commonjs/i18n/strings.js +187 -1
  51. package/lib/commonjs/i18n/strings.js.map +1 -1
  52. package/lib/commonjs/i18n/useT.js +8 -4
  53. package/lib/commonjs/i18n/useT.js.map +1 -1
  54. package/lib/commonjs/networking/api-requests/user.api.js +13 -0
  55. package/lib/commonjs/networking/api-requests/user.api.js.map +1 -1
  56. package/lib/commonjs/networking/stanzaHandlers.js +1 -1
  57. package/lib/commonjs/networking/stanzaHandlers.js.map +1 -1
  58. package/lib/commonjs/roomStore/chatSettingsSlice.js +7 -2
  59. package/lib/commonjs/roomStore/chatSettingsSlice.js.map +1 -1
  60. package/lib/commonjs/services/languageSettings.js +177 -0
  61. package/lib/commonjs/services/languageSettings.js.map +1 -0
  62. package/lib/commonjs/version.js +1 -1
  63. package/lib/module/components/MainComponents/ChatWrapper.js +5 -3
  64. package/lib/module/components/MainComponents/ChatWrapper.js.map +1 -1
  65. package/lib/module/components/MainComponents/ReduxWrapper.js +9 -1
  66. package/lib/module/components/MainComponents/ReduxWrapper.js.map +1 -1
  67. package/lib/module/components/Menu/HeaderRoomListMenu.js +26 -13
  68. package/lib/module/components/Menu/HeaderRoomListMenu.js.map +1 -1
  69. package/lib/module/components/MenuRoom/MenuRoom.js +5 -3
  70. package/lib/module/components/MenuRoom/MenuRoom.js.map +1 -1
  71. package/lib/module/components/MessageBubble/Message.js +3 -3
  72. package/lib/module/components/MessageBubble/Message.js.map +1 -1
  73. package/lib/module/components/MessageBubble/MessageInteractions.js +5 -3
  74. package/lib/module/components/MessageBubble/MessageInteractions.js.map +1 -1
  75. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js +1 -1
  76. package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
  77. package/lib/module/components/Modals/ChatProfileModal/ReportChatModal.js +2 -2
  78. package/lib/module/components/Modals/ChatProfileModal/ReportChatModal.js.map +1 -1
  79. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js +7 -5
  80. package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
  81. package/lib/module/components/Modals/Modal/Modal.js +33 -11
  82. package/lib/module/components/Modals/Modal/Modal.js.map +1 -1
  83. package/lib/module/components/Modals/NewChatModal/NewChatModal.js +8 -8
  84. package/lib/module/components/Modals/NewChatModal/NewChatModal.js.map +1 -1
  85. package/lib/module/components/Modals/SettingsModals/ChangePassword/ChangePasswordModal.js +274 -0
  86. package/lib/module/components/Modals/SettingsModals/ChangePassword/ChangePasswordModal.js.map +1 -0
  87. package/lib/module/components/Modals/SettingsModals/ManageDataModal/ManageDataModal.js +10 -8
  88. package/lib/module/components/Modals/SettingsModals/ManageDataModal/ManageDataModal.js.map +1 -1
  89. package/lib/module/components/Modals/SettingsModals/Visibility/VisibilityModal.js +13 -11
  90. package/lib/module/components/Modals/SettingsModals/Visibility/VisibilityModal.js.map +1 -1
  91. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js +22 -10
  92. package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
  93. package/lib/module/components/Modals/UserSettingsModal/LanguageCard.js +116 -0
  94. package/lib/module/components/Modals/UserSettingsModal/LanguageCard.js.map +1 -0
  95. package/lib/module/components/Modals/UserSettingsModal/UserSettingsModal.js +10 -5
  96. package/lib/module/components/Modals/UserSettingsModal/UserSettingsModal.js.map +1 -1
  97. package/lib/module/components/Thread/ThreadWrapper.js +4 -2
  98. package/lib/module/components/Thread/ThreadWrapper.js.map +1 -1
  99. package/lib/module/e2ee/stanza.js +1 -1
  100. package/lib/module/e2ee/stanza.js.map +1 -1
  101. package/lib/module/helpers/constants/MODAL_COMPONENTS.js +2 -0
  102. package/lib/module/helpers/constants/MODAL_COMPONENTS.js.map +1 -1
  103. package/lib/module/helpers/constants/MODAL_TYPES.js +1 -0
  104. package/lib/module/helpers/constants/MODAL_TYPES.js.map +1 -1
  105. package/lib/module/helpers/preferencesStorage.js.map +1 -1
  106. package/lib/module/hooks/useChatSettingState.js +2 -0
  107. package/lib/module/hooks/useChatSettingState.js.map +1 -1
  108. package/lib/module/hooks/useChatWrapperInit.js +4 -0
  109. package/lib/module/hooks/useChatWrapperInit.js.map +1 -1
  110. package/lib/module/i18n/strings.js +187 -1
  111. package/lib/module/i18n/strings.js.map +1 -1
  112. package/lib/module/i18n/useT.js +8 -4
  113. package/lib/module/i18n/useT.js.map +1 -1
  114. package/lib/module/networking/api-requests/user.api.js +12 -0
  115. package/lib/module/networking/api-requests/user.api.js.map +1 -1
  116. package/lib/module/networking/stanzaHandlers.js +1 -1
  117. package/lib/module/networking/stanzaHandlers.js.map +1 -1
  118. package/lib/module/roomStore/chatSettingsSlice.js +5 -1
  119. package/lib/module/roomStore/chatSettingsSlice.js.map +1 -1
  120. package/lib/module/services/languageSettings.js +164 -0
  121. package/lib/module/services/languageSettings.js.map +1 -0
  122. package/lib/module/version.js +1 -1
  123. package/lib/typescript/components/MainComponents/ChatWrapper.d.ts.map +1 -1
  124. package/lib/typescript/components/MainComponents/ReduxWrapper.d.ts.map +1 -1
  125. package/lib/typescript/components/Menu/HeaderRoomListMenu.d.ts +7 -1
  126. package/lib/typescript/components/Menu/HeaderRoomListMenu.d.ts.map +1 -1
  127. package/lib/typescript/components/MenuRoom/MenuRoom.d.ts.map +1 -1
  128. package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts.map +1 -1
  129. package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts.map +1 -1
  130. package/lib/typescript/components/Modals/Modal/Modal.d.ts.map +1 -1
  131. package/lib/typescript/components/Modals/SettingsModals/ChangePassword/ChangePasswordModal.d.ts +15 -0
  132. package/lib/typescript/components/Modals/SettingsModals/ChangePassword/ChangePasswordModal.d.ts.map +1 -0
  133. package/lib/typescript/components/Modals/SettingsModals/ManageDataModal/ManageDataModal.d.ts.map +1 -1
  134. package/lib/typescript/components/Modals/SettingsModals/Visibility/VisibilityModal.d.ts.map +1 -1
  135. package/lib/typescript/components/Modals/UserProfileModal/UserProfileModal.d.ts.map +1 -1
  136. package/lib/typescript/components/Modals/UserSettingsModal/LanguageCard.d.ts +14 -0
  137. package/lib/typescript/components/Modals/UserSettingsModal/LanguageCard.d.ts.map +1 -0
  138. package/lib/typescript/components/Modals/UserSettingsModal/UserSettingsModal.d.ts.map +1 -1
  139. package/lib/typescript/components/Thread/ThreadWrapper.d.ts.map +1 -1
  140. package/lib/typescript/helpers/constants/MODAL_COMPONENTS.d.ts.map +1 -1
  141. package/lib/typescript/helpers/constants/MODAL_TYPES.d.ts +1 -0
  142. package/lib/typescript/helpers/constants/MODAL_TYPES.d.ts.map +1 -1
  143. package/lib/typescript/helpers/preferencesStorage.d.ts +4 -0
  144. package/lib/typescript/helpers/preferencesStorage.d.ts.map +1 -1
  145. package/lib/typescript/hooks/useChatSettingState.d.ts +1 -0
  146. package/lib/typescript/hooks/useChatSettingState.d.ts.map +1 -1
  147. package/lib/typescript/hooks/useChatWrapperInit.d.ts.map +1 -1
  148. package/lib/typescript/i18n/strings.d.ts.map +1 -1
  149. package/lib/typescript/i18n/useT.d.ts.map +1 -1
  150. package/lib/typescript/networking/api-requests/user.api.d.ts +6 -0
  151. package/lib/typescript/networking/api-requests/user.api.d.ts.map +1 -1
  152. package/lib/typescript/networking/stanzaHandlers.d.ts.map +1 -1
  153. package/lib/typescript/roomStore/chatSettingsSlice.d.ts +8 -1
  154. package/lib/typescript/roomStore/chatSettingsSlice.d.ts.map +1 -1
  155. package/lib/typescript/services/languageSettings.d.ts +29 -0
  156. package/lib/typescript/services/languageSettings.d.ts.map +1 -0
  157. package/lib/typescript/types/models/user.model.d.ts +4 -0
  158. package/lib/typescript/types/models/user.model.d.ts.map +1 -1
  159. package/lib/typescript/types/types.d.ts +21 -0
  160. package/lib/typescript/types/types.d.ts.map +1 -1
  161. package/lib/typescript/version.d.ts +1 -1
  162. package/package.json +1 -1
  163. package/src/components/MainComponents/ChatWrapper.tsx +5 -3
  164. package/src/components/MainComponents/ReduxWrapper.tsx +6 -0
  165. package/src/components/Menu/HeaderRoomListMenu.tsx +40 -13
  166. package/src/components/MenuRoom/MenuRoom.tsx +5 -3
  167. package/src/components/MessageBubble/Message.tsx +3 -3
  168. package/src/components/MessageBubble/MessageInteractions.tsx +5 -3
  169. package/src/components/Modals/ChatProfileModal/ChatProfileModal.tsx +1 -1
  170. package/src/components/Modals/ChatProfileModal/ReportChatModal.tsx +2 -2
  171. package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +7 -5
  172. package/src/components/Modals/Modal/Modal.tsx +44 -16
  173. package/src/components/Modals/NewChatModal/NewChatModal.tsx +8 -8
  174. package/src/components/Modals/SettingsModals/ChangePassword/ChangePasswordModal.tsx +316 -0
  175. package/src/components/Modals/SettingsModals/ManageDataModal/ManageDataModal.tsx +10 -15
  176. package/src/components/Modals/SettingsModals/Visibility/VisibilityModal.tsx +13 -14
  177. package/src/components/Modals/UserProfileModal/UserProfileModal.tsx +19 -12
  178. package/src/components/Modals/UserSettingsModal/LanguageCard.tsx +144 -0
  179. package/src/components/Modals/UserSettingsModal/UserSettingsModal.tsx +11 -4
  180. package/src/components/Thread/ThreadWrapper.tsx +4 -2
  181. package/src/e2ee/stanza.ts +1 -1
  182. package/src/helpers/constants/MODAL_COMPONENTS.tsx +2 -0
  183. package/src/helpers/constants/MODAL_TYPES.ts +1 -0
  184. package/src/helpers/preferencesStorage.ts +4 -0
  185. package/src/hooks/useChatSettingState.tsx +4 -0
  186. package/src/hooks/useChatWrapperInit.ts +4 -0
  187. package/src/i18n/strings.ts +187 -1
  188. package/src/i18n/useT.ts +6 -4
  189. package/src/networking/api-requests/user.api.ts +9 -0
  190. package/src/networking/stanzaHandlers.ts +1 -0
  191. package/src/roomStore/chatSettingsSlice.ts +13 -0
  192. package/src/services/languageSettings.ts +143 -0
  193. package/src/types/models/user.model.ts +4 -0
  194. package/src/types/types.ts +21 -0
  195. package/src/version.ts +1 -1
@@ -0,0 +1,316 @@
1
+ import React, { useCallback, useMemo, useState } from 'react';
2
+ import {
3
+ ActivityIndicator,
4
+ KeyboardAvoidingView,
5
+ Platform,
6
+ ScrollView,
7
+ StyleSheet,
8
+ Text,
9
+ TextInput,
10
+ TouchableOpacity,
11
+ View,
12
+ } from 'react-native';
13
+ import ModalHeaderComponent from '../../ModalHeaderComponent';
14
+ import { ModalContainerFullScreen } from '../../styledModalComponents';
15
+ import { useTheme } from '../../../../hooks/useTheme';
16
+ import type { ChatTheme } from '../../../../theme/theme';
17
+ import { useT } from '../../../../i18n/useT';
18
+ import { useToast } from '../../../../context/ToastContext';
19
+ import { useChatSettingState } from '../../../../hooks/useChatSettingState';
20
+ import { changePassword } from '../../../../networking/api-requests/user.api';
21
+ import { chatTextStyle } from '../../../../helpers/typography';
22
+
23
+ /** What the backend asks of a password at sign-up. */
24
+ export const MIN_PASSWORD_LENGTH = 8;
25
+
26
+ /** The reason a backend gave, or nothing - never an axios dump. */
27
+ const serverReason = (error: any): string => {
28
+ const data = error?.response?.data;
29
+ const text =
30
+ (typeof data === 'string' && data) ||
31
+ data?.message ||
32
+ data?.error ||
33
+ (Array.isArray(data?.errors) && data.errors.map((e: any) => e?.message || e).join(', ')) ||
34
+ '';
35
+ return typeof text === 'string' ? text.trim() : '';
36
+ };
37
+
38
+ interface FieldProps {
39
+ testID: string;
40
+ label: string;
41
+ value: string;
42
+ onChangeText: (value: string) => void;
43
+ error?: string;
44
+ editable: boolean;
45
+ newPassword?: boolean;
46
+ onSubmitEditing?: () => void;
47
+ styles: ReturnType<typeof createStyles>;
48
+ theme: ChatTheme;
49
+ }
50
+
51
+ /** One labelled password field: label above, error (if any) below. */
52
+ const Field: React.FC<FieldProps> = ({
53
+ testID,
54
+ label,
55
+ value,
56
+ onChangeText,
57
+ error,
58
+ editable,
59
+ newPassword,
60
+ onSubmitEditing,
61
+ styles,
62
+ theme,
63
+ }) => (
64
+ <View style={styles.field}>
65
+ <Text style={styles.fieldLabel}>{label}</Text>
66
+ <TextInput
67
+ testID={testID}
68
+ style={[styles.input, error ? styles.inputError : null]}
69
+ value={value}
70
+ onChangeText={onChangeText}
71
+ placeholder={label}
72
+ placeholderTextColor={theme.textMuted}
73
+ keyboardAppearance={theme.dark ? 'dark' : 'light'}
74
+ secureTextEntry
75
+ autoCapitalize="none"
76
+ autoCorrect={false}
77
+ textContentType={newPassword ? 'newPassword' : 'password'}
78
+ editable={editable}
79
+ returnKeyType={onSubmitEditing ? 'done' : 'next'}
80
+ onSubmitEditing={onSubmitEditing}
81
+ blurOnSubmit={!!onSubmitEditing}
82
+ />
83
+ {error ? <Text style={styles.fieldError}>{error}</Text> : null}
84
+ </View>
85
+ );
86
+
87
+ interface ChangePasswordModalProps {
88
+ handleCloseModal: () => void;
89
+ }
90
+
91
+ /**
92
+ * Change the account password: the current one, the new one twice. Nothing
93
+ * is sent until the form is complete and the two match; what the backend
94
+ * refuses is shown as it said it. Laid out like the Settings screen it is
95
+ * reached from: one card on the list ground.
96
+ */
97
+ const ChangePasswordModal: React.FC<ChangePasswordModalProps> = ({ handleCloseModal }) => {
98
+ const theme = useTheme();
99
+ const t = useT();
100
+ const { showToast } = useToast();
101
+ const { config } = useChatSettingState();
102
+ const styles = useMemo(() => createStyles(theme), [theme]);
103
+ const [current, setCurrent] = useState('');
104
+ const [next, setNext] = useState('');
105
+ const [again, setAgain] = useState('');
106
+ const [busy, setBusy] = useState(false);
107
+ const [error, setError] = useState('');
108
+
109
+ const mismatch = again.length > 0 && next !== again;
110
+ const tooShort = next.length > 0 && next.length < MIN_PASSWORD_LENGTH;
111
+ const sameAsCurrent = next.length > 0 && next === current;
112
+ const ready =
113
+ current.length > 0 &&
114
+ next.length >= MIN_PASSWORD_LENGTH &&
115
+ next === again &&
116
+ !sameAsCurrent &&
117
+ !busy;
118
+
119
+ const submit = useCallback(async () => {
120
+ if (!ready) {return;}
121
+ setBusy(true);
122
+ setError('');
123
+ try {
124
+ await changePassword(current, next);
125
+ showToast({
126
+ id: Date.now().toString(),
127
+ title: t('settings.password.title'),
128
+ message: t('settings.password.changed'),
129
+ type: 'success',
130
+ });
131
+ handleCloseModal();
132
+ } catch (err) {
133
+ const status = (err as any)?.response?.status;
134
+ setError(
135
+ serverReason(err) ||
136
+ (status === 400 || status === 401 || status === 403
137
+ ? t('settings.password.wrongCurrent')
138
+ : t('settings.password.failed'))
139
+ );
140
+ setBusy(false);
141
+ }
142
+ }, [current, handleCloseModal, next, ready, showToast, t]);
143
+
144
+ const newError = tooShort
145
+ ? t('settings.password.tooShort', { min: MIN_PASSWORD_LENGTH })
146
+ : sameAsCurrent
147
+ ? t('settings.password.sameAsCurrent')
148
+ : undefined;
149
+
150
+ return (
151
+ <ModalContainerFullScreen style={styles.screen}>
152
+ <ModalHeaderComponent
153
+ handleCloseModal={handleCloseModal}
154
+ headerTitle={t('settings.password.title')}
155
+ titleStyle={chatTextStyle(config?.typography?.profile?.screenTitle)}
156
+ />
157
+ <KeyboardAvoidingView
158
+ behavior={Platform.OS === 'ios' ? 'padding' : undefined}
159
+ style={styles.fill}
160
+ >
161
+ <ScrollView
162
+ style={styles.fill}
163
+ contentContainerStyle={styles.body}
164
+ keyboardShouldPersistTaps="handled"
165
+ >
166
+ <View style={styles.card} testID="settings-change-password">
167
+ <Field
168
+ testID="password-current"
169
+ label={t('settings.password.current')}
170
+ value={current}
171
+ onChangeText={setCurrent}
172
+ editable={!busy}
173
+ styles={styles}
174
+ theme={theme}
175
+ />
176
+ <Field
177
+ testID="password-new"
178
+ label={t('settings.password.new')}
179
+ value={next}
180
+ onChangeText={setNext}
181
+ error={newError}
182
+ editable={!busy}
183
+ newPassword
184
+ styles={styles}
185
+ theme={theme}
186
+ />
187
+ <Field
188
+ testID="password-again"
189
+ label={t('settings.password.again')}
190
+ value={again}
191
+ onChangeText={setAgain}
192
+ error={mismatch ? t('settings.password.mismatch') : undefined}
193
+ editable={!busy}
194
+ newPassword
195
+ onSubmitEditing={submit}
196
+ styles={styles}
197
+ theme={theme}
198
+ />
199
+ <Text style={styles.hint}>{t('settings.password.hint')}</Text>
200
+ </View>
201
+
202
+ {!!error && (
203
+ <Text testID="password-error" style={styles.error}>
204
+ {error}
205
+ </Text>
206
+ )}
207
+
208
+ <TouchableOpacity
209
+ testID="password-submit"
210
+ accessibilityRole="button"
211
+ accessibilityState={{ disabled: !ready }}
212
+ activeOpacity={0.8}
213
+ disabled={!ready}
214
+ onPress={submit}
215
+ style={[styles.button, !ready ? styles.buttonDisabled : null]}
216
+ >
217
+ {busy ? (
218
+ <ActivityIndicator color={theme.textOnPrimary} />
219
+ ) : (
220
+ <Text style={[styles.buttonText, !ready ? styles.buttonTextDisabled : null]}>
221
+ {t('settings.password.submit')}
222
+ </Text>
223
+ )}
224
+ </TouchableOpacity>
225
+ </ScrollView>
226
+ </KeyboardAvoidingView>
227
+ </ModalContainerFullScreen>
228
+ );
229
+ };
230
+
231
+ export default ChangePasswordModal;
232
+
233
+ const createStyles = (theme: ChatTheme) =>
234
+ StyleSheet.create({
235
+ screen: {
236
+ backgroundColor: theme.listBackground,
237
+ },
238
+ fill: {
239
+ flex: 1,
240
+ width: '100%',
241
+ },
242
+ body: {
243
+ padding: 12,
244
+ gap: 12,
245
+ paddingBottom: 32,
246
+ },
247
+ // Same surface as the Settings cards this screen is reached from.
248
+ card: {
249
+ backgroundColor: theme.surface,
250
+ borderRadius: 20,
251
+ paddingHorizontal: 20,
252
+ paddingVertical: 22,
253
+ gap: 18,
254
+ shadowColor: theme.shadow,
255
+ shadowOffset: { width: 0, height: 4 },
256
+ shadowOpacity: 0.06,
257
+ shadowRadius: 12,
258
+ elevation: 3,
259
+ },
260
+ field: {
261
+ gap: 8,
262
+ },
263
+ fieldLabel: {
264
+ fontSize: 14,
265
+ fontWeight: '500',
266
+ color: theme.textSecondary,
267
+ },
268
+ input: {
269
+ height: 48,
270
+ paddingHorizontal: 16,
271
+ borderRadius: 12,
272
+ borderWidth: 1,
273
+ borderColor: theme.border,
274
+ backgroundColor: theme.surfaceSecondary,
275
+ fontSize: 16,
276
+ color: theme.text,
277
+ },
278
+ inputError: {
279
+ borderColor: theme.danger,
280
+ },
281
+ fieldError: {
282
+ fontSize: 13,
283
+ lineHeight: 18,
284
+ color: theme.danger,
285
+ },
286
+ hint: {
287
+ fontSize: 13,
288
+ lineHeight: 18,
289
+ color: theme.textSecondary,
290
+ },
291
+ error: {
292
+ fontSize: 14,
293
+ lineHeight: 19,
294
+ paddingHorizontal: 8,
295
+ color: theme.danger,
296
+ },
297
+ button: {
298
+ minHeight: 48,
299
+ borderRadius: 14,
300
+ alignItems: 'center',
301
+ justifyContent: 'center',
302
+ paddingHorizontal: 20,
303
+ backgroundColor: theme.primary,
304
+ },
305
+ buttonDisabled: {
306
+ backgroundColor: theme.surfaceSecondary,
307
+ },
308
+ buttonText: {
309
+ fontSize: 16,
310
+ fontWeight: '600',
311
+ color: theme.textOnPrimary,
312
+ },
313
+ buttonTextDisabled: {
314
+ color: theme.textMuted,
315
+ },
316
+ });
@@ -21,6 +21,7 @@ import {
21
21
  } from '../SharedStyledComponents';
22
22
  import { View } from 'react-native';
23
23
  import { useTheme } from '../../../../hooks/useTheme';
24
+ import { useT } from '../../../../i18n/useT';
24
25
 
25
26
  interface ManageDataModalProps {
26
27
  handleCloseModal: any;
@@ -39,6 +40,7 @@ const ManageDataModal: React.FC<ManageDataModalProps> = ({
39
40
  }) => {
40
41
  const { config } = useSelector((state: RootState) => state.chatSettingStore);
41
42
  const theme = useTheme();
43
+ const t = useT();
42
44
 
43
45
  const handleDownloadClick = async () => {
44
46
  // const exportedData = await getExportMyData();
@@ -59,13 +61,13 @@ const ManageDataModal: React.FC<ManageDataModalProps> = ({
59
61
  <ModalContainerFullScreen>
60
62
  <ModalHeaderComponent
61
63
  handleCloseModal={handleCloseModal}
62
- headerTitle={'Manage Data'}
64
+ headerTitle={t('settings.manageData.title')}
63
65
  />
64
66
  <SharedSettingsCenterContainer>
65
67
  <SharedSettingsColumnContainer>
66
68
  <SharedSettingsSectionContainer>
67
69
  <SharedSettingsStyledLabel>
68
- Download your data
70
+ {t('settings.manageData.downloadLabel')}
69
71
  </SharedSettingsStyledLabel>
70
72
  <LabelData
71
73
  style={{
@@ -73,25 +75,23 @@ const ManageDataModal: React.FC<ManageDataModalProps> = ({
73
75
  textAlign: 'left',
74
76
  }}
75
77
  >
76
- You own your data. Tap the button below to download a copy of your
77
- data
78
+ {t('settings.manageData.downloadDescription')}
78
79
  </LabelData>
79
80
  </SharedSettingsSectionContainer>
80
81
  <SharedSettingsStyledButton
81
82
  borderColor={theme.primary}
82
83
  onPress={handleDownloadClick}
83
- text="Download your data"
84
+ text={t('settings.manageData.downloadLabel')}
84
85
  color={theme.primary}
85
86
  />
86
87
  </SharedSettingsColumnContainer>
87
88
  <SharedSettingsColumnContainer>
88
89
  <SharedSettingsSectionContainer>
89
90
  <SharedSettingsStyledLabel>
90
- Download your data
91
+ {t('settings.manageData.deleteLabel')}
91
92
  </SharedSettingsStyledLabel>
92
93
  <SharedSettingsLabelData>
93
- Use this only if you want to permanently delete your account &
94
- data from our system.
94
+ {t('settings.manageData.deleteDescription')}
95
95
  </SharedSettingsLabelData>
96
96
  </SharedSettingsSectionContainer>
97
97
  <SharedSettingsInfoPanel
@@ -101,17 +101,12 @@ const ManageDataModal: React.FC<ManageDataModalProps> = ({
101
101
  <InfoIcon color={theme.primary} />
102
102
  </View>
103
103
  <SharedSettingsInfoText>
104
- Due to the immutable nature of distributed ledger technology,
105
- network nodes operated by the community may still retain
106
- historical transactions generated by your account, however your
107
- personally identifiable information such as your name, e-mail,
108
- your key-value storage etc will be removed. Any of your digital
109
- assets will be lost.
104
+ {t('settings.manageData.deleteDisclosure')}
110
105
  </SharedSettingsInfoText>
111
106
  </SharedSettingsInfoPanel>
112
107
  <SharedSettingsStyledButton
113
108
  borderColor={theme.danger}
114
- text="Delete My Account"
109
+ text={t('action.deleteMyAccount')}
115
110
  color={theme.danger}
116
111
  />
117
112
  </SharedSettingsColumnContainer>
@@ -16,6 +16,7 @@ import {
16
16
  } from '../SharedStyledComponents';
17
17
  import { RadioInput } from './RadioInput';
18
18
  import { useTheme } from '../../../../hooks/useTheme';
19
+ import { useT } from '../../../../i18n/useT';
19
20
 
20
21
  interface VisibilityModalProps {
21
22
  handleCloseModal: any;
@@ -27,6 +28,7 @@ const VisibilityModal: React.FC<VisibilityModalProps> = ({
27
28
  const dispatch = useDispatch();
28
29
  const { user } = useSelector((state: RootState) => state.chatSettingStore);
29
30
  const theme = useTheme();
31
+ const t = useT();
30
32
 
31
33
  const doUpdateUser = (user: User) => dispatch(setUser(user));
32
34
  const [isProfileOpen, setIsProfileOpen] = useState(user?.isProfileOpen);
@@ -67,17 +69,17 @@ const VisibilityModal: React.FC<VisibilityModalProps> = ({
67
69
  <ModalContainerFullScreen>
68
70
  <ModalHeaderComponent
69
71
  handleCloseModal={handleCloseModal}
70
- headerTitle={'Visibility'}
72
+ headerTitle={t('settings.visibility.title')}
71
73
  />
72
74
  <SharedSettingsCenterContainer>
73
75
  <SharedSettingsColumnContainer>
74
76
  <SharedSettingsStyledLabel>
75
- Profile Visiblility
77
+ {t('settings.visibility.profileLabel')}
76
78
  </SharedSettingsStyledLabel>
77
79
  <RadioGroup>
78
80
  <RadioLabel>
79
81
  <RadioInput
80
- option={{ label: 'Open (default)', value: isProfileOpen }}
82
+ option={{ label: t('settings.visibility.open'), value: isProfileOpen }}
81
83
  radioColor={theme.primary}
82
84
  checked={isProfileOpen === true}
83
85
  onChange={() => setIsProfileOpen(true)}
@@ -85,50 +87,47 @@ const VisibilityModal: React.FC<VisibilityModalProps> = ({
85
87
  </RadioLabel>
86
88
 
87
89
  <SharedSettingsLabelData>
88
- Your profile can be viewed by anyone who follows your profile link
89
- or QR code.
90
+ {t('settings.visibility.openDescription')}
90
91
  </SharedSettingsLabelData>
91
92
  <RadioLabel>
92
93
  <RadioInput
93
- option={{ label: 'Restricted', value: isProfileOpen }}
94
+ option={{ label: t('settings.visibility.restricted'), value: isProfileOpen }}
94
95
  radioColor={theme.primary}
95
96
  checked={isProfileOpen === false}
96
97
  onChange={() => setIsProfileOpen(false)}
97
98
  />
98
99
  </RadioLabel>
99
100
  <SharedSettingsLabelData>
100
- Only users with your permission or temporary secure link can see
101
- your profile.
101
+ {t('settings.visibility.restrictedDescription')}
102
102
  </SharedSettingsLabelData>
103
103
  </RadioGroup>
104
104
  </SharedSettingsColumnContainer>
105
105
  <SharedSettingsColumnContainer>
106
106
  <SharedSettingsStyledLabel>
107
- Documents Visibility
107
+ {t('settings.visibility.documentsLabel')}
108
108
  </SharedSettingsStyledLabel>
109
109
  <RadioGroup>
110
110
  <RadioLabel>
111
111
  <RadioInput
112
- option={{ label: 'Full (default)', value: isAssetsOpen }}
112
+ option={{ label: t('settings.visibility.full'), value: isAssetsOpen }}
113
113
  radioColor={theme.primary}
114
114
  checked={isAssetsOpen === true}
115
115
  onChange={() => setIsAssetsOpen(true)}
116
116
  />
117
117
  </RadioLabel>
118
118
  <SharedSettingsLabelData>
119
- Show all Documents to those who can see your profile.
119
+ {t('settings.visibility.fullDescription')}
120
120
  </SharedSettingsLabelData>
121
121
  <RadioLabel>
122
122
  <RadioInput
123
- option={{ label: 'InViewidual', value: isAssetsOpen }}
123
+ option={{ label: t('settings.visibility.individual'), value: isAssetsOpen }}
124
124
  radioColor={theme.primary}
125
125
  checked={isAssetsOpen === false}
126
126
  onChange={() => setIsAssetsOpen(false)}
127
127
  />
128
128
  </RadioLabel>
129
129
  <SharedSettingsLabelData>
130
- You need to share each document inViewidually before others can
131
- see them.
130
+ {t('settings.visibility.individualDescription')}
132
131
  </SharedSettingsLabelData>
133
132
  </RadioGroup>
134
133
  </SharedSettingsColumnContainer>
@@ -39,7 +39,6 @@ import { MODAL_TYPES } from '../../../helpers/constants/MODAL_TYPES';
39
39
  import {
40
40
  setActiveFile,
41
41
  setActiveModal,
42
- setLangSource,
43
42
  } from '../../../roomStore/chatSettingsSlice';
44
43
  import { addRoomViaApi, setCurrentRoom } from '../../../roomStore/roomsSlice';
45
44
  import { runLogoutFlow } from '../../Menu/HeaderRoomListMenu';
@@ -51,6 +50,7 @@ import {
51
50
  } from '../../../networking/api-requests/rooms.api';
52
51
  import EncryptionCard from '../../MainComponents/EncryptionCard';
53
52
  import { accountNameOf } from '../../../helpers/accountName';
53
+ import { setChatLanguage } from '../../../services/languageSettings';
54
54
  import { composeName } from '../../../helpers/displayName';
55
55
  import { isUnresolvedSenderId } from '../../../helpers/isUnresolvedSenderId';
56
56
 
@@ -66,7 +66,7 @@ import {
66
66
  } from '../../../networking/api-requests/user.api';
67
67
  import { createRoomFromApi } from '../../../helpers/createRoomFromApi';
68
68
  import { walletToUsername } from '../../../helpers/walletUsername';
69
- import { Iso639_1Codes } from '../../../types/types';
69
+ import { } from '../../../types/types';
70
70
  import {
71
71
  ProfileHero,
72
72
  ProfileTopBar,
@@ -206,8 +206,12 @@ const UserProfileModal: React.FC<UserProfileModalProps> = ({
206
206
  // AsyncStorage), including the host's confirm copy and callbacks.
207
207
  const performLogout = useLogout();
208
208
  const handleLogout = useCallback(() => {
209
- runLogoutFlow(config?.logout ?? { enabled: true }, performLogout).catch(() => {});
210
- }, [config?.logout, performLogout]);
209
+ runLogoutFlow(config?.logout ?? { enabled: true }, performLogout, {
210
+ label: t('menu.logout'),
211
+ message: t('menu.logoutConfirm'),
212
+ cancel: t('action.cancel'),
213
+ }).catch(() => {});
214
+ }, [config?.logout, performLogout, t]);
211
215
 
212
216
  const handleShare = useCallback(async () => {
213
217
  const name =
@@ -243,8 +247,8 @@ const UserProfileModal: React.FC<UserProfileModalProps> = ({
243
247
 
244
248
  showToast({
245
249
  id: Date.now().toString(),
246
- title: 'Success!',
247
- message: 'Room created succusfully!',
250
+ title: t('toast.success'),
251
+ message: t('toast.roomCreatedSuccess'),
248
252
  type: 'success',
249
253
  duration: 3000,
250
254
  });
@@ -256,8 +260,8 @@ const UserProfileModal: React.FC<UserProfileModalProps> = ({
256
260
  const openPrivateRoom = useCallback(async (e2ee: boolean) => {
257
261
  showToast({
258
262
  id: Date.now().toString(),
259
- title: 'Room creation',
260
- message: 'Room is being created...',
263
+ title: t('toast.roomCreationTitle'),
264
+ message: t('toast.roomCreating'),
261
265
  type: 'info',
262
266
  duration: 3000,
263
267
  });
@@ -297,7 +301,7 @@ const UserProfileModal: React.FC<UserProfileModalProps> = ({
297
301
  }
298
302
 
299
303
  dispatch(setActiveModal(undefined));
300
- }, [selectedUser]); // eslint-disable-line react-hooks/exhaustive-deps
304
+ }, [selectedUser, t]); // eslint-disable-line react-hooks/exhaustive-deps
301
305
 
302
306
  const handlePrivateMessage = useCallback(
303
307
  () => openPrivateRoom(false),
@@ -430,9 +434,12 @@ const UserProfileModal: React.FC<UserProfileModalProps> = ({
430
434
  testID={`${PREFIX}-language-${option.id}`}
431
435
  activeOpacity={0.7}
432
436
  style={styles.languageRow}
433
- onPress={() =>
434
- dispatch(setLangSource(option.id as Iso639_1Codes))
435
- }
437
+ // The same choice Settings offers as "chat language": kept
438
+ // on the device, and written to the profile when the host
439
+ // turned that on.
440
+ onPress={() => {
441
+ setChatLanguage(option.id).catch(() => {});
442
+ }}
436
443
  >
437
444
  <Text style={styles.languageLabel}>{option.name}</Text>
438
445
  {selected && (