Skip to content

Commit daf291d

Browse files
authored
feat: add clickable timestamp to quoted messages (#1089)
1 parent 52d26b5 commit daf291d

1 file changed

Lines changed: 79 additions & 5 deletions

File tree

packages/react/src/views/AttachmentHandler/TextAttachment.js

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { useState, useContext } from 'react';
2+
import { format } from 'date-fns';
23
import { css } from '@emotion/react';
34
import PropTypes from 'prop-types';
45
import { Box, Avatar, useTheme, Icon } from '@embeddedchat/ui-elements';
@@ -11,12 +12,11 @@ const FileAttachment = ({
1112
attachment,
1213
host,
1314
type,
14-
author,
1515
variantStyles = {},
1616
msg,
1717
}) => {
1818
const { RCInstance } = useContext(RCContext);
19-
const { theme } = useTheme();
19+
const { theme, mode } = useTheme();
2020
const [isExpanded, setIsExpanded] = useState(true);
2121

2222
const getUserAvatarUrl = (icon) => {
@@ -28,6 +28,51 @@ const FileAttachment = ({
2828
setIsExpanded((prevState) => !prevState);
2929
};
3030

31+
const getMessageIdFromAttachment = (quoteAttachment) => {
32+
const link = quoteAttachment?.title_link || quoteAttachment?.message_link;
33+
if (!link) return null;
34+
35+
try {
36+
const query = link.includes('?') ? link.split('?')[1] : '';
37+
const params = new URLSearchParams(query);
38+
return params.get('msg');
39+
} catch {
40+
return null;
41+
}
42+
};
43+
44+
const handleQuoteClick = (quoteAttachment) => {
45+
const msgId = getMessageIdFromAttachment(quoteAttachment);
46+
if (!msgId) return;
47+
48+
const element = document.getElementById(`ec-message-body-${msgId}`);
49+
if (!element) return;
50+
51+
const container = element.closest('.ec-message');
52+
if (!container) return;
53+
54+
container.scrollIntoView({ behavior: 'smooth', block: 'center' });
55+
56+
const highlightColor =
57+
mode === 'light' ? theme.colors.warningForeground : theme.colors.warning;
58+
59+
container.style.transition = 'background-color 1s ease-out';
60+
container.style.backgroundColor = highlightColor;
61+
62+
setTimeout(() => {
63+
container.style.backgroundColor = '';
64+
}, 2000);
65+
};
66+
67+
const getTimeString = (ts) => {
68+
if (!ts) return null;
69+
if (typeof ts === 'object' && ts.$date) {
70+
ts = ts.$date;
71+
}
72+
const date = new Date(ts);
73+
return !Number.isNaN(date.getTime()) ? format(date, 'h:mm a') : ts;
74+
};
75+
3176
const formatFileSize = (bytes) => {
3277
if (!bytes || bytes === 0) return '0 B';
3378

@@ -44,9 +89,9 @@ const FileAttachment = ({
4489
return `${size.toFixed(decimals)} ${units[unitIndex]}`;
4590
};
4691

47-
const getFileSizeWithFormat = (size, format) => {
92+
const getFileSizeWithFormat = (size, fileFormat) => {
4893
const formattedSize = formatFileSize(size);
49-
return format ? `${formattedSize} - ${format}` : formattedSize;
94+
return fileFormat ? `${formattedSize} - ${fileFormat}` : formattedSize;
5095
};
5196

5297
return (
@@ -84,6 +129,21 @@ const FileAttachment = ({
84129
size="1.2em"
85130
/>
86131
<Box>@{attachment?.author_name}</Box>
132+
{getTimeString(attachment?.ts) && (
133+
<Box
134+
onClick={() => handleQuoteClick(attachment)}
135+
css={css`
136+
font-size: 0.75rem;
137+
color: ${theme.colors.mutedForeground};
138+
cursor: pointer;
139+
&:hover {
140+
text-decoration: underline;
141+
}
142+
`}
143+
>
144+
{getTimeString(attachment.ts)}
145+
</Box>
146+
)}
87147
</Box>
88148
)}
89149

@@ -217,6 +277,21 @@ const FileAttachment = ({
217277
size="1.2em"
218278
/>
219279
<Box>@{nestedAttachment?.author_name}</Box>
280+
{getTimeString(nestedAttachment?.ts) && (
281+
<Box
282+
onClick={() => handleQuoteClick(nestedAttachment)}
283+
css={css`
284+
font-size: 0.75rem;
285+
color: ${theme.colors.mutedForeground};
286+
cursor: pointer;
287+
&:hover {
288+
text-decoration: underline;
289+
}
290+
`}
291+
>
292+
{getTimeString(nestedAttachment.ts)}
293+
</Box>
294+
)}
220295
</>
221296
)}
222297
</Box>
@@ -309,7 +384,6 @@ FileAttachment.propTypes = {
309384
attachment: PropTypes.object,
310385
host: PropTypes.string,
311386
type: PropTypes.string,
312-
author: PropTypes.object,
313387
variantStyles: PropTypes.object,
314388
msg: PropTypes.object,
315389
};

0 commit comments

Comments
 (0)