@ethora/chat-component-rn 26.8.7 → 26.8.9
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 +27 -4
- package/lib/commonjs/components/Header/HeaderRoomList.js +34 -6
- package/lib/commonjs/components/Header/HeaderRoomList.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ChatHeader.js +15 -1
- package/lib/commonjs/components/MainComponents/ChatHeader.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ChatWrapper.js +5 -3
- package/lib/commonjs/components/MainComponents/ChatWrapper.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ReduxWrapper.js +8 -0
- package/lib/commonjs/components/MainComponents/ReduxWrapper.js.map +1 -1
- package/lib/commonjs/components/MainComponents/RoomList.js +63 -11
- package/lib/commonjs/components/MainComponents/RoomList.js.map +1 -1
- package/lib/commonjs/components/Menu/HeaderRoomListMenu.js +26 -13
- package/lib/commonjs/components/Menu/HeaderRoomListMenu.js.map +1 -1
- package/lib/commonjs/components/MenuRoom/MenuRoom.js +5 -3
- package/lib/commonjs/components/MenuRoom/MenuRoom.js.map +1 -1
- package/lib/commonjs/components/MessageBubble/Message.js +3 -3
- package/lib/commonjs/components/MessageBubble/Message.js.map +1 -1
- package/lib/commonjs/components/MessageBubble/MessageInteractions.js +5 -3
- package/lib/commonjs/components/MessageBubble/MessageInteractions.js.map +1 -1
- package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js +1 -1
- package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
- package/lib/commonjs/components/Modals/ChatProfileModal/ReportChatModal.js +5 -4
- package/lib/commonjs/components/Modals/ChatProfileModal/ReportChatModal.js.map +1 -1
- package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js +7 -5
- package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
- package/lib/commonjs/components/Modals/Modal/Modal.js +33 -11
- package/lib/commonjs/components/Modals/Modal/Modal.js.map +1 -1
- package/lib/commonjs/components/Modals/NewChatModal/NewChatModal.js +8 -8
- package/lib/commonjs/components/Modals/NewChatModal/NewChatModal.js.map +1 -1
- package/lib/commonjs/components/Modals/SettingsModals/ChangePassword/ChangePasswordModal.js +279 -0
- package/lib/commonjs/components/Modals/SettingsModals/ChangePassword/ChangePasswordModal.js.map +1 -0
- package/lib/commonjs/components/Modals/SettingsModals/ManageDataModal/ManageDataModal.js +10 -8
- package/lib/commonjs/components/Modals/SettingsModals/ManageDataModal/ManageDataModal.js.map +1 -1
- package/lib/commonjs/components/Modals/SettingsModals/Visibility/VisibilityModal.js +13 -11
- package/lib/commonjs/components/Modals/SettingsModals/Visibility/VisibilityModal.js.map +1 -1
- package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js +22 -38
- package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
- package/lib/commonjs/components/Modals/UserSettingsModal/LanguageCard.js +121 -0
- package/lib/commonjs/components/Modals/UserSettingsModal/LanguageCard.js.map +1 -0
- package/lib/commonjs/components/Modals/UserSettingsModal/UserSettingsModal.js +11 -6
- package/lib/commonjs/components/Modals/UserSettingsModal/UserSettingsModal.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/SwipeableRoomRow.js +133 -0
- package/lib/commonjs/components/RoomComponents/SwipeableRoomRow.js.map +1 -0
- package/lib/commonjs/components/Thread/ThreadWrapper.js +4 -2
- package/lib/commonjs/components/Thread/ThreadWrapper.js.map +1 -1
- package/lib/commonjs/context/xmppProvider.js +15 -3
- package/lib/commonjs/context/xmppProvider.js.map +1 -1
- package/lib/commonjs/e2ee/stanza.js +1 -1
- package/lib/commonjs/e2ee/stanza.js.map +1 -1
- package/lib/commonjs/helpers/constants/MODAL_COMPONENTS.js +2 -0
- package/lib/commonjs/helpers/constants/MODAL_COMPONENTS.js.map +1 -1
- package/lib/commonjs/helpers/constants/MODAL_TYPES.js +1 -0
- package/lib/commonjs/helpers/constants/MODAL_TYPES.js.map +1 -1
- package/lib/commonjs/helpers/preferencesStorage.js.map +1 -1
- package/lib/commonjs/hooks/useChatSettingState.js +4 -0
- package/lib/commonjs/hooks/useChatSettingState.js.map +1 -1
- package/lib/commonjs/hooks/useChatWrapperInit.js +4 -0
- package/lib/commonjs/hooks/useChatWrapperInit.js.map +1 -1
- package/lib/commonjs/hooks/useConnectionLabel.js +29 -0
- package/lib/commonjs/hooks/useConnectionLabel.js.map +1 -0
- package/lib/commonjs/i18n/strings.js +199 -1
- package/lib/commonjs/i18n/strings.js.map +1 -1
- package/lib/commonjs/i18n/useT.js +8 -4
- package/lib/commonjs/i18n/useT.js.map +1 -1
- package/lib/commonjs/networking/api-requests/user.api.js +13 -0
- package/lib/commonjs/networking/api-requests/user.api.js.map +1 -1
- package/lib/commonjs/networking/stanzaHandlers.js +1 -1
- package/lib/commonjs/networking/stanzaHandlers.js.map +1 -1
- package/lib/commonjs/networking/xmppClient.js +29 -1
- package/lib/commonjs/networking/xmppClient.js.map +1 -1
- package/lib/commonjs/roomStore/chatSettingsSlice.js +12 -2
- package/lib/commonjs/roomStore/chatSettingsSlice.js.map +1 -1
- package/lib/commonjs/services/languageSettings.js +177 -0
- package/lib/commonjs/services/languageSettings.js.map +1 -0
- package/lib/commonjs/version.js +1 -1
- package/lib/module/components/Header/HeaderRoomList.js +34 -6
- package/lib/module/components/Header/HeaderRoomList.js.map +1 -1
- package/lib/module/components/MainComponents/ChatHeader.js +15 -1
- package/lib/module/components/MainComponents/ChatHeader.js.map +1 -1
- package/lib/module/components/MainComponents/ChatWrapper.js +5 -3
- package/lib/module/components/MainComponents/ChatWrapper.js.map +1 -1
- package/lib/module/components/MainComponents/ReduxWrapper.js +9 -1
- package/lib/module/components/MainComponents/ReduxWrapper.js.map +1 -1
- package/lib/module/components/MainComponents/RoomList.js +64 -12
- package/lib/module/components/MainComponents/RoomList.js.map +1 -1
- package/lib/module/components/Menu/HeaderRoomListMenu.js +26 -13
- package/lib/module/components/Menu/HeaderRoomListMenu.js.map +1 -1
- package/lib/module/components/MenuRoom/MenuRoom.js +5 -3
- package/lib/module/components/MenuRoom/MenuRoom.js.map +1 -1
- package/lib/module/components/MessageBubble/Message.js +3 -3
- package/lib/module/components/MessageBubble/Message.js.map +1 -1
- package/lib/module/components/MessageBubble/MessageInteractions.js +5 -3
- package/lib/module/components/MessageBubble/MessageInteractions.js.map +1 -1
- package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js +1 -1
- package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
- package/lib/module/components/Modals/ChatProfileModal/ReportChatModal.js +5 -4
- package/lib/module/components/Modals/ChatProfileModal/ReportChatModal.js.map +1 -1
- package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js +7 -5
- package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
- package/lib/module/components/Modals/Modal/Modal.js +33 -11
- package/lib/module/components/Modals/Modal/Modal.js.map +1 -1
- package/lib/module/components/Modals/NewChatModal/NewChatModal.js +8 -8
- package/lib/module/components/Modals/NewChatModal/NewChatModal.js.map +1 -1
- package/lib/module/components/Modals/SettingsModals/ChangePassword/ChangePasswordModal.js +274 -0
- package/lib/module/components/Modals/SettingsModals/ChangePassword/ChangePasswordModal.js.map +1 -0
- package/lib/module/components/Modals/SettingsModals/ManageDataModal/ManageDataModal.js +10 -8
- package/lib/module/components/Modals/SettingsModals/ManageDataModal/ManageDataModal.js.map +1 -1
- package/lib/module/components/Modals/SettingsModals/Visibility/VisibilityModal.js +13 -11
- package/lib/module/components/Modals/SettingsModals/Visibility/VisibilityModal.js.map +1 -1
- package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js +24 -40
- package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js.map +1 -1
- package/lib/module/components/Modals/UserSettingsModal/LanguageCard.js +116 -0
- package/lib/module/components/Modals/UserSettingsModal/LanguageCard.js.map +1 -0
- package/lib/module/components/Modals/UserSettingsModal/UserSettingsModal.js +10 -5
- package/lib/module/components/Modals/UserSettingsModal/UserSettingsModal.js.map +1 -1
- package/lib/module/components/RoomComponents/SwipeableRoomRow.js +126 -0
- package/lib/module/components/RoomComponents/SwipeableRoomRow.js.map +1 -0
- package/lib/module/components/Thread/ThreadWrapper.js +4 -2
- package/lib/module/components/Thread/ThreadWrapper.js.map +1 -1
- package/lib/module/context/xmppProvider.js +16 -4
- package/lib/module/context/xmppProvider.js.map +1 -1
- package/lib/module/e2ee/stanza.js +1 -1
- package/lib/module/e2ee/stanza.js.map +1 -1
- package/lib/module/helpers/constants/MODAL_COMPONENTS.js +2 -0
- package/lib/module/helpers/constants/MODAL_COMPONENTS.js.map +1 -1
- package/lib/module/helpers/constants/MODAL_TYPES.js +1 -0
- package/lib/module/helpers/constants/MODAL_TYPES.js.map +1 -1
- package/lib/module/helpers/preferencesStorage.js.map +1 -1
- package/lib/module/hooks/useChatSettingState.js +4 -0
- package/lib/module/hooks/useChatSettingState.js.map +1 -1
- package/lib/module/hooks/useChatWrapperInit.js +4 -0
- package/lib/module/hooks/useChatWrapperInit.js.map +1 -1
- package/lib/module/hooks/useConnectionLabel.js +25 -0
- package/lib/module/hooks/useConnectionLabel.js.map +1 -0
- package/lib/module/i18n/strings.js +199 -1
- package/lib/module/i18n/strings.js.map +1 -1
- package/lib/module/i18n/useT.js +8 -4
- package/lib/module/i18n/useT.js.map +1 -1
- package/lib/module/networking/api-requests/user.api.js +12 -0
- package/lib/module/networking/api-requests/user.api.js.map +1 -1
- package/lib/module/networking/stanzaHandlers.js +1 -1
- package/lib/module/networking/stanzaHandlers.js.map +1 -1
- package/lib/module/networking/xmppClient.js +29 -1
- package/lib/module/networking/xmppClient.js.map +1 -1
- package/lib/module/roomStore/chatSettingsSlice.js +9 -1
- package/lib/module/roomStore/chatSettingsSlice.js.map +1 -1
- package/lib/module/services/languageSettings.js +164 -0
- package/lib/module/services/languageSettings.js.map +1 -0
- package/lib/module/version.js +1 -1
- package/lib/typescript/components/Header/HeaderRoomList.d.ts +1 -0
- package/lib/typescript/components/Header/HeaderRoomList.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/ChatHeader.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/ChatWrapper.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/ReduxWrapper.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/RoomList.d.ts.map +1 -1
- package/lib/typescript/components/Menu/HeaderRoomListMenu.d.ts +7 -1
- package/lib/typescript/components/Menu/HeaderRoomListMenu.d.ts.map +1 -1
- package/lib/typescript/components/MenuRoom/MenuRoom.d.ts.map +1 -1
- package/lib/typescript/components/MessageBubble/MessageInteractions.d.ts.map +1 -1
- package/lib/typescript/components/Modals/ChatProfileModal/ReportChatModal.d.ts +2 -0
- package/lib/typescript/components/Modals/ChatProfileModal/ReportChatModal.d.ts.map +1 -1
- package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts.map +1 -1
- package/lib/typescript/components/Modals/Modal/Modal.d.ts.map +1 -1
- package/lib/typescript/components/Modals/SettingsModals/ChangePassword/ChangePasswordModal.d.ts +15 -0
- package/lib/typescript/components/Modals/SettingsModals/ChangePassword/ChangePasswordModal.d.ts.map +1 -0
- package/lib/typescript/components/Modals/SettingsModals/ManageDataModal/ManageDataModal.d.ts.map +1 -1
- package/lib/typescript/components/Modals/SettingsModals/Visibility/VisibilityModal.d.ts.map +1 -1
- package/lib/typescript/components/Modals/UserProfileModal/UserProfileModal.d.ts.map +1 -1
- package/lib/typescript/components/Modals/UserSettingsModal/LanguageCard.d.ts +14 -0
- package/lib/typescript/components/Modals/UserSettingsModal/LanguageCard.d.ts.map +1 -0
- package/lib/typescript/components/Modals/UserSettingsModal/UserSettingsModal.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/SwipeableRoomRow.d.ts +19 -0
- package/lib/typescript/components/RoomComponents/SwipeableRoomRow.d.ts.map +1 -0
- package/lib/typescript/components/Thread/ThreadWrapper.d.ts.map +1 -1
- package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
- package/lib/typescript/helpers/constants/MODAL_COMPONENTS.d.ts.map +1 -1
- package/lib/typescript/helpers/constants/MODAL_TYPES.d.ts +1 -0
- package/lib/typescript/helpers/constants/MODAL_TYPES.d.ts.map +1 -1
- package/lib/typescript/helpers/preferencesStorage.d.ts +4 -0
- package/lib/typescript/helpers/preferencesStorage.d.ts.map +1 -1
- package/lib/typescript/hooks/useChatSettingState.d.ts +2 -0
- package/lib/typescript/hooks/useChatSettingState.d.ts.map +1 -1
- package/lib/typescript/hooks/useChatWrapperInit.d.ts.map +1 -1
- package/lib/typescript/hooks/useConnectionLabel.d.ts +8 -0
- package/lib/typescript/hooks/useConnectionLabel.d.ts.map +1 -0
- package/lib/typescript/i18n/strings.d.ts.map +1 -1
- package/lib/typescript/i18n/useT.d.ts.map +1 -1
- package/lib/typescript/networking/api-requests/user.api.d.ts +6 -0
- package/lib/typescript/networking/api-requests/user.api.d.ts.map +1 -1
- package/lib/typescript/networking/stanzaHandlers.d.ts.map +1 -1
- package/lib/typescript/networking/xmppClient.d.ts +11 -1
- package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
- package/lib/typescript/roomStore/chatSettingsSlice.d.ts +18 -1
- package/lib/typescript/roomStore/chatSettingsSlice.d.ts.map +1 -1
- package/lib/typescript/services/languageSettings.d.ts +29 -0
- package/lib/typescript/services/languageSettings.d.ts.map +1 -0
- package/lib/typescript/types/models/user.model.d.ts +4 -0
- package/lib/typescript/types/models/user.model.d.ts.map +1 -1
- package/lib/typescript/types/types.d.ts +23 -0
- package/lib/typescript/types/types.d.ts.map +1 -1
- package/lib/typescript/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/Header/HeaderRoomList.tsx +31 -6
- package/src/components/MainComponents/ChatHeader.tsx +16 -1
- package/src/components/MainComponents/ChatWrapper.tsx +5 -3
- package/src/components/MainComponents/ReduxWrapper.tsx +6 -0
- package/src/components/MainComponents/RoomList.tsx +78 -12
- package/src/components/Menu/HeaderRoomListMenu.tsx +40 -13
- package/src/components/MenuRoom/MenuRoom.tsx +5 -3
- package/src/components/MessageBubble/Message.tsx +3 -3
- package/src/components/MessageBubble/MessageInteractions.tsx +5 -3
- package/src/components/Modals/ChatProfileModal/ChatProfileModal.tsx +1 -1
- package/src/components/Modals/ChatProfileModal/ReportChatModal.tsx +6 -3
- package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +7 -5
- package/src/components/Modals/Modal/Modal.tsx +44 -16
- package/src/components/Modals/NewChatModal/NewChatModal.tsx +8 -8
- package/src/components/Modals/SettingsModals/ChangePassword/ChangePasswordModal.tsx +316 -0
- package/src/components/Modals/SettingsModals/ManageDataModal/ManageDataModal.tsx +10 -15
- package/src/components/Modals/SettingsModals/Visibility/VisibilityModal.tsx +13 -14
- package/src/components/Modals/UserProfileModal/UserProfileModal.tsx +19 -40
- package/src/components/Modals/UserSettingsModal/LanguageCard.tsx +144 -0
- package/src/components/Modals/UserSettingsModal/UserSettingsModal.tsx +11 -4
- package/src/components/RoomComponents/SwipeableRoomRow.tsx +133 -0
- package/src/components/Thread/ThreadWrapper.tsx +4 -2
- package/src/context/xmppProvider.tsx +21 -4
- package/src/e2ee/stanza.ts +1 -1
- package/src/helpers/constants/MODAL_COMPONENTS.tsx +2 -0
- package/src/helpers/constants/MODAL_TYPES.ts +1 -0
- package/src/helpers/preferencesStorage.ts +4 -0
- package/src/hooks/useChatSettingState.tsx +8 -0
- package/src/hooks/useChatWrapperInit.ts +4 -0
- package/src/hooks/useConnectionLabel.ts +16 -0
- package/src/i18n/strings.ts +199 -1
- package/src/i18n/useT.ts +6 -4
- package/src/networking/api-requests/user.api.ts +9 -0
- package/src/networking/stanzaHandlers.ts +1 -0
- package/src/networking/xmppClient.ts +33 -1
- package/src/roomStore/chatSettingsSlice.ts +30 -0
- package/src/services/languageSettings.ts +143 -0
- package/src/types/models/user.model.ts +4 -0
- package/src/types/types.ts +23 -0
- package/src/version.ts +1 -1
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import React, { useCallback, useMemo } from 'react';
|
|
2
|
+
import { StyleProp, StyleSheet, Text, TouchableOpacity, View, ViewStyle } from 'react-native';
|
|
3
|
+
import { useTheme } from '../../../hooks/useTheme';
|
|
4
|
+
import { useT } from '../../../i18n/useT';
|
|
5
|
+
import { useChatSettingState } from '../../../hooks/useChatSettingState';
|
|
6
|
+
import {
|
|
7
|
+
appLanguageChoices,
|
|
8
|
+
chatLanguageChoices,
|
|
9
|
+
setAppLanguage,
|
|
10
|
+
setChatLanguage,
|
|
11
|
+
type LanguageChoice,
|
|
12
|
+
} from '../../../services/languageSettings';
|
|
13
|
+
|
|
14
|
+
interface LanguageCardProps {
|
|
15
|
+
style?: StyleProp<ViewStyle>;
|
|
16
|
+
testID?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The user's two languages: the interface, and what messages are translated
|
|
21
|
+
* into. See services/languageSettings for what each one does and where it
|
|
22
|
+
* is kept.
|
|
23
|
+
*/
|
|
24
|
+
const LanguageCard: React.FC<LanguageCardProps> = ({ style, testID }) => {
|
|
25
|
+
const theme = useTheme();
|
|
26
|
+
const t = useT();
|
|
27
|
+
const { config, langSource, uiLocale } = useChatSettingState();
|
|
28
|
+
|
|
29
|
+
const appChoices = useMemo(() => appLanguageChoices(config), [config]);
|
|
30
|
+
const chatChoices = useMemo(() => chatLanguageChoices(config), [config]);
|
|
31
|
+
const appCurrent = uiLocale || config?.i18n?.locale || '';
|
|
32
|
+
// Unset follows the interface language - the backend's own rule.
|
|
33
|
+
const chatCurrent = langSource || appCurrent;
|
|
34
|
+
|
|
35
|
+
const pick = useCallback(
|
|
36
|
+
(kind: 'app' | 'chat', id: string) => {
|
|
37
|
+
void (kind === 'app' ? setAppLanguage(id) : setChatLanguage(id));
|
|
38
|
+
},
|
|
39
|
+
[]
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const same = (a: string, b: string) => a.toLowerCase() === b.toLowerCase();
|
|
43
|
+
|
|
44
|
+
const group = (
|
|
45
|
+
kind: 'app' | 'chat',
|
|
46
|
+
label: string,
|
|
47
|
+
hint: string,
|
|
48
|
+
choices: LanguageChoice[],
|
|
49
|
+
current: string
|
|
50
|
+
) => (
|
|
51
|
+
<View style={styles.group}>
|
|
52
|
+
<Text style={[styles.label, { color: theme.text }]}>{label}</Text>
|
|
53
|
+
<Text style={[styles.hint, { color: theme.textSecondary }]}>{hint}</Text>
|
|
54
|
+
<View style={styles.pills} accessibilityRole="radiogroup">
|
|
55
|
+
{choices.map((choice) => {
|
|
56
|
+
const selected = same(choice.id, current);
|
|
57
|
+
return (
|
|
58
|
+
<TouchableOpacity
|
|
59
|
+
key={choice.id}
|
|
60
|
+
testID={`settings-language-${kind}-${choice.id}`}
|
|
61
|
+
accessibilityRole="radio"
|
|
62
|
+
accessibilityState={{ selected }}
|
|
63
|
+
activeOpacity={0.7}
|
|
64
|
+
onPress={() => pick(kind, choice.id)}
|
|
65
|
+
style={[
|
|
66
|
+
styles.pill,
|
|
67
|
+
{
|
|
68
|
+
borderColor: selected ? theme.primary : theme.border,
|
|
69
|
+
backgroundColor: selected ? theme.primary : theme.surfaceSecondary,
|
|
70
|
+
},
|
|
71
|
+
]}
|
|
72
|
+
>
|
|
73
|
+
<Text
|
|
74
|
+
style={[
|
|
75
|
+
styles.pillLabel,
|
|
76
|
+
{ color: selected ? theme.textOnPrimary : theme.text },
|
|
77
|
+
]}
|
|
78
|
+
>
|
|
79
|
+
{choice.name}
|
|
80
|
+
</Text>
|
|
81
|
+
</TouchableOpacity>
|
|
82
|
+
);
|
|
83
|
+
})}
|
|
84
|
+
</View>
|
|
85
|
+
</View>
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<View style={[style, styles.card]} testID={testID || 'settings-languages'}>
|
|
90
|
+
{group(
|
|
91
|
+
'app',
|
|
92
|
+
t('settings.language.app'),
|
|
93
|
+
t('settings.language.appHint'),
|
|
94
|
+
appChoices,
|
|
95
|
+
appCurrent
|
|
96
|
+
)}
|
|
97
|
+
{group(
|
|
98
|
+
'chat',
|
|
99
|
+
t('settings.language.chat'),
|
|
100
|
+
t('settings.language.chatHint'),
|
|
101
|
+
chatChoices,
|
|
102
|
+
chatCurrent
|
|
103
|
+
)}
|
|
104
|
+
</View>
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export default LanguageCard;
|
|
109
|
+
|
|
110
|
+
const styles = StyleSheet.create({
|
|
111
|
+
// Applied over the host's card style: its surface, this layout.
|
|
112
|
+
card: {
|
|
113
|
+
flexDirection: 'column',
|
|
114
|
+
alignItems: 'stretch',
|
|
115
|
+
gap: 18,
|
|
116
|
+
},
|
|
117
|
+
group: {
|
|
118
|
+
gap: 6,
|
|
119
|
+
},
|
|
120
|
+
label: {
|
|
121
|
+
fontSize: 17,
|
|
122
|
+
fontWeight: '500',
|
|
123
|
+
},
|
|
124
|
+
hint: {
|
|
125
|
+
fontSize: 13,
|
|
126
|
+
lineHeight: 18,
|
|
127
|
+
},
|
|
128
|
+
pills: {
|
|
129
|
+
flexDirection: 'row',
|
|
130
|
+
flexWrap: 'wrap',
|
|
131
|
+
gap: 8,
|
|
132
|
+
marginTop: 6,
|
|
133
|
+
},
|
|
134
|
+
pill: {
|
|
135
|
+
paddingHorizontal: 14,
|
|
136
|
+
paddingVertical: 8,
|
|
137
|
+
borderRadius: 999,
|
|
138
|
+
borderWidth: 1,
|
|
139
|
+
},
|
|
140
|
+
pillLabel: {
|
|
141
|
+
fontSize: 14,
|
|
142
|
+
fontWeight: '600',
|
|
143
|
+
},
|
|
144
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** @format */
|
|
2
2
|
|
|
3
|
-
import EncryptionCard from '../../MainComponents/EncryptionCard';
|
|
4
3
|
import React, { useCallback, useMemo } from 'react';
|
|
4
|
+
import LanguageCard from './LanguageCard';
|
|
5
5
|
import {
|
|
6
6
|
StyleSheet,
|
|
7
7
|
Switch,
|
|
@@ -64,12 +64,17 @@ const UserSettingsModal: React.FC<UserSettingsModalProps> = ({
|
|
|
64
64
|
[t]
|
|
65
65
|
);
|
|
66
66
|
|
|
67
|
+
const showPassword = config?.settings?.changePassword === true;
|
|
67
68
|
const options = useMemo(
|
|
68
69
|
() => [
|
|
69
70
|
{ label: t('settings.manageData.title'), key: MODAL_TYPES.MANAGE_DATA },
|
|
70
71
|
{ label: t('settings.visibility.title'), key: MODAL_TYPES.VISIBILITY },
|
|
72
|
+
// Off unless the host turned it on: not every backend has the endpoint.
|
|
73
|
+
...(showPassword
|
|
74
|
+
? [{ label: t('settings.password.title'), key: MODAL_TYPES.CHANGE_PASSWORD }]
|
|
75
|
+
: []),
|
|
71
76
|
],
|
|
72
|
-
[t]
|
|
77
|
+
[t, showPassword]
|
|
73
78
|
);
|
|
74
79
|
|
|
75
80
|
const handleClick = useCallback(
|
|
@@ -160,8 +165,10 @@ const UserSettingsModal: React.FC<UserSettingsModalProps> = ({
|
|
|
160
165
|
</View>
|
|
161
166
|
) : null}
|
|
162
167
|
|
|
163
|
-
{
|
|
164
|
-
|
|
168
|
+
{/* The interface language and the translation language; off
|
|
169
|
+
unless the host turned it on, as it writes to the profile. */}
|
|
170
|
+
{config?.settings?.languages?.enabled === true ? (
|
|
171
|
+
<LanguageCard style={styles.card} />
|
|
165
172
|
) : null}
|
|
166
173
|
|
|
167
174
|
{options.map((option) => (
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import React, { useCallback, useMemo, useRef } from 'react';
|
|
2
|
+
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
|
3
|
+
import ReanimatedSwipeable, {
|
|
4
|
+
type SwipeableMethods,
|
|
5
|
+
} from 'react-native-gesture-handler/ReanimatedSwipeable';
|
|
6
|
+
import { LeaveIcon, ReportIcon } from '../../assets/icons';
|
|
7
|
+
import { useTheme } from '../../hooks/useTheme';
|
|
8
|
+
import { useT } from '../../i18n/useT';
|
|
9
|
+
import type { ChatTheme } from '../../theme/theme';
|
|
10
|
+
|
|
11
|
+
/** Width of one action button behind the row. */
|
|
12
|
+
export const ROOM_ACTION_WIDTH = 84;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Only one row shows its actions at a time, as in the messengers: opening
|
|
16
|
+
* another one, or the list being scrolled, folds the open one back.
|
|
17
|
+
*/
|
|
18
|
+
let openRow: SwipeableMethods | null = null;
|
|
19
|
+
|
|
20
|
+
export const closeOpenRoomRow = (): void => {
|
|
21
|
+
openRow?.close();
|
|
22
|
+
openRow = null;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
interface SwipeableRoomRowProps {
|
|
26
|
+
/** Identifies the row to the host's callbacks. */
|
|
27
|
+
roomJid: string;
|
|
28
|
+
onReport: (roomJid: string) => void;
|
|
29
|
+
onLeave: (roomJid: string) => void;
|
|
30
|
+
children: React.ReactNode;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A room-list row that slides left to show Report and Leave behind it,
|
|
35
|
+
* the way chats do in the big messengers. A tap on an action folds the
|
|
36
|
+
* row back first, so the list is tidy under whatever the action opens.
|
|
37
|
+
*/
|
|
38
|
+
const SwipeableRoomRow: React.FC<SwipeableRoomRowProps> = ({
|
|
39
|
+
roomJid,
|
|
40
|
+
onReport,
|
|
41
|
+
onLeave,
|
|
42
|
+
children,
|
|
43
|
+
}) => {
|
|
44
|
+
const theme = useTheme();
|
|
45
|
+
const t = useT();
|
|
46
|
+
const styles = useMemo(() => createStyles(theme), [theme]);
|
|
47
|
+
const ref = useRef<SwipeableMethods>(null);
|
|
48
|
+
|
|
49
|
+
const onWillOpen = useCallback(() => {
|
|
50
|
+
if (openRow && openRow !== ref.current) {openRow.close();}
|
|
51
|
+
openRow = ref.current;
|
|
52
|
+
}, []);
|
|
53
|
+
const onClose = useCallback(() => {
|
|
54
|
+
if (openRow === ref.current) {openRow = null;}
|
|
55
|
+
}, []);
|
|
56
|
+
|
|
57
|
+
const act = useCallback(
|
|
58
|
+
(what: (jid: string) => void) => {
|
|
59
|
+
ref.current?.close();
|
|
60
|
+
what(roomJid);
|
|
61
|
+
},
|
|
62
|
+
[roomJid]
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const renderRightActions = useCallback(
|
|
66
|
+
() => (
|
|
67
|
+
<View style={styles.actions}>
|
|
68
|
+
<TouchableOpacity
|
|
69
|
+
testID={`room-action-report-${roomJid.split('@')[0]}`}
|
|
70
|
+
accessibilityRole="button"
|
|
71
|
+
accessibilityLabel={t('action.report')}
|
|
72
|
+
activeOpacity={0.8}
|
|
73
|
+
style={[styles.action, { backgroundColor: theme.textSecondary }]}
|
|
74
|
+
onPress={() => act(onReport)}
|
|
75
|
+
>
|
|
76
|
+
<ReportIcon color={theme.textOnPrimary} width={22} height={22} />
|
|
77
|
+
<Text style={styles.label}>{t('action.report')}</Text>
|
|
78
|
+
</TouchableOpacity>
|
|
79
|
+
<TouchableOpacity
|
|
80
|
+
testID={`room-action-leave-${roomJid.split('@')[0]}`}
|
|
81
|
+
accessibilityRole="button"
|
|
82
|
+
accessibilityLabel={t('action.leave')}
|
|
83
|
+
activeOpacity={0.8}
|
|
84
|
+
style={[styles.action, { backgroundColor: theme.danger }]}
|
|
85
|
+
onPress={() => act(onLeave)}
|
|
86
|
+
>
|
|
87
|
+
<LeaveIcon color={theme.textOnPrimary} width={22} height={22} />
|
|
88
|
+
<Text style={styles.label}>{t('action.leave')}</Text>
|
|
89
|
+
</TouchableOpacity>
|
|
90
|
+
</View>
|
|
91
|
+
),
|
|
92
|
+
[act, onLeave, onReport, roomJid, styles, t, theme]
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<ReanimatedSwipeable
|
|
97
|
+
ref={ref}
|
|
98
|
+
testID={`room-swipe-${roomJid.split('@')[0]}`}
|
|
99
|
+
friction={2}
|
|
100
|
+
rightThreshold={ROOM_ACTION_WIDTH / 2}
|
|
101
|
+
overshootRight={false}
|
|
102
|
+
enableTrackpadTwoFingerGesture
|
|
103
|
+
renderRightActions={renderRightActions}
|
|
104
|
+
onSwipeableWillOpen={onWillOpen}
|
|
105
|
+
onSwipeableClose={onClose}
|
|
106
|
+
// The row must cover the actions until it moves.
|
|
107
|
+
childrenContainerStyle={{ backgroundColor: theme.listBackground }}
|
|
108
|
+
>
|
|
109
|
+
{children}
|
|
110
|
+
</ReanimatedSwipeable>
|
|
111
|
+
);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export default SwipeableRoomRow;
|
|
115
|
+
|
|
116
|
+
const createStyles = (theme: ChatTheme) =>
|
|
117
|
+
StyleSheet.create({
|
|
118
|
+
actions: {
|
|
119
|
+
flexDirection: 'row',
|
|
120
|
+
width: ROOM_ACTION_WIDTH * 2,
|
|
121
|
+
},
|
|
122
|
+
action: {
|
|
123
|
+
width: ROOM_ACTION_WIDTH,
|
|
124
|
+
alignItems: 'center',
|
|
125
|
+
justifyContent: 'center',
|
|
126
|
+
gap: 4,
|
|
127
|
+
},
|
|
128
|
+
label: {
|
|
129
|
+
color: theme.textOnPrimary,
|
|
130
|
+
fontSize: 12,
|
|
131
|
+
fontWeight: '600',
|
|
132
|
+
},
|
|
133
|
+
});
|
|
@@ -29,6 +29,7 @@ import { useSendMessage } from '../../hooks/useSendMessage';
|
|
|
29
29
|
import { createMainMessageForThread } from '../../helpers/createMainMessageForThread';
|
|
30
30
|
import { useChatSettingState } from '../../hooks/useChatSettingState';
|
|
31
31
|
import { useTheme } from '../../hooks/useTheme';
|
|
32
|
+
import { useT } from '../../i18n/useT';
|
|
32
33
|
import CustomTypingIndicator from '../styled/StyledInputComponents/CustomTypingIndicator';
|
|
33
34
|
import { KeyboardInputDock } from '../MainComponents/KeyboardInputDock';
|
|
34
35
|
import {
|
|
@@ -69,6 +70,7 @@ const ThreadWrapper: FC<ThreadWrapperProps> = ({
|
|
|
69
70
|
const reduxStore = useStore<RootState>();
|
|
70
71
|
const { config } = useChatSettingState();
|
|
71
72
|
const theme = useTheme();
|
|
73
|
+
const t = useT();
|
|
72
74
|
const insets = useSafeAreaInsets();
|
|
73
75
|
const { width: windowWidth } = useWindowDimensions();
|
|
74
76
|
const roomJid = activeMessage.roomJid;
|
|
@@ -217,7 +219,7 @@ const ThreadWrapper: FC<ThreadWrapperProps> = ({
|
|
|
217
219
|
style={[styles.fill, { backgroundColor: theme.surface }]}
|
|
218
220
|
>
|
|
219
221
|
<View style={[styles.fill, { backgroundColor: theme.chatBackground }]}>
|
|
220
|
-
<ModalHeaderComponent headerTitle=
|
|
222
|
+
<ModalHeaderComponent headerTitle={t('thread.title')} handleCloseModal={close} />
|
|
221
223
|
<MessageList
|
|
222
224
|
loadMoreMessages={loadMoreMessages}
|
|
223
225
|
CustomMessage={CustomMessageComponent}
|
|
@@ -267,7 +269,7 @@ const ThreadWrapper: FC<ThreadWrapperProps> = ({
|
|
|
267
269
|
numberOfLines={1}
|
|
268
270
|
style={[styles.alsoText, { color: theme.textSecondary }]}
|
|
269
271
|
>
|
|
270
|
-
|
|
272
|
+
{t('thread.alsoSendTo')}{' '}
|
|
271
273
|
<Text style={{ color: theme.primary, fontWeight: '600' }}>
|
|
272
274
|
{roomDisplayName}
|
|
273
275
|
</Text>
|
|
@@ -39,7 +39,12 @@ import { getRooms as prefetchRoomsViaRest } from '../networking/api-requests/roo
|
|
|
39
39
|
import { allRoomPresences } from '../networking/xmpp/allRoomPresences.xmpp';
|
|
40
40
|
import { pushSubscriptionService } from '../services/pushSubscriptionService';
|
|
41
41
|
import { store } from '../roomStore';
|
|
42
|
-
import {
|
|
42
|
+
import {
|
|
43
|
+
logout,
|
|
44
|
+
setStoreClient,
|
|
45
|
+
setConfig,
|
|
46
|
+
setConnectionState,
|
|
47
|
+
} from '../roomStore/chatSettingsSlice';
|
|
43
48
|
import { logoutService } from '../hooks/useLogout';
|
|
44
49
|
import {
|
|
45
50
|
setLogoutState,
|
|
@@ -260,7 +265,12 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config, is
|
|
|
260
265
|
created.setOnOnline(() => {
|
|
261
266
|
const underlying = (created as any).client;
|
|
262
267
|
if (!underlying) {return;}
|
|
263
|
-
|
|
268
|
+
// The headers say "Updating…" until the rooms are joined again and
|
|
269
|
+
// the archive caught up - a stream that is up is not yet a chat
|
|
270
|
+
// that is. Same tick as the client's own 'online', so the title
|
|
271
|
+
// never shows the plain state in between.
|
|
272
|
+
store.dispatch(setConnectionState('syncing'));
|
|
273
|
+
const rejoin = allRoomPresences(underlying)
|
|
264
274
|
.catch((e) =>
|
|
265
275
|
devPushLog('warn', 'reconnect: allRoomPresences re-join failed', e)
|
|
266
276
|
)
|
|
@@ -268,7 +278,7 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config, is
|
|
|
268
278
|
// Also refresh the private store so unread / lastViewed markers
|
|
269
279
|
// are accurate after a long reconnect — the MUC re-join above only
|
|
270
280
|
// restores delivery, not unread state. Idempotent on first connect.
|
|
271
|
-
created
|
|
281
|
+
const markers = created
|
|
272
282
|
.getChatsPrivateStoreRequestStanza()
|
|
273
283
|
.catch((e: unknown) =>
|
|
274
284
|
devPushLog('warn', 'reconnect: privateStore refresh failed', e)
|
|
@@ -283,9 +293,16 @@ export const XmppProvider: React.FC<XmppProviderProps> = ({ children, config, is
|
|
|
283
293
|
// point, so this is a no-op until the bootstrap's own history load
|
|
284
294
|
// populates it. Customer-reported #32.
|
|
285
295
|
const rooms = store.getState().rooms?.rooms || {};
|
|
286
|
-
updateMessagesTillLast(rooms, created).catch((e: unknown) =>
|
|
296
|
+
const catchUp = updateMessagesTillLast(rooms, created).catch((e: unknown) =>
|
|
287
297
|
devPushLog('warn', 'reconnect: offline catch-up sync failed', e)
|
|
288
298
|
);
|
|
299
|
+
Promise.allSettled([rejoin, markers, catchUp]).then(() => {
|
|
300
|
+
// Only if this stream is still the live one: a drop meanwhile
|
|
301
|
+
// has already put the headers back to "Connecting…".
|
|
302
|
+
if (created.status === 'online') {
|
|
303
|
+
store.dispatch(setConnectionState('online'));
|
|
304
|
+
}
|
|
305
|
+
});
|
|
289
306
|
});
|
|
290
307
|
|
|
291
308
|
setClient(created);
|
package/src/e2ee/stanza.ts
CHANGED
|
@@ -48,7 +48,7 @@ function placeholder(error: E2eeError): Element {
|
|
|
48
48
|
return new Element('body', {}).t(
|
|
49
49
|
translateKey(
|
|
50
50
|
placeholderKey(error),
|
|
51
|
-
settings?.config?.i18n?.locale || settings?.langSource,
|
|
51
|
+
settings?.uiLocale || settings?.config?.i18n?.locale || settings?.langSource,
|
|
52
52
|
settings?.config?.i18n?.strings
|
|
53
53
|
)
|
|
54
54
|
);
|
|
@@ -14,6 +14,7 @@ import ManageDataModal from '../../components/Modals/SettingsModals/ManageDataMo
|
|
|
14
14
|
import ProfileSharesModal from '../../components/Modals/SettingsModals/ProfileShares/ProfileShares';
|
|
15
15
|
import ReferralsModal from '../../components/Modals/SettingsModals/Referrals/Referrals';
|
|
16
16
|
import VisibilityModal from '../../components/Modals/SettingsModals/Visibility/VisibilityModal';
|
|
17
|
+
import ChangePasswordModal from '../../components/Modals/SettingsModals/ChangePassword/ChangePasswordModal';
|
|
17
18
|
import UserProfileModal from '../../components/Modals/UserProfileModal/UserProfileModal';
|
|
18
19
|
import UserSettingsModal from '../../components/Modals/UserSettingsModal/UserSettingsModal';
|
|
19
20
|
|
|
@@ -27,6 +28,7 @@ export const MODAL_COMPONENTS: Record<
|
|
|
27
28
|
[MODAL_TYPES.CHAT_PROFILE]: ChatProfileModal,
|
|
28
29
|
[MODAL_TYPES.MANAGE_DATA]: ManageDataModal,
|
|
29
30
|
[MODAL_TYPES.VISIBILITY]: VisibilityModal,
|
|
31
|
+
[MODAL_TYPES.CHANGE_PASSWORD]: ChangePasswordModal,
|
|
30
32
|
[MODAL_TYPES.REFERRALS]: ReferralsModal,
|
|
31
33
|
[MODAL_TYPES.DOCUMENT_SHARES]: DocumentSharesModal,
|
|
32
34
|
[MODAL_TYPES.PROFILE_SHARES]: ProfileSharesModal,
|
|
@@ -9,6 +9,10 @@ const KEY = '@ethora/preferences';
|
|
|
9
9
|
export interface StoredPreferences {
|
|
10
10
|
theme?: ThemePreference;
|
|
11
11
|
pushEnabled?: boolean;
|
|
12
|
+
/** Interface language picked in Settings (BCP-47). */
|
|
13
|
+
uiLocale?: string;
|
|
14
|
+
/** Language messages are translated into, picked in Settings (BCP-47). */
|
|
15
|
+
chatLanguage?: string;
|
|
12
16
|
}
|
|
13
17
|
|
|
14
18
|
export const loadPreferences = async (): Promise<StoredPreferences> => {
|
|
@@ -9,6 +9,12 @@ export const useChatSettingState = () => {
|
|
|
9
9
|
const activeModal = useSelector(
|
|
10
10
|
(state: RootState) => state.chatSettingStore.activeModal
|
|
11
11
|
);
|
|
12
|
+
const uiLocale = useSelector(
|
|
13
|
+
(state: RootState) => state.chatSettingStore.uiLocale
|
|
14
|
+
);
|
|
15
|
+
const connection = useSelector(
|
|
16
|
+
(state: RootState) => state.chatSettingStore.connection
|
|
17
|
+
);
|
|
12
18
|
const config = useSelector(
|
|
13
19
|
(state: RootState) => state.chatSettingStore.config
|
|
14
20
|
);
|
|
@@ -29,6 +35,8 @@ export const useChatSettingState = () => {
|
|
|
29
35
|
);
|
|
30
36
|
|
|
31
37
|
return {
|
|
38
|
+
uiLocale,
|
|
39
|
+
connection,
|
|
32
40
|
user,
|
|
33
41
|
activeFile,
|
|
34
42
|
activeModal,
|
|
@@ -12,6 +12,7 @@ import { initRoomsPresence } from '../helpers/initRoomsPresence';
|
|
|
12
12
|
import { updateMessagesTillLast } from '../helpers/updateMessagesTillLast';
|
|
13
13
|
import { refreshAuthTokensQuietly } from '../networking/authRefresh';
|
|
14
14
|
import { setLangSource, setConfig } from '../roomStore/chatSettingsSlice';
|
|
15
|
+
import { applyProfileLanguages } from '../services/languageSettings';
|
|
15
16
|
import { setIsLoading } from '../roomStore/roomsSlice';
|
|
16
17
|
import { useRoomState } from './useRoomState';
|
|
17
18
|
import { useChatSettingState } from './useChatSettingState';
|
|
@@ -120,6 +121,9 @@ const useChatWrapperInit = ({
|
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
dispatch(setConfig(config));
|
|
124
|
+
// The profile's languages are the account's truth, over what the
|
|
125
|
+
// device remembered.
|
|
126
|
+
applyProfileLanguages(user as any, config);
|
|
123
127
|
try {
|
|
124
128
|
if (!user.xmppUsername) {
|
|
125
129
|
setShowModal(true);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useChatSettingState } from './useChatSettingState';
|
|
2
|
+
import { useT } from '../i18n/useT';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* What the headers say in place of their usual title while the session is
|
|
6
|
+
* not usable yet: "Connecting…" until the stream is up (first connect and
|
|
7
|
+
* every reconnect), "Updating…" while rooms are re-joined and the archive
|
|
8
|
+
* caught up. Nothing once the chat is live.
|
|
9
|
+
*/
|
|
10
|
+
export const useConnectionLabel = (): string | undefined => {
|
|
11
|
+
const { connection } = useChatSettingState();
|
|
12
|
+
const t = useT();
|
|
13
|
+
if (connection === 'connecting') {return t('connection.connecting');}
|
|
14
|
+
if (connection === 'syncing') {return t('connection.updating');}
|
|
15
|
+
return undefined;
|
|
16
|
+
};
|