Skip to content

Commit

Permalink
Merge pull request #2797 from GetStream/develop
Browse files Browse the repository at this point in the history
Next Release
  • Loading branch information
isekovanic authored Nov 19, 2024
2 parents ddd4581 + 008cf01 commit ddaf5e3
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 34 deletions.
2 changes: 2 additions & 0 deletions package/expo-package/src/optionalDependencies/pickImage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ export const pickImage = ImagePicker
return { cancelled: true };
}
}
return { cancelled: true };
} catch (error) {
console.log('Error while picking image', error);
return { cancelled: true };
}
}
: null;
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const pickImage = ImagePicker
}
} catch (error) {
console.log('Error picking image: ', error);
return { cancelled: true };
}
}
: null;
12 changes: 6 additions & 6 deletions package/src/components/ChannelPreview/ChannelPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import React from 'react';
import type { Channel } from 'stream-chat';

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

import {
ChannelsContextValue,
Expand Down Expand Up @@ -31,15 +30,16 @@ export const ChannelPreview = <
const { channel, client: propClient, forceUpdate: propForceUpdate, Preview: propPreview } = props;

const { client: contextClient } = useChatContext<StreamChatGenerics>();
const { forceUpdate: contextForceUpdate, Preview: contextPreview } =
useChannelsContext<StreamChatGenerics>();
const { Preview: contextPreview } = useChannelsContext<StreamChatGenerics>();

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 { latestMessagePreview, muted, unread } = useChannelPreviewData(
channel,
client,
propForceUpdate,
);

return (
<Preview
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@ describe('ChannelPreview', () => {
);
};

const generateChannelWrapper = (overrides: Record<string, unknown>) =>
generateChannel({
countUnread: jest.fn().mockReturnValue(0),
initialized: true,
lastMessage: jest.fn().mockReturnValue(generateMessage()),
muteStatus: jest.fn().mockReturnValue({ muted: false }),
...overrides,
});

const useInitializeChannel = async (c: GetOrCreateChannelApiParams) => {
useMockedApis(chatClient, [getOrCreateChannelApi(c)]);

Expand All @@ -89,9 +98,8 @@ describe('ChannelPreview', () => {
it("should not update the unread count if the event's cid does not match the channel's cid", async () => {
const channelOnMock = jest.fn().mockReturnValue({ unsubscribe: jest.fn() });

const c = generateChannel({
const c = generateChannelWrapper({
countUnread: jest.fn().mockReturnValue(10),
muteStatus: jest.fn().mockReturnValue({ muted: false }),
on: channelOnMock,
});

Expand All @@ -117,9 +125,8 @@ describe('ChannelPreview', () => {
it('should update the unread count to 0', async () => {
const channelOnMock = jest.fn().mockReturnValue({ unsubscribe: jest.fn() });

const c = generateChannel({
const c = generateChannelWrapper({
countUnread: jest.fn().mockReturnValue(10),
muteStatus: jest.fn().mockReturnValue({ muted: false }),
on: channelOnMock,
});

Expand Down Expand Up @@ -147,9 +154,7 @@ describe('ChannelPreview', () => {
it("should not update the unread count if the event's cid is undefined", async () => {
const channelOnMock = jest.fn().mockReturnValue({ unsubscribe: jest.fn() });

const c = generateChannel({
countUnread: jest.fn().mockReturnValue(0),
muteStatus: jest.fn().mockReturnValue({ muted: false }),
const c = generateChannelWrapper({
on: channelOnMock,
});

Expand Down Expand Up @@ -182,9 +187,7 @@ describe('ChannelPreview', () => {
it("should not update the unread count if the event's cid does not match the channel's cid", async () => {
const channelOnMock = jest.fn().mockReturnValue({ unsubscribe: jest.fn() });

const c = generateChannel({
countUnread: jest.fn().mockReturnValue(0),
muteStatus: jest.fn().mockReturnValue({ muted: false }),
const c = generateChannelWrapper({
on: channelOnMock,
});

Expand Down Expand Up @@ -217,9 +220,7 @@ describe('ChannelPreview', () => {
it("should not update the unread count if the event's user id does not match the client's user id", async () => {
const channelOnMock = jest.fn().mockReturnValue({ unsubscribe: jest.fn() });

const c = generateChannel({
countUnread: jest.fn().mockReturnValue(0),
muteStatus: jest.fn().mockReturnValue({ muted: false }),
const c = generateChannelWrapper({
on: channelOnMock,
});

Expand Down Expand Up @@ -255,12 +256,10 @@ describe('ChannelPreview', () => {
await useInitializeChannel(c);
const channelOnMock = jest.fn().mockReturnValue({ unsubscribe: jest.fn() });

const testChannel = {
const testChannel = generateChannelWrapper({
...channel,
countUnread: jest.fn().mockReturnValue(0),
muteStatus: jest.fn().mockReturnValue({ muted: false }),
on: channelOnMock,
};
});

const { getByTestId } = render(<TestComponent />);

Expand Down Expand Up @@ -291,9 +290,9 @@ describe('ChannelPreview', () => {
it('should update the unread count to 0 if the channel is muted', async () => {
const channelOnMock = jest.fn().mockReturnValue({ unsubscribe: jest.fn() });

const c = generateChannel({
const c = generateChannelWrapper({
countUnread: jest.fn().mockReturnValue(10),
muteStatus: jest.fn().mockReturnValue({ muted: false }),
muteStatus: jest.fn().mockReturnValue({ muted: true }),
on: channelOnMock,
});

Expand All @@ -304,7 +303,7 @@ describe('ChannelPreview', () => {
await waitFor(() => getByTestId('channel-id'));

await waitFor(() => {
expect(getByTestId('unread-count')).toHaveTextContent('10');
expect(getByTestId('unread-count')).toHaveTextContent('0');
});

act(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,65 @@ import type { Channel, ChannelState, Event, MessageResponse, StreamChat } from '

import { useIsChannelMuted } from './useIsChannelMuted';

import { useLatestMessagePreview } from './useLatestMessagePreview';

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

export const useChannelPreviewData = <
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
>(
channel: Channel<StreamChatGenerics>,
client: StreamChat<StreamChatGenerics>,
forceUpdate?: number,
forceUpdateOverride?: number,
) => {
const [forceUpdate, setForceUpdate] = useState(0);
const [lastMessage, setLastMessage] = useState<
| ReturnType<ChannelState<StreamChatGenerics>['formatMessage']>
| MessageResponse<StreamChatGenerics>
>(channel.state.messages[channel.state.messages.length - 1]);
const [unread, setUnread] = useState(channel.countUnread());
const { muted } = useIsChannelMuted(channel);
const { forceUpdate: contextForceUpdate } = useChannelsContext<StreamChatGenerics>();
const channelListForceUpdate = forceUpdateOverride ?? contextForceUpdate;

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

useEffect(() => {
const { unsubscribe } = client.on('notification.mark_read', () => {
setUnread(channel.countUnread());
});
return unsubscribe;
}, [channel, client]);

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
}, [channel, channelLastMessageString, channelListForceUpdate]);

/**
* This effect listens for the `notification.mark_read` event and sets the unread count to 0
*/
useEffect(() => {
const handleReadEvent = (event: Event) => {
if (!event.cid) return;
if (channel.cid === event.cid) setUnread(0);
if (channel.cid !== event.cid) return;
if (event?.user?.id === client.userID) {
setUnread(0);
} else if (event?.user?.id) {
setForceUpdate((prev) => prev + 1);
}
};
const { unsubscribe } = client.on('notification.mark_read', handleReadEvent);
const { unsubscribe } = client.on('message.read', handleReadEvent);
return unsubscribe;
}, [client, channel]);

Expand Down Expand Up @@ -70,16 +104,35 @@ export const useChannelPreviewData = <
refreshUnreadCount();
};

const handleNewMessageEvent = (event: Event<StreamChatGenerics>) => {
const message = event.message;
if (message && (!message.parent_id || message.show_in_channel)) {
setLastMessage(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', handleEvent),
channel.on('message.updated', handleEvent),
channel.on('message.deleted', handleEvent),
channel.on('message.new', handleNewMessageEvent),
channel.on('message.updated', handleUpdatedOrDeletedMessage),
channel.on('message.deleted', handleUpdatedOrDeletedMessage),
channel.on('message.undeleted', handleEvent),
channel.on('channel.truncated', handleEvent),
];

return () => listeners.forEach((l) => l.unsubscribe());
}, [channel, refreshUnreadCount, forceUpdate]);
}, [channel, refreshUnreadCount, forceUpdate, channelListForceUpdate]);

const latestMessagePreview = useLatestMessagePreview(channel, forceUpdate, lastMessage);

return { lastMessage, muted, unread };
return { latestMessagePreview, muted, unread };
};
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,11 @@ export const generateMarkdownText = (text?: string) => {

resultText = resultText.replace(/[<"'>]/g, '\\$&');

// Remove whitespaces that come directly after newlines except in code blocks where we deem this allowed.
resultText = resultText.replace(/(```[\s\S]*?```|`.*?`)|\n[ ]{2,}/g, (_, code) => {
if (code) return code;
return '\n';
});

return resultText;
};

0 comments on commit ddaf5e3

Please sign in to comment.