@droppii-org/chat-mobile 0.2.35 → 0.2.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/module/components/messages/linkMessage/index.js +1 -10
- package/lib/module/components/messages/linkMessage/index.js.map +1 -1
- package/lib/module/components/messages/quotedMessage/index.js +173 -0
- package/lib/module/components/messages/quotedMessage/index.js.map +1 -0
- package/lib/module/context/ChatContext.js +26 -22
- package/lib/module/context/ChatContext.js.map +1 -1
- package/lib/module/context/ChatDetailContext.js +7 -4
- package/lib/module/context/ChatDetailContext.js.map +1 -1
- package/lib/module/core/useChatListener.js +0 -1
- package/lib/module/core/useChatListener.js.map +1 -1
- package/lib/module/core/useUserListener.js +12 -21
- package/lib/module/core/useUserListener.js.map +1 -1
- package/lib/module/hooks/message/useSendMessage.js +26 -4
- package/lib/module/hooks/message/useSendMessage.js.map +1 -1
- package/lib/module/hooks/useChatMessages.js.map +1 -1
- package/lib/module/hooks/useFileAttachment.js +3 -1
- package/lib/module/hooks/useFileAttachment.js.map +1 -1
- package/lib/module/index.js +0 -7
- package/lib/module/index.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatComposer.js +2 -1
- package/lib/module/screens/chat-detail/ChatComposer.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatCurrentQuotedMessage.js +142 -0
- package/lib/module/screens/chat-detail/ChatCurrentQuotedMessage.js.map +1 -0
- package/lib/module/screens/chat-detail/ChatDetail.js +10 -1
- package/lib/module/screens/chat-detail/ChatDetail.js.map +1 -1
- package/lib/module/screens/chat-detail/ChatListLegend.js.map +1 -1
- package/lib/module/screens/chat-detail/PinnedMessageBar.js +8 -3
- package/lib/module/screens/chat-detail/PinnedMessageBar.js.map +1 -1
- package/lib/module/screens/chat-detail/PinnedMessagesSheet.js +173 -0
- package/lib/module/screens/chat-detail/PinnedMessagesSheet.js.map +1 -0
- package/lib/module/screens/chat-detail/legend/LegendChatMessage.js +2 -1
- package/lib/module/screens/chat-detail/legend/LegendChatMessage.js.map +1 -1
- package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxMenu.js +17 -1
- package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxMenu.js.map +1 -1
- package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxOverlay.js +11 -1
- package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxOverlay.js.map +1 -1
- package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxWrapper.js +1 -0
- package/lib/module/screens/chat-detail/messageToolbox/MessageToolboxWrapper.js.map +1 -1
- package/lib/module/screens/chat-detail/messageToolbox/messageToolbox.utils.js +5 -2
- package/lib/module/screens/chat-detail/messageToolbox/messageToolbox.utils.js.map +1 -1
- package/lib/module/screens/inbox/Inbox.js +1 -5
- package/lib/module/screens/inbox/Inbox.js.map +1 -1
- package/lib/module/services/apis.js +0 -3
- package/lib/module/services/apis.js.map +1 -1
- package/lib/module/services/message.js.map +1 -1
- package/lib/module/store/message.js.map +1 -1
- package/lib/module/store/pinnedMessage.js.map +1 -1
- package/lib/module/store/user.js +2 -3
- package/lib/module/store/user.js.map +1 -1
- package/lib/module/translation/resources/i18n.js +9 -2
- package/lib/module/translation/resources/i18n.js.map +1 -1
- package/lib/module/types/auth.js +0 -7
- package/lib/module/types/auth.js.map +1 -1
- package/lib/module/types/chat.js +12 -0
- package/lib/module/types/chat.js.map +1 -1
- package/lib/module/utils/UIUtils.js +6 -0
- package/lib/module/utils/UIUtils.js.map +1 -1
- package/lib/module/utils/legendListMessage.js +1 -0
- package/lib/module/utils/legendListMessage.js.map +1 -1
- package/lib/module/utils/message.js.map +1 -1
- package/lib/module/utils/messageUtils.js +18 -1
- package/lib/module/utils/messageUtils.js.map +1 -1
- package/lib/typescript/src/components/messages/linkMessage/index.d.ts.map +1 -1
- package/lib/typescript/src/components/messages/quotedMessage/index.d.ts +7 -0
- package/lib/typescript/src/components/messages/quotedMessage/index.d.ts.map +1 -0
- package/lib/typescript/src/components/messages/types.d.ts +2 -2
- package/lib/typescript/src/components/messages/types.d.ts.map +1 -1
- package/lib/typescript/src/context/ChatContext.d.ts.map +1 -1
- package/lib/typescript/src/context/ChatDetailContext.d.ts.map +1 -1
- package/lib/typescript/src/core/useChatListener.d.ts.map +1 -1
- package/lib/typescript/src/core/useUserListener.d.ts.map +1 -1
- package/lib/typescript/src/hooks/message/usePinMessage.d.ts +1 -1
- package/lib/typescript/src/hooks/message/useSendMessage.d.ts +2 -1
- package/lib/typescript/src/hooks/message/useSendMessage.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useChatMessages.d.ts +2 -2
- package/lib/typescript/src/hooks/useChatMessages.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useFileAttachment.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatComposer.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/ChatCurrentQuotedMessage.d.ts +2 -0
- package/lib/typescript/src/screens/chat-detail/ChatCurrentQuotedMessage.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/ChatDetail.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/PinnedMessageBar.d.ts +4 -3
- package/lib/typescript/src/screens/chat-detail/PinnedMessageBar.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/PinnedMessagesSheet.d.ts +9 -0
- package/lib/typescript/src/screens/chat-detail/PinnedMessagesSheet.d.ts.map +1 -0
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts +2 -3
- package/lib/typescript/src/screens/chat-detail/legend/LegendChatMessage.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxMenu.d.ts +11 -7
- package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxMenu.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxOverlay.d.ts +4 -4
- package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxOverlay.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/messageToolbox/MessageToolboxWrapper.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/messageToolbox/messageToolbox.utils.d.ts +5 -3
- package/lib/typescript/src/screens/chat-detail/messageToolbox/messageToolbox.utils.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/messageToolbox/types.d.ts +2 -2
- package/lib/typescript/src/screens/chat-detail/messageToolbox/types.d.ts.map +1 -1
- package/lib/typescript/src/screens/chat-detail/types.d.ts +5 -5
- package/lib/typescript/src/screens/chat-detail/types.d.ts.map +1 -1
- package/lib/typescript/src/screens/inbox/Inbox.d.ts.map +1 -1
- package/lib/typescript/src/services/apis.d.ts +0 -1
- package/lib/typescript/src/services/apis.d.ts.map +1 -1
- package/lib/typescript/src/services/message.d.ts +1 -2
- package/lib/typescript/src/services/message.d.ts.map +1 -1
- package/lib/typescript/src/store/message.d.ts.map +1 -1
- package/lib/typescript/src/store/pinnedMessage.d.ts +4 -4
- package/lib/typescript/src/store/pinnedMessage.d.ts.map +1 -1
- package/lib/typescript/src/store/user.d.ts +4 -4
- package/lib/typescript/src/store/user.d.ts.map +1 -1
- package/lib/typescript/src/translation/resources/i18n.d.ts.map +1 -1
- package/lib/typescript/src/types/auth.d.ts +0 -5
- package/lib/typescript/src/types/auth.d.ts.map +1 -1
- package/lib/typescript/src/types/chat.d.ts +19 -36
- package/lib/typescript/src/types/chat.d.ts.map +1 -1
- package/lib/typescript/src/utils/UIUtils.d.ts.map +1 -1
- package/lib/typescript/src/utils/legendListMessage.d.ts +4 -4
- package/lib/typescript/src/utils/legendListMessage.d.ts.map +1 -1
- package/lib/typescript/src/utils/message.d.ts +4 -5
- package/lib/typescript/src/utils/message.d.ts.map +1 -1
- package/lib/typescript/src/utils/messageUtils.d.ts +9 -7
- package/lib/typescript/src/utils/messageUtils.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/messages/linkMessage/index.tsx +1 -11
- package/src/components/messages/quotedMessage/index.tsx +197 -0
- package/src/components/messages/types.ts +2 -2
- package/src/context/ChatContext.tsx +30 -25
- package/src/context/ChatDetailContext.tsx +9 -3
- package/src/core/useChatListener.ts +0 -1
- package/src/core/useUserListener.ts +9 -25
- package/src/hooks/message/useSendMessage.ts +25 -4
- package/src/hooks/useChatMessages.ts +4 -6
- package/src/hooks/useFileAttachment.ts +3 -1
- package/src/index.tsx +5 -6
- package/src/screens/chat-detail/ChatComposer.tsx +2 -0
- package/src/screens/chat-detail/ChatCurrentQuotedMessage.tsx +150 -0
- package/src/screens/chat-detail/ChatDetail.tsx +13 -2
- package/src/screens/chat-detail/ChatListLegend.tsx +3 -3
- package/src/screens/chat-detail/PinnedMessageBar.tsx +53 -49
- package/src/screens/chat-detail/PinnedMessagesSheet.tsx +243 -0
- package/src/screens/chat-detail/legend/LegendChatMessage.tsx +4 -4
- package/src/screens/chat-detail/messageToolbox/MessageToolboxMenu.tsx +30 -6
- package/src/screens/chat-detail/messageToolbox/MessageToolboxOverlay.tsx +15 -6
- package/src/screens/chat-detail/messageToolbox/MessageToolboxWrapper.tsx +1 -0
- package/src/screens/chat-detail/messageToolbox/messageToolbox.utils.ts +21 -12
- package/src/screens/chat-detail/messageToolbox/types.ts +2 -2
- package/src/screens/chat-detail/types.ts +5 -5
- package/src/screens/inbox/Inbox.tsx +1 -5
- package/src/services/apis.ts +0 -4
- package/src/services/message.ts +1 -2
- package/src/store/message.ts +3 -3
- package/src/store/pinnedMessage.ts +4 -4
- package/src/store/user.ts +5 -5
- package/src/translation/resources/i18n.ts +10 -1
- package/src/types/auth.ts +0 -6
- package/src/types/chat.ts +19 -40
- package/src/utils/UIUtils.ts +5 -0
- package/src/utils/legendListMessage.ts +4 -5
- package/src/utils/message.ts +10 -11
- package/src/utils/messageUtils.ts +37 -9
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import {
|
|
2
|
+
KColors,
|
|
3
|
+
KContainer,
|
|
4
|
+
KImage,
|
|
5
|
+
KLabel,
|
|
6
|
+
KRadiusValue,
|
|
7
|
+
KSpacingValue,
|
|
8
|
+
} from '@droppii/libs';
|
|
9
|
+
import { memo, useMemo } from 'react';
|
|
10
|
+
import { StyleSheet } from 'react-native';
|
|
11
|
+
import { useChatDetailContext } from '../../context';
|
|
12
|
+
import {
|
|
13
|
+
getMessagePreview,
|
|
14
|
+
getReplySenderName,
|
|
15
|
+
parseUrlMetadata,
|
|
16
|
+
} from '../../utils/messageUtils';
|
|
17
|
+
import { trans } from '../../translation';
|
|
18
|
+
import { useUserStore } from '../../store';
|
|
19
|
+
import { MessageType } from '@droppii/openim-rn-client-sdk';
|
|
20
|
+
import { getFileIcon, getMimeType } from '../../utils/imageUtils';
|
|
21
|
+
|
|
22
|
+
export const ChatCurrentQuotedMessage = memo(() => {
|
|
23
|
+
const { quotedMessage, conversation, setQuotedMessage } =
|
|
24
|
+
useChatDetailContext();
|
|
25
|
+
|
|
26
|
+
const currentUserId = useUserStore((s) => s.user?.userID);
|
|
27
|
+
|
|
28
|
+
const imageUrl = useMemo(() => {
|
|
29
|
+
switch (quotedMessage?.contentType) {
|
|
30
|
+
case MessageType.PictureMessage:
|
|
31
|
+
return quotedMessage?.pictureElem?.snapshotPicture?.url || '';
|
|
32
|
+
case MessageType.VideoMessage:
|
|
33
|
+
return quotedMessage?.videoElem?.snapshotUrl || '';
|
|
34
|
+
case MessageType.UrlTextMessage:
|
|
35
|
+
const metadata = parseUrlMetadata(quotedMessage?.ex);
|
|
36
|
+
return metadata?.image || '';
|
|
37
|
+
default:
|
|
38
|
+
return '';
|
|
39
|
+
}
|
|
40
|
+
}, [quotedMessage]);
|
|
41
|
+
|
|
42
|
+
const iconSource = useMemo(() => {
|
|
43
|
+
if (quotedMessage?.contentType !== MessageType.FileMessage) return '';
|
|
44
|
+
const mimeType = getMimeType(quotedMessage?.fileElem?.fileName || '');
|
|
45
|
+
return getFileIcon(mimeType);
|
|
46
|
+
}, [quotedMessage]);
|
|
47
|
+
|
|
48
|
+
if (!quotedMessage) return null;
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<KContainer.View style={styles.container}>
|
|
52
|
+
<KContainer.View style={styles.dot} />
|
|
53
|
+
<KContainer.VisibleView visible={!!imageUrl}>
|
|
54
|
+
<KContainer.View style={styles.imageContainer}>
|
|
55
|
+
<KImage.Base uri={imageUrl} style={styles.imageContainer} />
|
|
56
|
+
<KContainer.VisibleView
|
|
57
|
+
visible={quotedMessage?.contentType === MessageType.VideoMessage}
|
|
58
|
+
>
|
|
59
|
+
<KContainer.View style={styles.playIconContainer}>
|
|
60
|
+
<KContainer.View style={styles.playIcon}>
|
|
61
|
+
<KImage.VectorIcons
|
|
62
|
+
name="play-b"
|
|
63
|
+
size={12}
|
|
64
|
+
color={KColors.white}
|
|
65
|
+
/>
|
|
66
|
+
</KContainer.View>
|
|
67
|
+
</KContainer.View>
|
|
68
|
+
</KContainer.VisibleView>
|
|
69
|
+
</KContainer.View>
|
|
70
|
+
</KContainer.VisibleView>
|
|
71
|
+
<KContainer.VisibleView visible={!!iconSource}>
|
|
72
|
+
<KImage.Base
|
|
73
|
+
uri={iconSource}
|
|
74
|
+
style={styles.icon}
|
|
75
|
+
resizeMode="contain"
|
|
76
|
+
/>
|
|
77
|
+
</KContainer.VisibleView>
|
|
78
|
+
<KContainer.View style={styles.content}>
|
|
79
|
+
<KLabel.Text typo="TextNmMedium" numberOfLines={1}>
|
|
80
|
+
{trans('chat:reply_to', {
|
|
81
|
+
name: getReplySenderName(
|
|
82
|
+
quotedMessage,
|
|
83
|
+
currentUserId,
|
|
84
|
+
conversation
|
|
85
|
+
),
|
|
86
|
+
})}
|
|
87
|
+
</KLabel.Text>
|
|
88
|
+
<KLabel.Text
|
|
89
|
+
typo="TextSmNormal"
|
|
90
|
+
color={KColors.gray.light}
|
|
91
|
+
numberOfLines={1}
|
|
92
|
+
>
|
|
93
|
+
{getMessagePreview(quotedMessage)}
|
|
94
|
+
</KLabel.Text>
|
|
95
|
+
</KContainer.View>
|
|
96
|
+
<KContainer.Touchable onPress={() => setQuotedMessage(undefined)}>
|
|
97
|
+
<KImage.VectorIcons name="close-o" size={16} />
|
|
98
|
+
</KContainer.Touchable>
|
|
99
|
+
</KContainer.View>
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
ChatCurrentQuotedMessage.displayName = 'ChatCurrentQuotedMessage';
|
|
104
|
+
|
|
105
|
+
const styles = StyleSheet.create({
|
|
106
|
+
container: {
|
|
107
|
+
paddingVertical: KSpacingValue['0.25rem'],
|
|
108
|
+
paddingHorizontal: KSpacingValue['0.5rem'],
|
|
109
|
+
backgroundColor: KColors.opacity.grayLight[10],
|
|
110
|
+
flexDirection: 'row',
|
|
111
|
+
alignItems: 'center',
|
|
112
|
+
gap: KSpacingValue['0.5rem'],
|
|
113
|
+
justifyContent: 'space-between',
|
|
114
|
+
},
|
|
115
|
+
dot: {
|
|
116
|
+
width: 3,
|
|
117
|
+
height: '100%',
|
|
118
|
+
backgroundColor: KColors.palette.grayCool.w200,
|
|
119
|
+
borderRadius: KRadiusValue.x,
|
|
120
|
+
},
|
|
121
|
+
content: {
|
|
122
|
+
flex: 1,
|
|
123
|
+
paddingHorizontal: KSpacingValue['0.25rem'],
|
|
124
|
+
},
|
|
125
|
+
icon: {
|
|
126
|
+
width: 24,
|
|
127
|
+
height: 24,
|
|
128
|
+
},
|
|
129
|
+
imageContainer: {
|
|
130
|
+
width: 42,
|
|
131
|
+
height: 42,
|
|
132
|
+
borderRadius: KRadiusValue['2x'],
|
|
133
|
+
},
|
|
134
|
+
playIconContainer: {
|
|
135
|
+
position: 'absolute',
|
|
136
|
+
top: 0,
|
|
137
|
+
left: 0,
|
|
138
|
+
right: 0,
|
|
139
|
+
bottom: 0,
|
|
140
|
+
justifyContent: 'center',
|
|
141
|
+
alignItems: 'center',
|
|
142
|
+
},
|
|
143
|
+
playIcon: {
|
|
144
|
+
borderRadius: 8,
|
|
145
|
+
padding: KSpacingValue['0.25rem'],
|
|
146
|
+
backgroundColor: KColors.hexToRgba(KColors.black, 0.6),
|
|
147
|
+
justifyContent: 'center',
|
|
148
|
+
alignItems: 'center',
|
|
149
|
+
},
|
|
150
|
+
});
|
|
@@ -8,6 +8,7 @@ import { useNavigation } from '@react-navigation/native';
|
|
|
8
8
|
import { useChatMessages } from '../../hooks/useChatMessages';
|
|
9
9
|
import { usePinMessage } from '../../hooks/message/usePinMessage';
|
|
10
10
|
import { PinnedMessageBar } from './PinnedMessageBar';
|
|
11
|
+
import { openPinnedMessagesSheet } from './PinnedMessagesSheet';
|
|
11
12
|
import {
|
|
12
13
|
MediaViewerModal,
|
|
13
14
|
type MediaViewerModalHandle,
|
|
@@ -21,12 +22,13 @@ import type { ChatDetailProps } from './types';
|
|
|
21
22
|
import { normalizeUrl } from '../../utils/url';
|
|
22
23
|
import { MessageToolboxOverlay } from './messageToolbox/MessageToolboxOverlay';
|
|
23
24
|
import type { MeasuredRect, MessageToolboxRef } from './messageToolbox/types';
|
|
24
|
-
import { DBotStatusValue
|
|
25
|
+
import { DBotStatusValue } from '../../types/chat';
|
|
25
26
|
import {
|
|
26
27
|
useGetConversationDetail,
|
|
27
28
|
useGetStatusConversationBot,
|
|
28
29
|
} from '../../hooks/conversation/useGetConversationDetail';
|
|
29
30
|
import { PeerType } from '@droppii/openim-rn-client-sdk';
|
|
31
|
+
import type { MessageItem } from '@droppii/openim-rn-client-sdk';
|
|
30
32
|
|
|
31
33
|
const ChatDetail = memo(
|
|
32
34
|
({
|
|
@@ -133,12 +135,20 @@ const ChatDetail = memo(
|
|
|
133
135
|
);
|
|
134
136
|
|
|
135
137
|
const handleMessageLongPress = useCallback(
|
|
136
|
-
(message:
|
|
138
|
+
(message: MessageItem, rect: MeasuredRect) => {
|
|
137
139
|
messageToolboxRef.current?.show(message, rect);
|
|
138
140
|
},
|
|
139
141
|
[]
|
|
140
142
|
);
|
|
141
143
|
|
|
144
|
+
const handlePressPinnedMessageBar = useCallback(() => {
|
|
145
|
+
openPinnedMessagesSheet({
|
|
146
|
+
currentUserId,
|
|
147
|
+
unpinMessage,
|
|
148
|
+
customMessageItemFactory,
|
|
149
|
+
});
|
|
150
|
+
}, [currentUserId, unpinMessage, customMessageItemFactory]);
|
|
151
|
+
|
|
142
152
|
return (
|
|
143
153
|
<KContainer.Page flex edges={['bottom']}>
|
|
144
154
|
<ChatDetailHeader
|
|
@@ -154,6 +164,7 @@ const ChatDetail = memo(
|
|
|
154
164
|
<PinnedMessageBar
|
|
155
165
|
messages={pinnedMessages}
|
|
156
166
|
currentUserId={currentUserId}
|
|
167
|
+
onPress={handlePressPinnedMessageBar}
|
|
157
168
|
/>
|
|
158
169
|
|
|
159
170
|
<KeyboardAvoidingView
|
|
@@ -9,7 +9,6 @@ import { LegendList, type LegendListRef } from '@legendapp/list/react-native';
|
|
|
9
9
|
import { KContainer, KColors, KSpacingValue, KLabel } from '@droppii/libs';
|
|
10
10
|
import { precomputeLegendListData } from '../../utils/legendListMessage';
|
|
11
11
|
import type { ChatListProps } from './types';
|
|
12
|
-
import type { DMessageItem } from '../../types/chat';
|
|
13
12
|
import { LegendChatMessage } from './legend/LegendChatMessage';
|
|
14
13
|
import { LegendChatDay } from './legend/LegendChatDay';
|
|
15
14
|
import { LegendChatLoadEarlier } from './legend/LegendChatLoadEarlier';
|
|
@@ -20,6 +19,7 @@ import { useResetFlow, useRunFlow } from '../../hooks/flows/useFlow';
|
|
|
20
19
|
import type { IRunConversationFlowPayload } from '../../types/flows';
|
|
21
20
|
import { useSendMessage } from '../../hooks/message/useSendMessage';
|
|
22
21
|
import { messageStyles } from './legend/messageTypes';
|
|
22
|
+
import type { MessageItem } from '@droppii/openim-rn-client-sdk';
|
|
23
23
|
|
|
24
24
|
export const ChatListLegend = memo(
|
|
25
25
|
({
|
|
@@ -90,7 +90,7 @@ export const ChatListLegend = memo(
|
|
|
90
90
|
);
|
|
91
91
|
|
|
92
92
|
const handleMessageLongPress = useCallback(
|
|
93
|
-
(messageId: string, message:
|
|
93
|
+
(messageId: string, message: MessageItem) => {
|
|
94
94
|
const node = rowRefsMap.current.get(messageId);
|
|
95
95
|
node?.measureInWindow((x, y, width, height) => {
|
|
96
96
|
onLongPress?.(message, { x, y, width, height });
|
|
@@ -115,7 +115,7 @@ export const ChatListLegend = memo(
|
|
|
115
115
|
try {
|
|
116
116
|
if (!item) return null;
|
|
117
117
|
|
|
118
|
-
const message = item as
|
|
118
|
+
const message = item as MessageItem;
|
|
119
119
|
const isOutgoing = message.sendID === currentUserId;
|
|
120
120
|
const isLastIncoming =
|
|
121
121
|
!isOutgoing &&
|
|
@@ -13,69 +13,68 @@ import {
|
|
|
13
13
|
getMessagePreview,
|
|
14
14
|
getPinnedMessagePreview,
|
|
15
15
|
} from '../../utils/messageUtils';
|
|
16
|
-
import type {
|
|
16
|
+
import type { MessageItem } from '@droppii/openim-rn-client-sdk';
|
|
17
17
|
|
|
18
18
|
interface PinnedMessageBarProps {
|
|
19
|
-
messages:
|
|
19
|
+
messages: MessageItem[];
|
|
20
20
|
currentUserId?: string;
|
|
21
|
+
onPress?: () => void;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
const PinnedMessageThumbnail = memo(
|
|
24
|
-
|
|
25
|
-
const preview = useMemo(() => getPinnedMessagePreview(message), [message]);
|
|
24
|
+
const PinnedMessageThumbnail = memo(({ message }: { message: MessageItem }) => {
|
|
25
|
+
const preview = useMemo(() => getPinnedMessagePreview(message), [message]);
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (preview.mediaType === 'video') {
|
|
38
|
-
return (
|
|
39
|
-
<KContainer.View style={styles.thumbnail}>
|
|
40
|
-
{preview.thumbnailUrl && (
|
|
41
|
-
<KImage.Base
|
|
42
|
-
uri={preview.thumbnailUrl}
|
|
43
|
-
style={styles.thumbnail}
|
|
44
|
-
resizeMode="cover"
|
|
45
|
-
/>
|
|
46
|
-
)}
|
|
47
|
-
<KContainer.View style={styles.videoOverlay} center>
|
|
48
|
-
<KImage.VectorIcons
|
|
49
|
-
name="play-circle"
|
|
50
|
-
provider="MaterialCommunityIcons"
|
|
51
|
-
size={16}
|
|
52
|
-
color={KColors.white}
|
|
53
|
-
/>
|
|
54
|
-
</KContainer.View>
|
|
55
|
-
</KContainer.View>
|
|
56
|
-
);
|
|
57
|
-
}
|
|
27
|
+
if (preview.mediaType === 'image' && preview.thumbnailUrl) {
|
|
28
|
+
return (
|
|
29
|
+
<KImage.Base
|
|
30
|
+
uri={preview.thumbnailUrl}
|
|
31
|
+
style={styles.thumbnail}
|
|
32
|
+
resizeMode="cover"
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
58
36
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
37
|
+
if (preview.mediaType === 'video') {
|
|
38
|
+
return (
|
|
39
|
+
<KContainer.View style={styles.thumbnail}>
|
|
40
|
+
{preview.thumbnailUrl && (
|
|
62
41
|
<KImage.Base
|
|
63
|
-
uri={preview.
|
|
64
|
-
style={styles.
|
|
65
|
-
resizeMode="
|
|
42
|
+
uri={preview.thumbnailUrl}
|
|
43
|
+
style={styles.thumbnail}
|
|
44
|
+
resizeMode="cover"
|
|
45
|
+
/>
|
|
46
|
+
)}
|
|
47
|
+
<KContainer.View style={styles.videoOverlay} center>
|
|
48
|
+
<KImage.VectorIcons
|
|
49
|
+
name="play-circle"
|
|
50
|
+
provider="MaterialCommunityIcons"
|
|
51
|
+
size={16}
|
|
52
|
+
color={KColors.white}
|
|
66
53
|
/>
|
|
67
54
|
</KContainer.View>
|
|
68
|
-
|
|
69
|
-
|
|
55
|
+
</KContainer.View>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
70
58
|
|
|
71
|
-
|
|
59
|
+
if (preview.mediaType === 'file' && preview.fileIcon) {
|
|
60
|
+
return (
|
|
61
|
+
<KContainer.View marginL="0.75rem">
|
|
62
|
+
<KImage.Base
|
|
63
|
+
uri={preview.fileIcon}
|
|
64
|
+
style={styles.fileIcon}
|
|
65
|
+
resizeMode="contain"
|
|
66
|
+
/>
|
|
67
|
+
</KContainer.View>
|
|
68
|
+
);
|
|
72
69
|
}
|
|
73
|
-
|
|
70
|
+
|
|
71
|
+
return null;
|
|
72
|
+
});
|
|
74
73
|
|
|
75
74
|
PinnedMessageThumbnail.displayName = 'PinnedMessageThumbnail';
|
|
76
75
|
|
|
77
76
|
export const PinnedMessageBar = memo(
|
|
78
|
-
({ messages, currentUserId }: PinnedMessageBarProps) => {
|
|
77
|
+
({ messages, currentUserId, onPress }: PinnedMessageBarProps) => {
|
|
79
78
|
if (messages.length === 0) {
|
|
80
79
|
return null;
|
|
81
80
|
}
|
|
@@ -87,7 +86,11 @@ export const PinnedMessageBar = memo(
|
|
|
87
86
|
const { subtitleKey } = getPinnedMessagePreview(latest);
|
|
88
87
|
|
|
89
88
|
return (
|
|
90
|
-
<KContainer.
|
|
89
|
+
<KContainer.Touchable
|
|
90
|
+
activeOpacity={0.7}
|
|
91
|
+
onPress={onPress}
|
|
92
|
+
style={styles.container}
|
|
93
|
+
>
|
|
91
94
|
<PinnedMessageThumbnail message={latest} />
|
|
92
95
|
|
|
93
96
|
<KContainer.View row alignItems flex paddingR="0.75rem">
|
|
@@ -118,6 +121,7 @@ export const PinnedMessageBar = memo(
|
|
|
118
121
|
badge={`+${remainingCount}`}
|
|
119
122
|
skipConvertBadge
|
|
120
123
|
badgeSize={{ width: 26, height: 20 }}
|
|
124
|
+
// eslint-disable-next-line react-native/no-inline-styles
|
|
121
125
|
wrapperLabelTextStyle={{ paddingHorizontal: 3 }}
|
|
122
126
|
/>
|
|
123
127
|
)}
|
|
@@ -127,7 +131,7 @@ export const PinnedMessageBar = memo(
|
|
|
127
131
|
color={KColors.gray.normal}
|
|
128
132
|
/>
|
|
129
133
|
</KContainer.View>
|
|
130
|
-
</KContainer.
|
|
134
|
+
</KContainer.Touchable>
|
|
131
135
|
);
|
|
132
136
|
}
|
|
133
137
|
);
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import {
|
|
2
|
+
forwardRef,
|
|
3
|
+
memo,
|
|
4
|
+
useCallback,
|
|
5
|
+
useImperativeHandle,
|
|
6
|
+
useRef,
|
|
7
|
+
useState,
|
|
8
|
+
} from 'react';
|
|
9
|
+
import type { ReactNode } from 'react';
|
|
10
|
+
import { Modal, Pressable, StyleSheet, type View } from 'react-native';
|
|
11
|
+
import type { MessageItem } from '@droppii/openim-rn-client-sdk';
|
|
12
|
+
import { KContainer, KLabel, KColors } from '@droppii/libs';
|
|
13
|
+
import { UIUtils } from '../../utils/UIUtils';
|
|
14
|
+
import { trans } from '../../translation';
|
|
15
|
+
import { usePinnedMessageStore } from '../../store/pinnedMessage';
|
|
16
|
+
import { LegendChatMessage } from './legend/LegendChatMessage';
|
|
17
|
+
import { formatMessageTime, messageStyles } from './legend/messageTypes';
|
|
18
|
+
import { MessageToolboxWrapper } from './messageToolbox/MessageToolboxWrapper';
|
|
19
|
+
import { MessageToolboxMenu } from './messageToolbox/MessageToolboxMenu';
|
|
20
|
+
import type { MeasuredRect } from './messageToolbox/types';
|
|
21
|
+
|
|
22
|
+
interface PinnedMessageToolboxRef {
|
|
23
|
+
show: (message: MessageItem, rect: MeasuredRect) => void;
|
|
24
|
+
hide: () => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface PinnedMessageToolboxOverlayProps {
|
|
28
|
+
currentUserId?: string;
|
|
29
|
+
unpinMessage: (message: MessageItem) => void | Promise<void>;
|
|
30
|
+
customMessageItemFactory?: (item: MessageItem) => ReactNode;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Mirrors MessageToolboxOverlay (used for the main chat list's long-press
|
|
34
|
+
// menu) but restricted to the 2 actions relevant to an already-pinned
|
|
35
|
+
// message: unpin and jump to the original message.
|
|
36
|
+
const PinnedMessageToolboxOverlay = memo(
|
|
37
|
+
forwardRef<PinnedMessageToolboxRef, PinnedMessageToolboxOverlayProps>(
|
|
38
|
+
({ currentUserId, unpinMessage, customMessageItemFactory }, ref) => {
|
|
39
|
+
const [visible, setVisible] = useState(false);
|
|
40
|
+
const [message, setMessage] = useState<MessageItem | undefined>(
|
|
41
|
+
undefined
|
|
42
|
+
);
|
|
43
|
+
const [rect, setRect] = useState<MeasuredRect | null>(null);
|
|
44
|
+
|
|
45
|
+
const hide = useCallback(() => {
|
|
46
|
+
setVisible(false);
|
|
47
|
+
setMessage(undefined);
|
|
48
|
+
setRect(null);
|
|
49
|
+
}, []);
|
|
50
|
+
|
|
51
|
+
const show = useCallback((msg: MessageItem, msgRect: MeasuredRect) => {
|
|
52
|
+
setMessage(msg);
|
|
53
|
+
setRect(msgRect);
|
|
54
|
+
setVisible(true);
|
|
55
|
+
}, []);
|
|
56
|
+
|
|
57
|
+
useImperativeHandle(ref, () => ({ show, hide }), [show, hide]);
|
|
58
|
+
|
|
59
|
+
const handleUnpinMessage = useCallback(() => {
|
|
60
|
+
if (!message) return;
|
|
61
|
+
hide();
|
|
62
|
+
unpinMessage(message);
|
|
63
|
+
}, [message, hide, unpinMessage]);
|
|
64
|
+
|
|
65
|
+
const handleViewOriginalMessage = useCallback(() => {
|
|
66
|
+
if (!message) return;
|
|
67
|
+
hide();
|
|
68
|
+
// TODO: implement navigating to / highlighting the original message
|
|
69
|
+
}, [message, hide]);
|
|
70
|
+
|
|
71
|
+
if (!visible || !message || !rect) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const isOutgoing = message.sendID === currentUserId;
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<Modal
|
|
79
|
+
visible={visible}
|
|
80
|
+
transparent
|
|
81
|
+
statusBarTranslucent
|
|
82
|
+
animationType="fade"
|
|
83
|
+
onRequestClose={hide}
|
|
84
|
+
>
|
|
85
|
+
<Pressable style={styles.backdrop} onPress={hide} />
|
|
86
|
+
|
|
87
|
+
<MessageToolboxWrapper
|
|
88
|
+
rect={rect}
|
|
89
|
+
isOutgoing={isOutgoing}
|
|
90
|
+
message={
|
|
91
|
+
<LegendChatMessage
|
|
92
|
+
message={message}
|
|
93
|
+
isOutgoing={isOutgoing}
|
|
94
|
+
createdAtTime={message.sendTime || message.createTime}
|
|
95
|
+
customMessageItemFactory={customMessageItemFactory}
|
|
96
|
+
/>
|
|
97
|
+
}
|
|
98
|
+
toolbox={
|
|
99
|
+
<MessageToolboxMenu
|
|
100
|
+
showUnpinMessage
|
|
101
|
+
showViewOriginalMessage
|
|
102
|
+
onUnpinMessage={handleUnpinMessage}
|
|
103
|
+
onViewOriginalMessage={handleViewOriginalMessage}
|
|
104
|
+
/>
|
|
105
|
+
}
|
|
106
|
+
/>
|
|
107
|
+
</Modal>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
PinnedMessageToolboxOverlay.displayName = 'PinnedMessageToolboxOverlay';
|
|
114
|
+
|
|
115
|
+
const styles = StyleSheet.create({
|
|
116
|
+
backdrop: {
|
|
117
|
+
position: 'absolute',
|
|
118
|
+
top: 0,
|
|
119
|
+
left: 0,
|
|
120
|
+
right: 0,
|
|
121
|
+
bottom: 0,
|
|
122
|
+
backgroundColor: 'rgba(0,0,0,0.75)',
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
interface PinnedMessagesSheetContentProps {
|
|
127
|
+
currentUserId?: string;
|
|
128
|
+
unpinMessage: (message: MessageItem) => void | Promise<void>;
|
|
129
|
+
customMessageItemFactory?: (item: MessageItem) => ReactNode;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const PinnedMessagesSheetContent = memo(
|
|
133
|
+
({
|
|
134
|
+
currentUserId,
|
|
135
|
+
unpinMessage,
|
|
136
|
+
customMessageItemFactory,
|
|
137
|
+
}: PinnedMessagesSheetContentProps) => {
|
|
138
|
+
const messages = usePinnedMessageStore((state) => state.messages);
|
|
139
|
+
const toolboxRef = useRef<PinnedMessageToolboxRef>(null);
|
|
140
|
+
const rowRefsMap = useRef<Map<string, View | null>>(new Map());
|
|
141
|
+
|
|
142
|
+
const handleLongPress = useCallback(
|
|
143
|
+
(messageId: string, message: MessageItem) => {
|
|
144
|
+
const node = rowRefsMap.current.get(messageId);
|
|
145
|
+
node?.measureInWindow((x, y, width, height) => {
|
|
146
|
+
toolboxRef.current?.show(message, { x, y, width, height });
|
|
147
|
+
});
|
|
148
|
+
},
|
|
149
|
+
[]
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<KContainer.View paddingV="0.5rem">
|
|
154
|
+
<KLabel.Text
|
|
155
|
+
typo="TextNmNormal"
|
|
156
|
+
color={KColors.gray.normal}
|
|
157
|
+
paddingH="0.75rem"
|
|
158
|
+
marginB="0.75rem"
|
|
159
|
+
>
|
|
160
|
+
{trans('chat:pinned_messages_hint')}
|
|
161
|
+
</KLabel.Text>
|
|
162
|
+
|
|
163
|
+
{messages.map((message) => {
|
|
164
|
+
const isOutgoing = message.sendID === currentUserId;
|
|
165
|
+
const messageId = message.clientMsgID || message.serverMsgID;
|
|
166
|
+
const createdAtTime = message.sendTime || message.createTime;
|
|
167
|
+
const timeLabel = formatMessageTime(createdAtTime);
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<KContainer.View
|
|
171
|
+
key={messageId}
|
|
172
|
+
ref={(node: View | null) => {
|
|
173
|
+
if (node) rowRefsMap.current.set(messageId, node);
|
|
174
|
+
else rowRefsMap.current.delete(messageId);
|
|
175
|
+
}}
|
|
176
|
+
style={
|
|
177
|
+
isOutgoing
|
|
178
|
+
? messageStyles.messageRowRight
|
|
179
|
+
: messageStyles.messageRowLeft
|
|
180
|
+
}
|
|
181
|
+
>
|
|
182
|
+
<LegendChatMessage
|
|
183
|
+
message={message}
|
|
184
|
+
isOutgoing={isOutgoing}
|
|
185
|
+
createdAtTime={createdAtTime}
|
|
186
|
+
onLongPress={() => handleLongPress(messageId, message)}
|
|
187
|
+
customMessageItemFactory={customMessageItemFactory}
|
|
188
|
+
/>
|
|
189
|
+
{!!timeLabel && (
|
|
190
|
+
<KLabel.Text
|
|
191
|
+
typo="TextXsNormal"
|
|
192
|
+
color={KColors.gray.light}
|
|
193
|
+
marginT="0.25rem"
|
|
194
|
+
>
|
|
195
|
+
{timeLabel}
|
|
196
|
+
</KLabel.Text>
|
|
197
|
+
)}
|
|
198
|
+
</KContainer.View>
|
|
199
|
+
);
|
|
200
|
+
})}
|
|
201
|
+
|
|
202
|
+
<PinnedMessageToolboxOverlay
|
|
203
|
+
ref={toolboxRef}
|
|
204
|
+
currentUserId={currentUserId}
|
|
205
|
+
unpinMessage={unpinMessage}
|
|
206
|
+
customMessageItemFactory={customMessageItemFactory}
|
|
207
|
+
/>
|
|
208
|
+
</KContainer.View>
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
PinnedMessagesSheetContent.displayName = 'PinnedMessagesSheetContent';
|
|
214
|
+
|
|
215
|
+
export interface OpenPinnedMessagesSheetParams {
|
|
216
|
+
currentUserId?: string;
|
|
217
|
+
unpinMessage: (message: MessageItem) => void | Promise<void>;
|
|
218
|
+
customMessageItemFactory?: (item: MessageItem) => ReactNode;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export const openPinnedMessagesSheet = ({
|
|
222
|
+
currentUserId,
|
|
223
|
+
unpinMessage,
|
|
224
|
+
customMessageItemFactory,
|
|
225
|
+
}: OpenPinnedMessagesSheetParams) => {
|
|
226
|
+
UIUtils.bottomSheet.open({
|
|
227
|
+
header: {
|
|
228
|
+
title: { text: trans('chat:pinned_messages_title') },
|
|
229
|
+
alignment: 'left',
|
|
230
|
+
showCloseButton: true,
|
|
231
|
+
},
|
|
232
|
+
touchOutsideToDismiss: true,
|
|
233
|
+
body: {
|
|
234
|
+
renderContent: () => (
|
|
235
|
+
<PinnedMessagesSheetContent
|
|
236
|
+
currentUserId={currentUserId}
|
|
237
|
+
unpinMessage={unpinMessage}
|
|
238
|
+
customMessageItemFactory={customMessageItemFactory}
|
|
239
|
+
/>
|
|
240
|
+
),
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
};
|
|
@@ -6,7 +6,6 @@ import { MessageType } from '@droppii/openim-rn-client-sdk';
|
|
|
6
6
|
import type { MessageItem } from '@droppii/openim-rn-client-sdk';
|
|
7
7
|
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
8
8
|
import { runOnJS } from 'react-native-reanimated';
|
|
9
|
-
import type { DMessageItem } from '../../../types/chat';
|
|
10
9
|
import type { MediaItem } from '../../../hooks/useMediaViewer';
|
|
11
10
|
import { TextMessage } from '../../../components/messages/textMessage';
|
|
12
11
|
import { ImageMessage } from '../../../components/messages/imageMessage';
|
|
@@ -18,6 +17,7 @@ import { RevokedMessage } from '../../../components/messages/revokedMessage';
|
|
|
18
17
|
import { MessageStatusIndicator } from '../../../components/messages/MessageStatusIndicator';
|
|
19
18
|
import type { BaseLegendMessageProps } from '../../../components/messages/types';
|
|
20
19
|
import { messageStyles } from './messageTypes';
|
|
20
|
+
import QuotedMessage from '../../../components/messages/quotedMessage';
|
|
21
21
|
|
|
22
22
|
type MessageProps = BaseLegendMessageProps & {
|
|
23
23
|
onPressUrl?: (url: string) => void;
|
|
@@ -25,14 +25,14 @@ type MessageProps = BaseLegendMessageProps & {
|
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
interface LegendChatMessageProps {
|
|
28
|
-
message:
|
|
28
|
+
message: MessageItem;
|
|
29
29
|
isOutgoing: boolean;
|
|
30
30
|
createdAtTime: number;
|
|
31
31
|
onMediaPress?: (items: MediaItem[], index?: number) => void;
|
|
32
32
|
onPressUrl?: (url: string) => void;
|
|
33
33
|
onRetry?: (message: MessageItem) => void;
|
|
34
34
|
onLongPress?: () => void;
|
|
35
|
-
customMessageItemFactory?: (m:
|
|
35
|
+
customMessageItemFactory?: (m: MessageItem) => ReactNode;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
// OpenIM message types (not all exposed as MessageType enum)
|
|
@@ -43,7 +43,7 @@ const messageComponentMap: Partial<
|
|
|
43
43
|
> = {
|
|
44
44
|
[MessageType.TextMessage]: TextMessage,
|
|
45
45
|
[MessageType.AtTextMessage]: TextMessage,
|
|
46
|
-
[MessageType.QuoteMessage]:
|
|
46
|
+
[MessageType.QuoteMessage]: QuotedMessage,
|
|
47
47
|
[MessageType.UrlTextMessage]: LinkMessage,
|
|
48
48
|
[MessageType.PictureMessage]: ImageMessage,
|
|
49
49
|
[MessageType.VideoMessage]: VideoMessage,
|