Skip to content

Commit

Permalink
Merge branch 'develop' into v6.0.0
Browse files Browse the repository at this point in the history
# Conflicts:
#	package/native-package/package.json
#	package/src/components/Message/MessageSimple/MessageContent.tsx
#	package/src/components/MessageOverlay/MessageOverlay.tsx
#	package/src/components/Reply/Reply.tsx
#	package/src/components/Thread/__tests__/__snapshots__/Thread.test.js.snap
#	package/src/store/QuickSqliteClient.ts
#	package/src/store/apis/getChannelMessages.ts
#	package/src/store/apis/updatePollMessage.ts
#	package/yarn.lock
  • Loading branch information
isekovanic committed Nov 14, 2024
2 parents 4811497 + 834d124 commit 5c03e20
Show file tree
Hide file tree
Showing 17 changed files with 475 additions and 152 deletions.
2 changes: 1 addition & 1 deletion package/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
"path": "0.12.7",
"react-native-markdown-package": "1.8.2",
"react-native-url-polyfill": "^1.3.0",
"stream-chat": "8.42.0"
"stream-chat": "8.44.0"
},
"peerDependencies": {
"@op-engineering/op-sqlite": ">=9.3.0",
Expand Down
27 changes: 17 additions & 10 deletions package/src/components/Channel/Channel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1203,6 +1203,7 @@ const ChannelWithContext = <
await channel.query({}, 'latest');
}
await channel.state.loadMessageIntoState('latest');
setMessages([...channel.state.messages]);
});

const loadChannel = () =>
Expand Down Expand Up @@ -1399,13 +1400,17 @@ const ChannelWithContext = <
}, [enableOfflineSupport, shouldSyncChannel]);

const reloadChannel = () =>
channelQueryCallRef.current(async () => {
setLoading(true);
await loadLatestMessagesRef.current(true);
setLoading(false);
channel?.state.setIsUpToDate(true);
setHasNoMoreRecentMessagesToLoad(true);
});
channelQueryCallRef.current(
async () => {
setLoading(true);
await loadLatestMessagesRef.current(true);
setLoading(false);
},
() => {
channel?.state.setIsUpToDate(true);
setHasNoMoreRecentMessagesToLoad(true);
},
);

