Skip to content

Commit bb66a63

Browse files
committed
fix: channel pinning and archiving condition fix
1 parent 551064d commit bb66a63

File tree

9 files changed

+141
-74
lines changed

9 files changed

+141
-74
lines changed

examples/SampleApp/src/components/ChannelInfoOverlay.tsx

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,5 @@
11
import React, { useEffect } from 'react';
2-
import {
3-
FlatList,
4-
Keyboard,
5-
Pressable,
6-
SafeAreaView,
7-
StyleSheet,
8-
Text,
9-
View,
10-
ViewStyle,
11-
} from 'react-native';
2+
import { FlatList, Keyboard, SafeAreaView, StyleSheet, Text, View, ViewStyle } from 'react-native';
123
import dayjs from 'dayjs';
134
import relativeTime from 'dayjs/plugin/relativeTime';
145
import {
@@ -372,19 +363,21 @@ export const ChannelInfoOverlay = (props: ChannelInfoOverlayProps) => {
372363
<Text style={[styles.rowText, { color: black }]}>View info</Text>
373364
</View>
374365
</TapGestureHandler>
375-
<Pressable
376-
onPress={async () => {
377-
try {
378-
if (membership?.pinned_at) {
379-
await channel.unpin();
380-
} else {
381-
await channel.pin();
366+
<TapGestureHandler
367+
onHandlerStateChange={async ({ nativeEvent: { state } }) => {
368+
if (state === State.END) {
369+
try {
370+
if (membership?.pinned_at) {
371+
await channel.unpin();
372+
} else {
373+
await channel.pin();
374+
}
375+
} catch (error) {
376+
console.log('Error pinning/unpinning channel', error);
382377
}
383-
} catch (error) {
384-
console.log('Error pinning/unpinning channel', error);
385-
}
386378

387-
setOverlay('none');
379+
setOverlay('none');
380+
}
388381
}}
389382
>
390383
<View
@@ -402,20 +395,22 @@ export const ChannelInfoOverlay = (props: ChannelInfoOverlayProps) => {
402395
{membership?.pinned_at ? 'Unpin' : 'Pin'}
403396
</Text>
404397
</View>
405-
</Pressable>
406-
<Pressable
407-
onPress={async () => {
408-
try {
409-
if (membership?.archived_at) {
410-
await channel.unarchive();
411-
} else {
412-
await channel.archive();
398+
</TapGestureHandler>
399+
<TapGestureHandler
400+
onHandlerStateChange={async ({ nativeEvent: { state } }) => {
401+
if (state === State.END) {
402+
try {
403+
if (membership?.archived_at) {
404+
await channel.unarchive();
405+
} else {
406+
await channel.archive();
407+
}
408+
} catch (error) {
409+
console.log('Error archiving/unarchiving channel', error);
413410
}
414-
} catch (error) {
415-
console.log('Error archiving/unarchiving channel', error);
416-
}
417411

418-
setOverlay('none');
412+
setOverlay('none');
413+
}
419414
}}
420415
>
421416
<View
@@ -433,7 +428,8 @@ export const ChannelInfoOverlay = (props: ChannelInfoOverlayProps) => {
433428
{membership?.archived_at ? 'Unarchieve' : 'Archieve'}
434429
</Text>
435430
</View>
436-
</Pressable>
431+
</TapGestureHandler>
432+
437433
{otherMembers.length > 1 && (
438434
<TapGestureHandler
439435
onHandlerStateChange={({ nativeEvent: { state } }) => {

examples/SampleApp/src/components/ChannelPreview.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
ChannelPreviewStatusProps,
1111
Delete,
1212
MenuPointHorizontal,
13+
Pin,
1314
useChannelMembershipState,
1415
useChatContext,
1516
useTheme,
@@ -22,7 +23,7 @@ import { useChannelInfoOverlayContext } from '../context/ChannelInfoOverlayConte
2223
import type { StackNavigationProp } from '@react-navigation/stack';
2324

2425
import type { StackNavigatorParamList, StreamChatGenerics } from '../types';
25-
import { Pin } from '../icons/Pin';
26+
import { ChannelState } from 'stream-chat';
2627

2728
const styles = StyleSheet.create({
2829
leftSwipeableButton: {
@@ -53,15 +54,17 @@ type ChannelListScreenNavigationProp = StackNavigationProp<
5354
'ChannelListScreen'
5455
>;
5556

56-
const CustomChannelPreviewStatus = (props: ChannelPreviewStatusProps) => {
57-
const membership = useChannelMembershipState(props.channel);
57+
const CustomChannelPreviewStatus = (
58+
props: ChannelPreviewStatusProps & { membership: ChannelState['membership'] },
59+
) => {
60+
const { membership } = props;
5861

5962
return (
6063
<View style={styles.statusContainer}>
6164
<ChannelPreviewStatus {...props} />
6265
{membership.pinned_at && (
6366
<View style={styles.pinIconContainer}>
64-
<Pin height={20} width={20} />
67+
<Pin size={24} />
6568
</View>
6669
)}
6770
</View>
@@ -132,7 +135,13 @@ export const ChannelPreview: React.FC<ChannelPreviewMessengerProps<StreamChatGen
132135
</View>
133136
)}
134137
>
135-
<ChannelPreviewMessenger {...props} PreviewStatus={CustomChannelPreviewStatus} />
138+
<ChannelPreviewMessenger
139+
{...props}
140+
// eslint-disable-next-line react/no-unstable-nested-components
141+
PreviewStatus={(statusProps) => (
142+
<CustomChannelPreviewStatus {...statusProps} membership={membership} />
143+
)}
144+
/>
136145
</Swipeable>
137146
);
138147
};

examples/SampleApp/src/screens/ChannelListScreen.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,10 @@ const styles = StyleSheet.create({
4949
});
5050

5151
const baseFilters = {
52+
archived: false,
5253
type: 'messaging',
5354
};
55+
5456
const sort: ChannelSort<StreamChatGenerics> = [
5557
{ pinned_at: -1 },
5658
{ last_message_at: -1 },
@@ -62,6 +64,7 @@ const options = {
6264
state: true,
6365
watch: true,
6466
};
67+
6568
export const ChannelListScreen: React.FC = () => {
6669
const { chatClient } = useAppContext();
6770
const navigation = useNavigation();

examples/TypeScriptMessaging/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ const App = () => {
302302
i18nInstance={streami18n}
303303
value={{ style: theme }}
304304
>
305-
<Chat client={chatClient} i18nInstance={streami18n}>
305+
<Chat client={chatClient} i18nInstance={streami18n} enableOfflineSupport>
306306
<Stack.Navigator
307307
initialRouteName='ChannelList'
308308
screenOptions={{

package/src/components/ChannelList/hooks/listeners/useAddedToChannelNotification.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ export const useAddedToChannelNotification = <
5454
let lastPinnedChannelIndex: number | null = null;
5555

5656
const newChannels = [...channels];
57-
5857
if (pinnedAtSort === 1 || pinnedAtSort === -1) {
5958
lastPinnedChannelIndex = findLastPinnedChannelIndex({ channels: newChannels });
6059
const newTargetChannelIndex =

package/src/components/ChannelList/hooks/listeners/useChannelMemberUpdated.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import type { DefaultStreamChatGenerics } from '../../../../types/types';
88
import {
99
findLastPinnedChannelIndex,
1010
findPinnedAtSortOrder,
11+
isChannelArchived,
12+
isChannelPinned,
13+
shouldConsiderArchivedChannels,
1114
shouldConsiderPinnedChannels,
1215
} from '../utils';
1316

@@ -45,11 +48,11 @@ export const useChannelMemberUpdated = <
4548
if (!event.member?.user || event.member.user.id !== client.userID || !event.channel_type) {
4649
return;
4750
}
48-
const member = event.member;
4951
const channelType = event.channel_type;
5052
const channelId = event.channel_id;
5153

5254
const considerPinnedChannels = shouldConsiderPinnedChannels(sort);
55+
const considerArchivedChannels = shouldConsiderArchivedChannels(filters);
5356
const pinnedAtSort = findPinnedAtSortOrder({ sort });
5457

5558
setChannels((currentChannels) => {
@@ -60,6 +63,9 @@ export const useChannelMemberUpdated = <
6063
const targetChannelIndex = currentChannels.indexOf(targetChannel);
6164
const targetChannelExistsWithinList = targetChannelIndex >= 0;
6265

66+
const isTargetChannelPinned = isChannelPinned(targetChannel);
67+
const isTargetChannelArchived = isChannelArchived(targetChannel);
68+
6369
if (!considerPinnedChannels || lockChannelOrder) {
6470
return currentChannels;
6571
}
@@ -72,16 +78,18 @@ export const useChannelMemberUpdated = <
7278

7379
// handle archiving (remove channel)
7480
if (
75-
typeof member.archived_at === 'string' ||
76-
(filters && filters.archived === true && member.archived_at === null)
81+
// When archived filter true, and channel is not archived
82+
(!considerArchivedChannels && !isTargetChannelArchived) ||
83+
// When archived filter false, and channel is archived
84+
(considerArchivedChannels && isTargetChannelArchived)
7785
) {
7886
return newChannels;
7987
}
8088

8189
// handle pinning
8290
let lastPinnedChannelIndex: number | null = null;
8391

84-
if (pinnedAtSort === 1 || (pinnedAtSort === -1 && !member.pinned_at)) {
92+
if (pinnedAtSort === 1 || (pinnedAtSort === -1 && !isTargetChannelPinned)) {
8593
lastPinnedChannelIndex = findLastPinnedChannelIndex({ channels: newChannels });
8694
}
8795
const newTargetChannelIndex =

package/src/components/ChannelList/hooks/listeners/useNewMessage.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@ import { useChatContext } from '../../../../contexts/chatContext/ChatContext';
66

77
import type { DefaultStreamChatGenerics } from '../../../../types/types';
88
import { moveChannelUp } from '../../utils';
9-
import { isChannelArchived, isChannelPinned, shouldConsiderPinnedChannels } from '../utils';
9+
import {
10+
isChannelArchived,
11+
isChannelPinned,
12+
shouldConsiderArchivedChannels,
13+
shouldConsiderPinnedChannels,
14+
} from '../utils';
1015

1116
type Parameters<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> =
1217
{
@@ -39,21 +44,19 @@ export const useNewMessage = <
3944
if (typeof onNewMessage === 'function') {
4045
onNewMessage(lockChannelOrder, setChannels, event, filters, sort);
4146
} else {
47+
const considerPinnedChannels = shouldConsiderPinnedChannels(sort);
48+
const considerArchivedChannels = shouldConsiderArchivedChannels(filters);
4249
setChannels((channels) => {
4350
if (!channels) return channels;
4451
const targetChannelIndex = channels.findIndex((channel) => channel.cid === event.cid);
4552
const targetChannel = channels[targetChannelIndex];
4653

4754
const isTargetChannelArchived = isChannelArchived(targetChannel);
4855
const isTargetChannelPinned = isChannelPinned(targetChannel);
49-
const isArchivedFilterTrue = filters && filters.archived === true;
50-
const isArchivedFilterFalse = filters && filters.archived === false;
51-
52-
const considerPinnedChannels = shouldConsiderPinnedChannels(sort);
5356

5457
if (
5558
// If the channel is archived and we are not considering archived channels
56-
(isTargetChannelArchived && isArchivedFilterFalse) ||
59+
(isTargetChannelArchived && considerArchivedChannels) ||
5760
// If the channel is pinned and we are not considering pinned channels
5861
(isTargetChannelPinned && considerPinnedChannels) ||
5962
lockChannelOrder
@@ -73,9 +76,9 @@ export const useNewMessage = <
7376
// While adding new channels, we need to consider whether they are archived or not.
7477
if (
7578
// When archived filter false, and channel is archived
76-
(isChannelArchived(channelToMove) && isArchivedFilterFalse) ||
79+
(considerArchivedChannels && isChannelArchived(channelToMove)) ||
7780
// When archived filter true, and channel is not archived
78-
(isArchivedFilterTrue && !isChannelArchived(channelToMove))
81+
(!considerArchivedChannels && !isChannelArchived(channelToMove))
7982
) {
8083
return [...channels];
8184
}

package/src/components/ChannelList/hooks/useChannelMembershipState.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,25 @@ export const useChannelMembershipState = <
1616

1717
const { client } = useChatContext<StreamChatGenerics>();
1818

19-
useEffect(() => {
20-
if (!channel) return;
19+
useEffect(
20+
() => {
21+
if (!channel) return;
2122

22-
const handleMembershipUpdate = () => {
2323
setMembership(channel.state.membership);
24-
};
2524

26-
const subscriptions = ['member.updated'].map((event) =>
27-
client.on(event, handleMembershipUpdate),
28-
);
25+
const handleMembershipUpdate = () => {
26+
setMembership(channel.state.membership);
27+
};
2928

30-
return () => subscriptions.forEach((subscription) => subscription.unsubscribe());
31-
}, [channel?.state.membership, client]);
29+
const subscriptions = ['member.updated'].map((event) =>
30+
client.on(event, handleMembershipUpdate),
31+
);
32+
33+
return () => subscriptions.forEach((subscription) => subscription.unsubscribe());
34+
},
35+
// eslint-disable-next-line react-hooks/exhaustive-deps
36+
[channel?.state.membership, client],
37+
);
3238

3339
return membership;
3440
};

0 commit comments

Comments
 (0)