Skip to content

Commit b0bbdd4

Browse files
committed
fix: add drafts to thread list item
1 parent 81a9a5a commit b0bbdd4

File tree

5 files changed

+80
-16
lines changed

5 files changed

+80
-16
lines changed

package/src/components/MessageInput/hooks/useAudioRecorder.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ import { resampleWaveformData } from '../utils/audioSampling';
1212

1313
/**
1414
* The hook that controls all the async audio core features including start/stop or recording, player, upload/delete of the recorded audio.
15-
*
16-
* FIXME: Change the name to `useAudioRecorder` in the next major version as the hook will only be used for audio recording.
1715
*/
1816
export const useAudioRecorder = ({
1917
audioRecorderManager,

package/src/components/ThreadList/ThreadListItem.tsx

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
import React, { useCallback, useEffect, useMemo } from 'react';
22
import { Pressable, StyleSheet, Text, View } from 'react-native';
33

4-
import { LocalMessage, Thread, ThreadState } from 'stream-chat';
4+
import {
5+
AttachmentManagerState,
6+
DraftMessage,
7+
LocalMessage,
8+
TextComposerState,
9+
Thread,
10+
ThreadState,
11+
} from 'stream-chat';
512

613
import { ThreadListItemMessagePreview as ThreadListItemMessagePreviewDefault } from './ThreadListItemMessagePreview';
714

@@ -34,11 +41,20 @@ export const attachmentTypeIconMap = {
3441
voiceRecording: '🎙️',
3542
} as const;
3643

44+
const textComposerStateSelector = (state: TextComposerState) => ({
45+
text: state.text,
46+
});
47+
48+
const attachmentManagerStateSelector = (state: AttachmentManagerState) => ({
49+
attachments: state.attachments,
50+
});
51+
3752
export const ThreadListItemComponent = () => {
3853
const {
3954
channel,
4055
dateString,
4156
deletedAtDateString,
57+
draftMessage,
4258
lastReply,
4359
ownUnreadMessageCount,
4460
parentMessage,
@@ -57,10 +73,7 @@ export const ThreadListItemComponent = () => {
5773
const styles = useStyles();
5874
const { t } = useTranslationContext();
5975

60-
useEffect(() => {
61-
const unsubscribe = thread.messageComposer.registerDraftEventSubscriptions();
62-
return () => unsubscribe();
63-
}, [thread.messageComposer]);
76+
const shouldRenderPreview = !!draftMessage || !!lastReply;
6477

6578
return (
6679
<View style={styles.wrapper}>
@@ -87,12 +100,14 @@ export const ThreadListItemComponent = () => {
87100
<Text numberOfLines={1} style={styles.channelName}>
88101
{displayName || 'N/A'}
89102
</Text>
90-
{lastReply ? (
103+
{shouldRenderPreview ? (
91104
<View style={styles.previewMessageContainer}>
92-
<ThreadMessagePreviewDeliveryStatus
93-
channel={channel}
94-
message={parentMessage as LocalMessage}
95-
/>
105+
{!draftMessage ? (
106+
<ThreadMessagePreviewDeliveryStatus
107+
channel={channel}
108+
message={parentMessage as LocalMessage}
109+
/>
110+
) : null}
96111
<ThreadListItemMessagePreview message={parentMessage as LocalMessage} />
97112
</View>
98113
) : null}
@@ -129,6 +144,14 @@ export const ThreadListItem = (props: ThreadListItemProps) => {
129144
const { t, tDateTimeParser } = useTranslationContext();
130145
const { thread, timestampTranslationKey = 'timestamp/ThreadListItem' } = props;
131146
const { ThreadListItem = ThreadListItemComponent } = useThreadsContext();
147+
const { text: draftText } = useStateStore(
148+
thread.messageComposer.textComposer.state,
149+
textComposerStateSelector,
150+
);
151+
const { attachments } = useStateStore(
152+
thread.messageComposer.attachmentManager.state,
153+
attachmentManagerStateSelector,
154+
);
132155

133156
const selector = useCallback(
134157
(nextValue: ThreadState) =>
@@ -150,6 +173,27 @@ export const ThreadListItem = (props: ThreadListItemProps) => {
150173

151174
const timestamp = lastReply?.created_at;
152175

176+
useEffect(() => {
177+
const unsubscribe = thread.messageComposer.registerDraftEventSubscriptions();
178+
return () => unsubscribe();
179+
}, [thread.messageComposer]);
180+
181+
const draftMessage = useMemo<DraftMessage | undefined>(() => {
182+
if (thread.messageComposer.compositionIsEmpty) {
183+
return undefined;
184+
}
185+
186+
if (!draftText && !attachments?.length) {
187+
return undefined;
188+
}
189+
190+
return {
191+
attachments,
192+
id: thread.messageComposer.id,
193+
text: draftText ?? '',
194+
};
195+
}, [attachments, draftText, thread.messageComposer]);
196+
153197
// TODO: Please rethink this, we have the same line of code in about 5 places in the SDK.
154198
const dateString = useMemo(
155199
() =>
@@ -178,6 +222,7 @@ export const ThreadListItem = (props: ThreadListItemProps) => {
178222
channel,
179223
dateString,
180224
deletedAtDateString,
225+
draftMessage,
181226
lastReply,
182227
ownUnreadMessageCount,
183228
parentMessage,
@@ -224,6 +269,7 @@ const useStyles = () => {
224269
},
225270
previewMessageContainer: {
226271
flexDirection: 'row',
272+
alignItems: 'center',
227273
gap: primitives.spacingXxs,
228274
...threadListItem.previewMessageContainer,
229275
},

package/src/components/ThreadList/ThreadListItemMessagePreview.tsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { View, Text, StyleSheet } from 'react-native';
44
import { DraftMessage, LocalMessage, MessageResponse } from 'stream-chat';
55

66
import { useTheme } from '../../contexts/themeContext/ThemeContext';
7+
import { useThreadListItemContext } from '../../contexts/threadsContext/ThreadListItemContext';
8+
import { useTranslationContext } from '../../contexts/translationContext/TranslationContext';
79
import { useMessagePreviewIcon, useMessagePreviewText } from '../../hooks';
810
import { primitives } from '../../theme';
911

@@ -12,15 +14,20 @@ export type ThreadListItemMessagePreviewProps = {
1214
};
1315

1416
export const ThreadListItemMessagePreview = ({ message }: ThreadListItemMessagePreviewProps) => {
17+
const { draftMessage } = useThreadListItemContext();
1518
const {
1619
theme: { semantics },
1720
} = useTheme();
1821
const styles = useStyles();
19-
const MessagePreviewIcon = useMessagePreviewIcon({ message });
20-
const messagePreviewTitle = useMessagePreviewText({ message });
22+
const { t } = useTranslationContext();
23+
const previewMessage = draftMessage ?? message;
24+
const MessagePreviewIcon = useMessagePreviewIcon({ message: previewMessage });
25+
const messagePreviewTitle = useMessagePreviewText({ message: previewMessage });
26+
const isDraft = !!draftMessage;
2127

2228
return (
2329
<View style={[styles.container]}>
30+
{isDraft ? <Text style={styles.draftText}>{t('Draft')}:</Text> : null}
2431
{MessagePreviewIcon ? (
2532
<MessagePreviewIcon height={20} stroke={semantics.textPrimary} width={20} />
2633
) : null}
@@ -56,6 +63,14 @@ const useStyles = () => {
5663
flexShrink: 1,
5764
...messagePreview.subtitle,
5865
},
66+
draftText: {
67+
color: semantics.accentPrimary,
68+
fontSize: primitives.typographyFontSizeMd,
69+
fontWeight: primitives.typographyFontWeightSemiBold,
70+
includeFontPadding: false,
71+
lineHeight: primitives.typographyLineHeightNormal,
72+
...messagePreview.draftText,
73+
},
5974
});
60-
}, [messagePreview.container, messagePreview.subtitle, semantics.textPrimary]);
75+
}, [messagePreview.container, messagePreview.draftText, messagePreview.subtitle, semantics]);
6176
};

package/src/contexts/themeContext/utils/theme.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ export type Theme = {
238238
};
239239
messagePreview: {
240240
container: ViewStyle;
241+
draftText: TextStyle;
241242
subtitle: TextStyle;
242243
};
243244
};
@@ -989,6 +990,7 @@ export type Theme = {
989990
unreadBubbleWrapper: ViewStyle;
990991
messagePreview: {
991992
container: ViewStyle;
993+
draftText: TextStyle;
992994
subtitle: TextStyle;
993995
};
994996
messagePreviewDeliveryStatus: {
@@ -1167,6 +1169,7 @@ export const defaultTheme: Theme = {
11671169
wrapper: {},
11681170
messagePreview: {
11691171
container: {},
1172+
draftText: {},
11701173
subtitle: {},
11711174
},
11721175
},
@@ -1893,6 +1896,7 @@ export const defaultTheme: Theme = {
18931896
messageRepliesText: {},
18941897
messagePreview: {
18951898
container: {},
1899+
draftText: {},
18961900
subtitle: {},
18971901
},
18981902
messagePreviewDeliveryStatus: {

package/src/contexts/threadsContext/ThreadListItemContext.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import React, { PropsWithChildren, useContext } from 'react';
22

3-
import { Channel, LocalMessage, Thread } from 'stream-chat';
3+
import { Channel, DraftMessage, LocalMessage, Thread } from 'stream-chat';
44

55
import { DEFAULT_BASE_CONTEXT_VALUE } from '../utils/defaultBaseContextValue';
66

77
export type ThreadListItemContextValue = {
88
channel: Channel;
99
dateString: string | number | undefined;
1010
deletedAtDateString: string | number | undefined;
11+
draftMessage?: DraftMessage;
1112
lastReply: LocalMessage | undefined;
1213
ownUnreadMessageCount: number;
1314
parentMessage: LocalMessage | undefined;

0 commit comments

Comments
 (0)