Skip to content

Commit 834d124

Browse files
authored
feat: listen to notification.mark_read and notification.mark_unread event and introduce improvements (#2776)
* feat: listen to notification.mark_read and notification.mark_unread event and introduce improvements * feat: listen to notification.mark_read and notification.mark_unread event and introduce improvements * fix: broken tests * fix: lint issues * fix: lint issues
1 parent feea8ee commit 834d124

13 files changed

+456
-136
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import React, { useEffect, useState } from 'react';
1+
import React from 'react';
22

3-
import type { Channel, ChannelState, Event, MessageResponse } from 'stream-chat';
3+
import type { Channel } from 'stream-chat';
44

5+
import { useChannelPreviewData } from './hooks/useChannelPreviewData';
56
import { useLatestMessagePreview } from './hooks/useLatestMessagePreview';
67

78
import {
@@ -12,120 +13,40 @@ import { ChatContextValue, useChatContext } from '../../contexts/chatContext/Cha
1213

1314
import type { DefaultStreamChatGenerics } from '../../types/types';
1415

15-
export type ChannelPreviewPropsWithContext<
16+
export type ChannelPreviewProps<
1617
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
17-
> = Pick<ChatContextValue<StreamChatGenerics>, 'client'> &
18-
Pick<ChannelsContextValue<StreamChatGenerics>, 'Preview' | 'forceUpdate'> & {
18+
> = Partial<Pick<ChatContextValue<StreamChatGenerics>, 'client'>> &
19+
Partial<Pick<ChannelsContextValue<StreamChatGenerics>, 'Preview' | 'forceUpdate'>> & {
1920
/**
2021
* Instance of Channel from stream-chat package.
2122
*/
2223
channel: Channel<StreamChatGenerics>;
2324
};
2425

25-
/**
26-
* This component manages state for the ChannelPreviewMessenger UI component and receives
27-
* all props from the ChannelListMessenger component.
28-
*/
29-
const ChannelPreviewWithContext = <
26+
export const ChannelPreview = <
3027
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
3128
>(
32-
props: ChannelPreviewPropsWithContext<StreamChatGenerics>,
29+
props: ChannelPreviewProps<StreamChatGenerics>,
3330
) => {
34-
const { channel, client, forceUpdate: channelListForceUpdate, Preview } = props;
31+
const { channel, client: propClient, forceUpdate: propForceUpdate, Preview: propPreview } = props;
3532

36-
const [lastMessage, setLastMessage] = useState<
37-
| ReturnType<ChannelState<StreamChatGenerics>['formatMessage']>
38-
| MessageResponse<StreamChatGenerics>
39-
| undefined
40-
>(channel.state.messages[channel.state.messages.length - 1]);
33+
const { client: contextClient } = useChatContext<StreamChatGenerics>();
34+
const { forceUpdate: contextForceUpdate, Preview: contextPreview } =
35+
useChannelsContext<StreamChatGenerics>();
4136

42-
const [forceUpdate, setForceUpdate] = useState(0);
43-
const [unread, setUnread] = useState(channel.countUnread());
37+
const client = propClient || contextClient;
38+
const forceUpdate = propForceUpdate || contextForceUpdate;
39+
const Preview = propPreview || contextPreview;
4440

41+
const { lastMessage, muted, unread } = useChannelPreviewData(channel, client, forceUpdate);
4542
const latestMessagePreview = useLatestMessagePreview(channel, forceUpdate, lastMessage);
4643

47-
const channelLastMessage = channel.lastMessage();
48-
const channelLastMessageString = `${channelLastMessage?.id}${channelLastMessage?.updated_at}`;
49-
50-
useEffect(() => {
51-
const { unsubscribe } = client.on('notification.mark_read', () => {
52-
setUnread(channel.countUnread());
53-
});
54-
return unsubscribe;
55-
// eslint-disable-next-line react-hooks/exhaustive-deps
56-
}, []);
57-
58-
useEffect(() => {
59-
if (
60-
channelLastMessage &&
61-
(channelLastMessage.id !== lastMessage?.id ||
62-
channelLastMessage.updated_at !== lastMessage?.updated_at)
63-
) {
64-
setLastMessage(channelLastMessage);
65-
}
66-
67-
const newUnreadCount = channel.countUnread();
68-
setUnread(newUnreadCount);
69-
// eslint-disable-next-line react-hooks/exhaustive-deps
70-
}, [channelLastMessageString, channelListForceUpdate]);
71-
72-
useEffect(() => {
73-
const handleNewMessageEvent = (event: Event<StreamChatGenerics>) => {
74-
const message = event.message;
75-
if (message && (!message.parent_id || message.show_in_channel)) {
76-
setLastMessage(event.message);
77-
setUnread(channel.countUnread());
78-
}
79-
};
80-
81-
const handleUpdatedOrDeletedMessage = (event: Event<StreamChatGenerics>) => {
82-
setLastMessage((prevLastMessage) => {
83-
if (prevLastMessage?.id === event.message?.id) {
84-
return event.message;
85-
}
86-
return prevLastMessage;
87-
});
88-
};
89-
90-
const listeners = [
91-
channel.on('message.new', handleNewMessageEvent),
92-
channel.on('message.updated', handleUpdatedOrDeletedMessage),
93-
channel.on('message.deleted', handleUpdatedOrDeletedMessage),
94-
];
95-
96-
return () => listeners.forEach((l) => l.unsubscribe());
97-
// eslint-disable-next-line react-hooks/exhaustive-deps
98-
}, []);
99-
100-
useEffect(() => {
101-
const handleReadEvent = (event: Event<StreamChatGenerics>) => {
102-
if (event.user?.id === client.userID) {
103-
setUnread(0);
104-
} else if (event.user?.id) {
105-
setForceUpdate((prev) => prev + 1);
106-
}
107-
};
108-
109-
const listener = channel.on('message.read', handleReadEvent);
110-
return () => listener.unsubscribe();
111-
// eslint-disable-next-line react-hooks/exhaustive-deps
112-
}, []);
113-
114-
return <Preview channel={channel} latestMessagePreview={latestMessagePreview} unread={unread} />;
115-
};
116-
117-
export type ChannelPreviewProps<
118-
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
119-
> = Partial<Omit<ChannelPreviewPropsWithContext<StreamChatGenerics>, 'channel'>> &
120-
Pick<ChannelPreviewPropsWithContext<StreamChatGenerics>, 'channel'>;
121-
122-
export const ChannelPreview = <
123-
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
124-
>(
125-
props: ChannelPreviewProps<StreamChatGenerics>,
126-
) => {
127-
const { client } = useChatContext<StreamChatGenerics>();
128-
const { forceUpdate, Preview } = useChannelsContext<StreamChatGenerics>();
129-
130-
return <ChannelPreviewWithContext {...{ client, forceUpdate, Preview }} {...props} />;
44+
return (
45+
<Preview
46+
channel={channel}
47+
latestMessagePreview={latestMessagePreview}
48+
muted={muted}
49+
unread={unread}
50+
/>
51+
);
13152
};

package/src/components/ChannelPreview/ChannelPreviewMessenger.tsx

+5-15
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, useState } from 'react';
1+
import React from 'react';
22
import { StyleSheet, View } from 'react-native';
33
import { TouchableOpacity } from 'react-native-gesture-handler';
44

@@ -17,7 +17,6 @@ import {
1717
ChannelsContextValue,
1818
useChannelsContext,
1919
} from '../../contexts/channelsContext/ChannelsContext';
20-
import { useChatContext } from '../../contexts/chatContext/ChatContext';
2120
import { useTheme } from '../../contexts/themeContext/ThemeContext';
2221
import { useViewport } from '../../hooks/useViewport';
2322
import type { DefaultStreamChatGenerics } from '../../types/types';
@@ -95,6 +94,8 @@ export type ChannelPreviewMessengerPropsWithContext<
9594
* default formatted date. This default logic is part of ChannelPreview component.
9695
*/
9796
formatLatestMessageDate?: (date: Date) => string;
97+
/** If the channel is muted. */
98+
muted?: boolean;
9899
/** Number of unread messages on the channel */
99100
unread?: number;
100101
};
@@ -109,6 +110,7 @@ const ChannelPreviewMessengerWithContext = <
109110
formatLatestMessageDate,
110111
latestMessagePreview,
111112
maxUnreadCount,
113+
muted,
112114
onSelect,
113115
PreviewAvatar = ChannelAvatar,
114116
PreviewMessage = ChannelPreviewMessage,
@@ -129,23 +131,11 @@ const ChannelPreviewMessengerWithContext = <
129131
},
130132
} = useTheme();
131133