// In case the channel is disconnected which may happen when channel is deleted,
// underlying js client throws an error. Following function ensures that Channel component
Expand Down Expand Up @@ -1795,10 +1800,12 @@ const ChannelWithContext = <
const latestLengthBeforeMerge = latestMessageSet?.messages.length || 0;
const didMerge = mergeOverlappingMessageSetsRef.current(true);
if (didMerge) {
if (latestMessageSet && latestLengthBeforeMerge >= limit) {
if (latestMessageSet && latestLengthBeforeMerge > 0) {
const shouldSetStateUpToDate =
latestMessageSet.messages.length < limit && latestMessageSet.isCurrent;
setLoadingMoreRecent(true);
channel.state.setIsUpToDate(true);
setHasNoMoreRecentMessagesToLoad(true);
channel.state.setIsUpToDate(shouldSetStateUpToDate);
setHasNoMoreRecentMessagesToLoad(shouldSetStateUpToDate);
loadMoreRecentFinished(channel.state.messages);
restartSetsMergeFuncRef.current();
return;
Expand Down
127 changes: 24 additions & 103 deletions package/src/components/ChannelPreview/ChannelPreview.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, { useEffect, useState } from 'react';
import React from 'react';

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

import { useChannelPreviewData } from './hooks/useChannelPreviewData';
import { useLatestMessagePreview } from './hooks/useLatestMessagePreview';

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

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

export type ChannelPreviewPropsWithContext<
export type ChannelPreviewProps<
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
> = Pick<ChatContextValue<StreamChatGenerics>, 'client'> &
Pick<ChannelsContextValue<StreamChatGenerics>, 'Preview' | 'forceUpdate'> & {
> = Partial<Pick<ChatContextValue<StreamChatGenerics>, 'client'>> &
Partial<Pick<ChannelsContextValue<StreamChatGenerics>, 'Preview' | 'forceUpdate'>> & {
/**
* Instance of Channel from stream-chat package.
*/
channel: Channel<StreamChatGenerics>;
};

/**
* This component manages state for the ChannelPreviewMessenger UI component and receives
* all props from the ChannelListMessenger component.
*/
const ChannelPreviewWithContext = <
export const ChannelPreview = <
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
>(
props: ChannelPreviewPropsWithContext<StreamChatGenerics>,
props: ChannelPreviewProps<StreamChatGenerics>,
) => {
const { channel, client, forceUpdate: channelListForceUpdate, Preview } = props;
const { channel, client: propClient, forceUpdate: propForceUpdate, Preview: propPreview } = props;

const [lastMessage, setLastMessage] = useState<
| ReturnType<ChannelState<StreamChatGenerics>['formatMessage']>
| MessageResponse<StreamChatGenerics>
| undefined
>(channel.state.messages[channel.state.messages.length - 1]);
const { client: contextClient } = useChatContext<StreamChatGenerics>();
const { forceUpdate: contextForceUpdate, Preview: contextPreview } =
useChannelsContext<StreamChatGenerics>();

const [forceUpdate, setForceUpdate] = useState(0);
const [unread, setUnread] = useState(channel.countUnread());
const client = propClient || contextClient;
const forceUpdate = propForceUpdate || contextForceUpdate;
const Preview = propPreview || contextPreview;

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

const channelLastMessage = channel.lastMessage();
const channelLastMessageString = `${channelLastMessage?.id}${channelLastMessage?.updated_at}`;

useEffect(() => {
const { unsubscribe } = client.on('notification.mark_read', () => {
setUnread(channel.countUnread());
});
return unsubscribe;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

useEffect(() => {
if (
channelLastMessage &&
(channelLastMessage.id !== lastMessage?.id ||
channelLastMessage.updated_at !== lastMessage?.updated_at)
) {
setLastMessage(channelLastMessage);
}

const newUnreadCount = channel.countUnread();
setUnread(newUnreadCount);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [channelLastMessageString, channelListForceUpdate]);

useEffect(() => {
const handleNewMessageEvent = (event: Event<StreamChatGenerics>) => {
const message = event.message;
if (message && (!message.parent_id || message.show_in_channel)) {
setLastMessage(event.message);
setUnread(channel.countUnread());
}
};

const handleUpdatedOrDeletedMessage = (event: Event<StreamChatGenerics>) => {
setLastMessage((prevLastMessage) => {
if (prevLastMessage?.id === event.message?.id) {
return event.message;
}
return prevLastMessage;
});
};

const listeners = [
channel.on('message.new', handleNewMessageEvent),
channel.on('message.updated', handleUpdatedOrDeletedMessage),
channel.on('message.deleted', handleUpdatedOrDeletedMessage),
];

return () => listeners.forEach((l) => l.unsubscribe());
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

useEffect(() => {
const handleReadEvent = (event: Event<StreamChatGenerics>) => {
if (event.user?.id === client.userID) {
setUnread(0);
} else if (event.user?.id) {
setForceUpdate((prev) => prev + 1);
}
};

const listener = channel.on('message.read', handleReadEvent);
return () => listener.unsubscribe();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return <Preview channel={channel} latestMessagePreview={latestMessagePreview} unread={unread} />;
};

export type ChannelPreviewProps<
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
> = Partial<Omit<ChannelPreviewPropsWithContext<StreamChatGenerics>, 'channel'>> &
Pick<ChannelPreviewPropsWithContext<StreamChatGenerics>, 'channel'>;

export const ChannelPreview = <
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
>(
props: ChannelPreviewProps<StreamChatGenerics>,
) => {
const { client } = useChatContext<StreamChatGenerics>();
const { forceUpdate, Preview } = useChannelsContext<StreamChatGenerics>();

return <ChannelPreviewWithContext {...{ client, forceUpdate, Preview }} {...props} />;
return (
<Preview
channel={channel}
latestMessagePreview={latestMessagePreview}
muted={muted}
unread={unread}
/>
);
};
20 changes: 5 additions & 15 deletions package/src/components/ChannelPreview/ChannelPreviewMessenger.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React from 'react';
import { StyleSheet, View } from 'react-native';
import { TouchableOpacity } from 'react-native-gesture-handler';

Expand All @@ -17,7 +17,6 @@ import {
ChannelsContextValue,
useChannelsContext,
} from '../../contexts/channelsContext/ChannelsContext';
import { useChatContext } from '../../contexts/chatContext/ChatContext';
import { useTheme } from '../../contexts/themeContext/ThemeContext';
import { useViewport } from '../../hooks/useViewport';
import type { DefaultStreamChatGenerics } from '../../types/types';
Expand Down Expand Up @@ -95,6 +94,8 @@ export type ChannelPreviewMessengerPropsWithContext<
* default formatted date. This default logic is part of ChannelPreview component.
*/
formatLatestMessageDate?: (date: Date) => string;
/** If the channel is muted. */
muted?: boolean;
/** Number of unread messages on the channel */
unread?: number;
};
Expand All @@ -109,6 +110,7 @@ const ChannelPreviewMessengerWithContext = <
formatLatestMessageDate,
latestMessagePreview,
maxUnreadCount,
muted,
onSelect,
PreviewAvatar = ChannelAvatar,
PreviewMessage = ChannelPreviewMessage,
Expand All @@ -129,23 +131,11 @@ const ChannelPreviewMessengerWithContext = <
},
} = useTheme();

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

const displayName = useChannelPreviewDisplayName(
channel,
Math.floor(maxWidth / ((title.fontSize || styles.title.fontSize) / 2)),
);

const [isChannelMuted, setIsChannelMuted] = useState(() => channel.muteStatus().muted);

useEffect(() => {
const handleEvent = () => setIsChannelMuted(channel.muteStatus().muted);

client.on('notification.channel_mutes_updated', handleEvent);
return () => client.off('notification.channel_mutes_updated', handleEvent);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [client]);

return (
<TouchableOpacity
onPress={() => {
Expand All @@ -168,7 +158,7 @@ const ChannelPreviewMessengerWithContext = <
<View style={[styles.row, row]}>
<PreviewTitle channel={channel} displayName={displayName} />
<View style={[styles.statusContainer, row]}>
{isChannelMuted && <PreviewMutedStatus />}
{muted && <PreviewMutedStatus />}
<PreviewUnreadCount channel={channel} maxUnreadCount={maxUnreadCount} unread={unread} />
</View>
</View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,11 @@ export const ChannelPreviewMutedStatus = () => {
channelPreview: {
mutedStatus: { height, iconStyle, width },
},
colors: { grey_dark },
colors: { grey },
},
} = useTheme();

return (
<Mute
height={height}
pathFill={grey_dark}
style={[styles.iconStyle, iconStyle]}
width={width}
/>
<Mute height={height} pathFill={grey} style={[styles.iconStyle, iconStyle]} width={width} />
);
};
Loading

0 comments on commit 5c03e20

Please sign in to comment.