@ethora/chat-component-rn 26.8.8 → 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 +6 -0
- package/README.md +2 -1
- 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/RoomList.js +63 -11
- package/lib/commonjs/components/MainComponents/RoomList.js.map +1 -1
- package/lib/commonjs/components/Modals/ChatProfileModal/ReportChatModal.js +3 -2
- package/lib/commonjs/components/Modals/ChatProfileModal/ReportChatModal.js.map +1 -1
- package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.js +1 -29
- package/lib/commonjs/components/Modals/UserProfileModal/UserProfileModal.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/context/xmppProvider.js +15 -3
- package/lib/commonjs/context/xmppProvider.js.map +1 -1
- package/lib/commonjs/hooks/useChatSettingState.js +2 -0
- package/lib/commonjs/hooks/useChatSettingState.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 +12 -0
- package/lib/commonjs/i18n/strings.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 +7 -2
- package/lib/commonjs/roomStore/chatSettingsSlice.js.map +1 -1
- 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/RoomList.js +64 -12
- package/lib/module/components/MainComponents/RoomList.js.map +1 -1
- package/lib/module/components/Modals/ChatProfileModal/ReportChatModal.js +3 -2
- package/lib/module/components/Modals/ChatProfileModal/ReportChatModal.js.map +1 -1
- package/lib/module/components/Modals/UserProfileModal/UserProfileModal.js +2 -30
- package/lib/module/components/Modals/UserProfileModal/UserProfileModal.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/context/xmppProvider.js +16 -4
- package/lib/module/context/xmppProvider.js.map +1 -1
- package/lib/module/hooks/useChatSettingState.js +2 -0
- package/lib/module/hooks/useChatSettingState.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 +12 -0
- package/lib/module/i18n/strings.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 +5 -1
- package/lib/module/roomStore/chatSettingsSlice.js.map +1 -1
- 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/RoomList.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/UserProfileModal/UserProfileModal.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/context/xmppProvider.d.ts.map +1 -1
- package/lib/typescript/hooks/useChatSettingState.d.ts +1 -0
- package/lib/typescript/hooks/useChatSettingState.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/networking/xmppClient.d.ts +11 -1
- package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
- package/lib/typescript/roomStore/chatSettingsSlice.d.ts +11 -1
- package/lib/typescript/roomStore/chatSettingsSlice.d.ts.map +1 -1
- package/lib/typescript/types/types.d.ts +2 -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/RoomList.tsx +78 -12
- package/src/components/Modals/ChatProfileModal/ReportChatModal.tsx +4 -1
- package/src/components/Modals/UserProfileModal/UserProfileModal.tsx +0 -28
- package/src/components/RoomComponents/SwipeableRoomRow.tsx +133 -0
- package/src/context/xmppProvider.tsx +21 -4
- package/src/hooks/useChatSettingState.tsx +4 -0
- package/src/hooks/useConnectionLabel.ts +16 -0
- package/src/i18n/strings.ts +12 -0
- package/src/networking/xmppClient.ts +33 -1
- package/src/roomStore/chatSettingsSlice.ts +17 -0
- package/src/types/types.ts +2 -0
- package/src/version.ts +1 -1
|
@@ -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
|
+
});
|
|
@@ -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);
|
|
@@ -12,6 +12,9 @@ export const useChatSettingState = () => {
|
|
|
12
12
|
const uiLocale = useSelector(
|
|
13
13
|
(state: RootState) => state.chatSettingStore.uiLocale
|
|
14
14
|
);
|
|
15
|
+
const connection = useSelector(
|
|
16
|
+
(state: RootState) => state.chatSettingStore.connection
|
|
17
|
+
);
|
|
15
18
|
const config = useSelector(
|
|
16
19
|
(state: RootState) => state.chatSettingStore.config
|
|
17
20
|
);
|
|
@@ -33,6 +36,7 @@ export const useChatSettingState = () => {
|
|
|
33
36
|
|
|
34
37
|
return {
|
|
35
38
|
uiLocale,
|
|
39
|
+
connection,
|
|
36
40
|
user,
|
|
37
41
|
activeFile,
|
|
38
42
|
activeModal,
|
|
@@ -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
|
+
};
|
package/src/i18n/strings.ts
CHANGED
|
@@ -16,6 +16,8 @@ export type UiStringTable = Record<string, string>;
|
|
|
16
16
|
const en: UiStringTable = {
|
|
17
17
|
'search.placeholder': 'Search...',
|
|
18
18
|
'roomList.title': 'Chats',
|
|
19
|
+
'connection.connecting': 'Connecting…',
|
|
20
|
+
'connection.updating': 'Updating…',
|
|
19
21
|
'menu.newChat': 'New Chat',
|
|
20
22
|
'menu.profile': 'Profile',
|
|
21
23
|
'menu.settings': 'Settings',
|
|
@@ -335,6 +337,8 @@ const en: UiStringTable = {
|
|
|
335
337
|
const fr: UiStringTable = {
|
|
336
338
|
'search.placeholder': 'Rechercher...',
|
|
337
339
|
'roomList.title': 'Discussions',
|
|
340
|
+
'connection.connecting': 'Connexion…',
|
|
341
|
+
'connection.updating': 'Mise à jour…',
|
|
338
342
|
'menu.newChat': 'Nouvelle discussion',
|
|
339
343
|
'menu.profile': 'Profil',
|
|
340
344
|
'menu.settings': 'Paramètres',
|
|
@@ -654,6 +658,8 @@ const fr: UiStringTable = {
|
|
|
654
658
|
const es: UiStringTable = {
|
|
655
659
|
'search.placeholder': 'Buscar...',
|
|
656
660
|
'roomList.title': 'Conversaciones',
|
|
661
|
+
'connection.connecting': 'Conectando…',
|
|
662
|
+
'connection.updating': 'Actualizando…',
|
|
657
663
|
'menu.newChat': 'Nuevo chat',
|
|
658
664
|
'menu.profile': 'Perfil',
|
|
659
665
|
'menu.settings': 'Configuración',
|
|
@@ -973,6 +979,8 @@ const es: UiStringTable = {
|
|
|
973
979
|
const pt: UiStringTable = {
|
|
974
980
|
'search.placeholder': 'Pesquisar...',
|
|
975
981
|
'roomList.title': 'Conversas',
|
|
982
|
+
'connection.connecting': 'A ligar…',
|
|
983
|
+
'connection.updating': 'A atualizar…',
|
|
976
984
|
'menu.newChat': 'Nova conversa',
|
|
977
985
|
'menu.profile': 'Perfil',
|
|
978
986
|
'menu.settings': 'Definições',
|
|
@@ -1292,6 +1300,8 @@ const pt: UiStringTable = {
|
|
|
1292
1300
|
const ht: UiStringTable = {
|
|
1293
1301
|
'search.placeholder': 'Chèche...',
|
|
1294
1302
|
'roomList.title': 'Chat yo',
|
|
1303
|
+
'connection.connecting': 'Ap konekte…',
|
|
1304
|
+
'connection.updating': 'Ap mete ajou…',
|
|
1295
1305
|
'menu.newChat': 'Nouvo chat',
|
|
1296
1306
|
'menu.profile': 'Pwofil',
|
|
1297
1307
|
'menu.settings': 'Paramèt',
|
|
@@ -1611,6 +1621,8 @@ const ht: UiStringTable = {
|
|
|
1611
1621
|
const zh: UiStringTable = {
|
|
1612
1622
|
'search.placeholder': '搜索...',
|
|
1613
1623
|
'roomList.title': '聊天',
|
|
1624
|
+
'connection.connecting': '连接中…',
|
|
1625
|
+
'connection.updating': '更新中…',
|
|
1614
1626
|
'menu.newChat': '新聊天',
|
|
1615
1627
|
'menu.profile': '个人资料',
|
|
1616
1628
|
'menu.settings': '设置',
|
|
@@ -29,6 +29,7 @@ import { pushLog as devPushLog, isDevLogActive } from '../utils/devLogger';
|
|
|
29
29
|
import { normalizeRoomJid } from '../helpers/normalizeRoomJid';
|
|
30
30
|
import { store } from '../roomStore';
|
|
31
31
|
import { applyPrivateStoreMarkers } from '../roomStore/roomsSlice';
|
|
32
|
+
import { setConnectionState } from '../roomStore/chatSettingsSlice';
|
|
32
33
|
import {
|
|
33
34
|
clearOutboundSends,
|
|
34
35
|
enqueueOutboundSend,
|
|
@@ -129,7 +130,38 @@ export class XmppClient {
|
|
|
129
130
|
service: string = '';
|
|
130
131
|
conference: string = '';
|
|
131
132
|
username: string;
|
|
132
|
-
|
|
133
|
+
private _status: 'offline' | 'connecting' | 'online' | 'error' = 'offline';
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The stream's state. Every change is mirrored into the store
|
|
137
|
+
* (`chatSettingStore.connection`) so the headers can say "Connecting…":
|
|
138
|
+
* a class field is nothing React can follow. `offline` and `error` are
|
|
139
|
+
* reported as `connecting` while a retry is scheduled - that is what the
|
|
140
|
+
* user is waiting for - and as `offline` only once nothing will try
|
|
141
|
+
* (logout, unmount).
|
|
142
|
+
*/
|
|
143
|
+
get status(): 'offline' | 'connecting' | 'online' | 'error' {
|
|
144
|
+
return this._status;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
set status(next: 'offline' | 'connecting' | 'online' | 'error') {
|
|
148
|
+
const changed = next !== this._status;
|
|
149
|
+
this._status = next;
|
|
150
|
+
if (!changed) {return;}
|
|
151
|
+
try {
|
|
152
|
+
const reported =
|
|
153
|
+
next === 'online'
|
|
154
|
+
? 'online'
|
|
155
|
+
: next === 'connecting' || !this.suppressReconnect
|
|
156
|
+
? 'connecting'
|
|
157
|
+
: 'offline';
|
|
158
|
+
if (store.getState().chatSettingStore.connection !== reported) {
|
|
159
|
+
store.dispatch(setConnectionState(reported));
|
|
160
|
+
}
|
|
161
|
+
} catch {
|
|
162
|
+
/* no store in some test harnesses */
|
|
163
|
+
}
|
|
164
|
+
}
|
|
133
165
|
|
|
134
166
|
password = '';
|
|
135
167
|
reconnectAttempts = 0;
|
|
@@ -44,8 +44,18 @@ export interface ChatState {
|
|
|
44
44
|
* `config.i18n.locale`, which is the default for a user who never chose.
|
|
45
45
|
*/
|
|
46
46
|
uiLocale?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Where the XMPP session stands, for the headers: `connecting` until the
|
|
49
|
+
* stream is up (also while it is being re-established), `syncing` while
|
|
50
|
+
* rooms are re-joined and the archive caught up after a (re)connect,
|
|
51
|
+
* `online` once that is done, `offline` when the stream is down and
|
|
52
|
+
* nothing is trying (logged out).
|
|
53
|
+
*/
|
|
54
|
+
connection?: ConnectionState;
|
|
47
55
|
}
|
|
48
56
|
|
|
57
|
+
export type ConnectionState = 'connecting' | 'syncing' | 'online' | 'offline';
|
|
58
|
+
|
|
49
59
|
export const unpackAndTransform = (input?: User): User => {
|
|
50
60
|
return {
|
|
51
61
|
// Was hardcoded to '' — every setUser dropped the user's own bio, so
|
|
@@ -231,6 +241,12 @@ const reducers = {
|
|
|
231
241
|
) => {
|
|
232
242
|
state.uiLocale = action.payload || undefined;
|
|
233
243
|
},
|
|
244
|
+
setConnectionState: (
|
|
245
|
+
state: WritableDraft<ChatState>,
|
|
246
|
+
action: PayloadAction<ConnectionState>
|
|
247
|
+
) => {
|
|
248
|
+
state.connection = action.payload;
|
|
249
|
+
},
|
|
234
250
|
};
|
|
235
251
|
|
|
236
252
|
export const chatSlice: Slice<ChatState, typeof reducers, 'chat'> = createSlice({
|
|
@@ -255,6 +271,7 @@ export const {
|
|
|
255
271
|
setThemePreference,
|
|
256
272
|
setPushEnabled,
|
|
257
273
|
setUiLocale,
|
|
274
|
+
setConnectionState,
|
|
258
275
|
} = chatSlice.actions;
|
|
259
276
|
|
|
260
277
|
export default chatSlice.reducer;
|
package/src/types/types.ts
CHANGED
|
@@ -652,6 +652,8 @@ export interface IConfig {
|
|
|
652
652
|
};
|
|
653
653
|
disableProfilesInteractions?: boolean;
|
|
654
654
|
disableUserCount?: boolean;
|
|
655
|
+
/** No Report / Leave behind a room-list row swiped to the left. */
|
|
656
|
+
disableRoomSwipeActions?: boolean;
|
|
655
657
|
disableSentLogic?: boolean;
|
|
656
658
|
disableTypingIndicator?: boolean;
|
|
657
659
|
// Hide the full-screen "Connection error" overlay (the dark modal with
|
package/src/version.ts
CHANGED