Skip to content

Commit c64f3e2

Browse files
Merge pull request #958 from GetStream/develop
Next Release
2 parents 8950840 + 2598091 commit c64f3e2

File tree

11 files changed

+110
-39
lines changed

11 files changed

+110
-39
lines changed

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
git config --global user.name "Vishal Narkhede"
3030
git config --global user.email "[email protected]"
3131
- name: Installation && Build SDK
32-
run: yarn --frozen-lockfile && yarn bootstrap-ci
32+
run: yarn bootstrap-ci
3333
- name: Lint
3434
run: yarn lerna-workspaces run lint
3535
- name: Publish Release

PULL_REQUEST_TEMPLATE.md

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,51 @@
1111
<!-- Add relevant screenshots -->
1212

1313
<details>
14-
<summary>iOS</summary>
14+
<summary>iOS</summary>
1515

16-
| Before | After |
17-
| --- | --- |
18-
| img | img |
16+
17+
<table>
18+
<thead>
19+
<tr>
20+
<td>Before</td>
21+
<td>After</td>
22+
</tr>
23+
</thead>
24+
<tbody>
25+
<tr>
26+
<td>
27+
<!--<img src="" /> -->
28+
</td>
29+
<td>
30+
<!--<img src="" /> -->
31+
</td>
32+
</tr>
33+
</tbody>
34+
</table>
1935
</details>
2036

2137

2238
<details>
23-
<summary>Android</summary>
39+
<summary>Android</summary>
2440

25-
| Before | After |
26-
| --- | --- |
27-
| img | img |
41+
<table>
42+
<thead>
43+
<tr>
44+
<td>Before</td>
45+
<td>After</td>
46+
</tr>
47+
</thead>
48+
<tbody>
49+
<tr>
50+
<td>
51+
<!--<img src="" /> -->
52+
</td>
53+
<td>
54+
<!--<img src="" /> -->
55+
</td>
56+
</tr>
57+
</tbody>
58+
</table>
2859
</details>
2960

3061
## 🧪 Testing

examples/SampleApp/.eslintrc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@
140140
"no-mixed-spaces-and-tabs": 1,
141141
"no-self-compare": 2,
142142
"no-underscore-dangle": [2, { "allowAfterThis": true }],
143-
"no-unused-vars": [1, { "ignoreRestSiblings": true }],
143+
"no-unused-vars": [1, { "ignoreRestSiblings": true, "args":"none" }],
144144
"no-useless-concat": 2,
145145
"no-var": 2,
146146
"object-shorthand": 1,