132-
const { client } = useChatContext<StreamChatGenerics>();
133-
134134
const displayName = useChannelPreviewDisplayName(
135135
channel,
136136
Math.floor(maxWidth / ((title.fontSize || styles.title.fontSize) / 2)),
137137
);
138138

139-
const [isChannelMuted, setIsChannelMuted] = useState(() => channel.muteStatus().muted);
140-
141-
useEffect(() => {
142-
const handleEvent = () => setIsChannelMuted(channel.muteStatus().muted);
143-
144-
client.on('notification.channel_mutes_updated', handleEvent);
145-
return () => client.off('notification.channel_mutes_updated', handleEvent);
146-
// eslint-disable-next-line react-hooks/exhaustive-deps
147-
}, [client]);
148-
149139
return (
150140
<TouchableOpacity
151141
onPress={() => {
@@ -168,7 +158,7 @@ const ChannelPreviewMessengerWithContext = <
168158
<View style={[styles.row, row]}>
169159
<PreviewTitle channel={channel} displayName={displayName} />
170160
<View style={[styles.statusContainer, row]}>
171-
{isChannelMuted && <PreviewMutedStatus />}
161+
{muted && <PreviewMutedStatus />}
172162
<PreviewUnreadCount channel={channel} maxUnreadCount={maxUnreadCount} unread={unread} />
173163
</View>
174164
</View>

package/src/components/ChannelPreview/ChannelPreviewMutedStatus.tsx

+2-7
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,11 @@ export const ChannelPreviewMutedStatus = () => {
2020
channelPreview: {
2121
mutedStatus: { height, iconStyle, width },
2222
},
23-
colors: { grey_dark },
23+
colors: { grey },
2424
},
2525
} = useTheme();
2626

2727
return (
28-
<Mute
29-
height={height}
30-
pathFill={grey_dark}
31-
style={[styles.iconStyle, iconStyle]}
32-
width={width}
33-
/>
28+
<Mute height={height} pathFill={grey} style={[styles.iconStyle, iconStyle]} width={width} />
3429
);
3530
};

0 commit comments

Comments
 (0)