examples/SampleApp/src/components/ChatScreenHeader.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useContext } from 'react';
2-
import { Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
2+
import { Image, StyleSheet, TouchableOpacity } from 'react-native';
33
import { CompositeNavigationProp, useNavigation } from '@react-navigation/native';
44
import { useChatContext, useTheme } from 'stream-chat-react-native';
55

examples/SampleApp/src/hooks/useChatClient.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ import type {
1515
LoginConfig,
1616
} from '../types';
1717

18-
const getRandomInt = (min: number, max: number) => Math.floor(Math.random() * (max - min)) + min;
19-
2018
export const useChatClient = () => {
2119
const [chatClient, setChatClient] = useState<StreamChat<
2220
LocalAttachmentType,
@@ -41,7 +39,6 @@ export const useChatClient = () => {
4139
>(config.apiKey, {
4240
timeout: 6000,
4341
});
44-
const randomSeed = getRandomInt(1, 50);
4542
const user = {
4643
id: config.userId,
4744
image: config.userImage,

examples/SampleApp/src/icons/Close.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { useTheme } from 'stream-chat-react-native';
44

55
import { IconProps } from '../utils/base';
66

7-
export const Close: React.FC<IconProps> = ({ active, height, width }) => {
7+
export const Close: React.FC<IconProps> = ({ height, width }) => {
88
const {
99
theme: {
1010
colors: { black },

examples/SampleApp/src/icons/RightArrow.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { useTheme } from 'stream-chat-react-native';
44

55
import { IconProps } from '../utils/base';
66

7-
export const RightArrow: React.FC<IconProps> = ({ active, height, width }) => {
7+
export const RightArrow: React.FC<IconProps> = ({ height, width }) => {
88
const {
99
theme: {
1010
colors: { accent_blue },

examples/SampleApp/src/screens/NewDirectMessagingScreen.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
2-
import { Alert, Platform, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native';
1+
import React, { useContext, useEffect, useRef, useState } from 'react';
2+
import { Platform, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native';
33
import { SafeAreaView } from 'react-native-safe-area-context';
44
import {
55
Channel,
66
Group,
77
MessageInput,
88
MessageList,
9-
SendButton,
10-
SendButtonProps,
119
User,
1210
UserAdd,
1311
useTheme,
@@ -163,7 +161,8 @@ export const NewDirectMessagingScreen: React.FC<NewDirectMessagingScreenProps> =
163161

164162
const [focusOnMessageInput, setFocusOnMessageInput] = useState(false);
165163
const [focusOnSearchInput, setFocusOnSearchInput] = useState(true);
166-
const [messageInputText, setMessageInputText] = useState('');
164+
// As we don't use the state value, we can omit it here and separate it with a comma within the array.
165+
const [, setMessageInputText] = useState('');
167166

168167
// When selectedUsers are changed, initiate a channel with those users as members,
169168
// and set it as a channel on current screen.

package/src/components/Channel/Channel.tsx

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ const ChannelWithContext = <
544544
colors: { black },
545545
},
546546
} = useTheme();
547-
547+
const [deleted, setDeleted] = useState(false);
548548
const [editing, setEditing] = useState<boolean | MessageType<At, Ch, Co, Ev, Me, Re, Us>>(false);
549549
const [error, setError] = useState(false);
550550
const [hasMore, setHasMore] = useState(true);
@@ -670,7 +670,7 @@ const ChannelWithContext = <
670670
const markRead: ChannelContextValue<At, Ch, Co, Ev, Me, Re, Us>['markRead'] = useRef(
671671
throttle(
672672
() => {
673-
if (channel?.disconnected || !channel?.getConfig?.()?.read_events) {
673+
if (!channel || channel?.disconnected || !clientChannelConfig?.read_events) {
674674
return;
675675
}
676676

@@ -778,18 +778,33 @@ const ChannelWithContext = <
778778
};
779779

780780
useEffect(() => {
781-
/**
782-
* The more complex sync logic around internet connectivity (NetInfo) is part of Chat.tsx
783-
* listen to client.connection.recovered and all channel events
784-
*/
785-
client.on('connection.recovered', connectionRecoveredHandler);
786-
client.on('connection.changed', connectionChangedHandler);
787-
channel?.on(handleEvent);
781+
const channelSubscriptions: Array<ReturnType<ChannelType['on']>> = [];
782+
const clientSubscriptions: Array<ReturnType<StreamChat['on']>> = [];
783+
784+
const subscribe = () => {
785+
if (!channel) return;
786+
787+
/**
788+
* The more complex sync logic around internet connectivity (NetInfo) is part of Chat.tsx
789+
* listen to client.connection.recovered and all channel events
790+
*/
791+
clientSubscriptions.push(client.on('connection.recovered', connectionRecoveredHandler));
792+
clientSubscriptions.push(client.on('connection.changed', connectionChangedHandler));
793+
clientSubscriptions.push(
794+
client.on('channel.deleted', (event) => {
795+
if (event.cid === channel.cid) {
796+
setDeleted(true);
797+
}
798+
}),
799+
);
800+
channelSubscriptions.push(channel.on(handleEvent));
801+
};
802+
803+
subscribe();
788804

789805
return () => {
790-
client.off('connection.recovered', connectionRecoveredHandler);
791-
client.off('connection.changed', connectionChangedHandler);
792-
channel?.off(handleEvent);
806+
clientSubscriptions.forEach((s) => s.unsubscribe());
807+
channelSubscriptions.forEach((s) => s.unsubscribe());
793808
};
794809
}, [channelId, connectionRecoveredHandler, handleEvent]);
795810

@@ -1152,13 +1167,23 @@ const ChannelWithContext = <
11521167
}
11531168
};
11541169

1170+
// In case the channel is disconnected which may happen when channel is deleted,
1171+
// underlying js client throws an error. Following function ensures that Channel component
1172+
// won't result in error in such a case.
1173+
const getChannelConfigSafely = () => {
1174+
try {
1175+
return channel?.getConfig();
1176+
} catch (_) {
1177+
return null;
1178+
}
1179+
};
1180+
11551181
/**
11561182
* Channel configs for use in disabling local functionality.
11571183
* Nullish coalescing is used to give first priority to props to override
11581184
* the server settings. Then priority to server settings to override defaults.
11591185
*/
1160-
const clientChannelConfig =
1161-
typeof channel?.getConfig === 'function' ? channel.getConfig() : undefined;
1186+
const clientChannelConfig = getChannelConfigSafely();
11621187

11631188
const messagesConfig: MessagesConfig = {
11641189
/**
@@ -1597,7 +1622,7 @@ const ChannelWithContext = <
15971622
error,
15981623
giphyEnabled:
15991624
giphyEnabled ??
1600-
!!(channel?.getConfig?.()?.commands || [])?.some((command) => command.name === 'giphy'),
1625+
!!(clientChannelConfig?.commands || [])?.some((command) => command.name === 'giphy'),
16011626
hideDateSeparators,
16021627
hideStickyDateHeader,
16031628
isAdmin,
@@ -1780,6 +1805,9 @@ const ChannelWithContext = <
17801805
typing,
17811806
});
17821807

1808+
// TODO: replace the null view with appropriate message. Currently this is waiting a design decision.
1809+
if (deleted) return null;
1810+
17831811
if (!channel || (error && messages.length === 0)) {
17841812
return <LoadingErrorIndicator error={error} listType='message' retry={reloadChannel} />;
17851813
}

package/src/components/Channel/__tests__/Channel.test.js

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useContext, useEffect } from 'react';
22
import { View } from 'react-native';
3-
import { cleanup, render, waitFor } from '@testing-library/react-native';
3+
import { act, cleanup, render, waitFor } from '@testing-library/react-native';
44
import { StreamChat } from 'stream-chat';
55

66
import { Channel } from '../Channel';
@@ -14,8 +14,10 @@ import {
1414
MessagesProvider,
1515
} from '../../../contexts/messagesContext/MessagesContext';
1616
import { ThreadContext, ThreadProvider } from '../../../contexts/threadContext/ThreadContext';
17+
1718
import { useMockedApis } from '../../../mock-builders/api/useMockedApis';
1819
import { getOrCreateChannelApi } from '../../../mock-builders/api/getOrCreateChannel';
20+
import dispatchChannelDeletedEvent from '../../../mock-builders/event/channelDeleted';
1921
import { generateChannel } from '../../../mock-builders/generator/channel';
2022
import { generateMember } from '../../../mock-builders/generator/member';
2123
import { generateMessage } from '../../../mock-builders/generator/message';
@@ -65,6 +67,7 @@ describe('Channel', () => {
6567
chatClient = await getTestClientWithUser(user);
6668
useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
6769
channel = chatClient.channel('messaging', mockedChannel.id);
70+
channel.cid = mockedChannel.channel.cid;
6871
});
6972

7073
afterEach(() => {
@@ -77,7 +80,9 @@ describe('Channel', () => {
7780
...channel,
7881
cid: null,
7982
off: () => {},
80-
on: () => {},
83+
on: () => ({
84+
unsubscribe: () => null,
85+
}),
8186
watch: () => {},
8287
};
8388
const { getByTestId } = renderComponent({ channel: nullChannel });
@@ -193,6 +198,17 @@ describe('Channel', () => {
193198
await waitFor(() => expect(channelQuerySpy).toHaveBeenCalled());
194199
});
195200

201+
it('should render null if channel gets deleted', async () => {
202+
const { getByTestId, queryByTestId } = renderComponent({
203+
channel,
204+
children: <View testID='children' />,
205+
});
206+
207+
await waitFor(() => expect(getByTestId('children')).toBeTruthy());
208+
act(() => dispatchChannelDeletedEvent(chatClient, channel));
209+
expect(queryByTestId('children')).toBeNull();
210+
});
211+
196212
describe('ChannelContext', () => {
197213
it('renders children without crashing', async () => {
198214
const { getByTestId } = render(

0 commit comments

Comments
 (0)