diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx
index 65d6f80..c129408 100644
--- a/src/components/Chat/MessageItem.tsx
+++ b/src/components/Chat/MessageItem.tsx
@@ -98,533 +98,555 @@ const UserBadge = memo(({ userInfo }) => {
);
});
-export const MessageItem = memo(
- ({
- handleReaction,
- isLast,
- isPrivate,
- isShowingAsReply,
- isTemp,
- isUpdating,
- lastSignature,
- message,
- myAddress,
- onEdit,
- onReply,
- onSeen,
- reactions,
- reply,
- replyIndex,
- scrollToItem,
- }) => {
- const { getIndividualUserInfo } = useContext(QORTAL_APP_CONTEXT);
- const [anchorEl, setAnchorEl] = useState(null);
- const [selectedReaction, setSelectedReaction] = useState(null);
- const [userInfo, setUserInfo] = useState(null);
+type MessageItemProps = {
+ handleReaction: (reaction: string, messageId: string) => void;
+ isLast: boolean;
+ isPrivate: boolean;
+ isShowingAsReply?: boolean;
+ isTemp: boolean;
+ isUpdating: boolean;
+ lastSignature: any;
+ message: any;
+ myAddress: any;
+ onEdit: (messageId: string) => void;
+ onReply: (messageId: string) => void;
+ onSeen: () => void;
+ reactions: any; // could be null, or type it more strictly
+ reply: any; // same here
+ replyIndex: number;
+ scrollToItem: (index: number) => void;
+};
- useEffect(() => {
- const getInfo = async () => {
- if (!message?.sender) return;
- try {
- const res = await getIndividualUserInfo(message?.sender);
- if (!res) return null;
- setUserInfo(res);
- } catch (error) {
- //
- }
- };
+export const MessageItemComponent = ({
+ handleReaction,
+ isLast,
+ isPrivate,
+ isShowingAsReply,
+ isTemp,
+ isUpdating,
+ lastSignature,
+ message,
+ myAddress,
+ onEdit,
+ onReply,
+ onSeen,
+ reactions,
+ reply,
+ replyIndex,
+ scrollToItem,
+}: MessageItemProps) => {
+ const { getIndividualUserInfo } = useContext(QORTAL_APP_CONTEXT);
+ const [anchorEl, setAnchorEl] = useState(null);
+ const [selectedReaction, setSelectedReaction] = useState(null);
+ const [userInfo, setUserInfo] = useState(null);
- getInfo();
- }, [message?.sender, getIndividualUserInfo]);
-
- const htmlText = useMemo(() => {
- if (message?.messageText) {
- const isHtml = isHtmlString(message?.messageText);
- if (isHtml) return message?.messageText;
- return generateHTML(message?.messageText, [
- StarterKit,
- Underline,
- Highlight,
- Mention,
- TextStyle,
- ]);
+ useEffect(() => {
+ const getInfo = async () => {
+ if (!message?.sender) return;
+ try {
+ const res = await getIndividualUserInfo(message?.sender);
+ if (!res) return null;
+ setUserInfo(res);
+ } catch (error) {
+ //
}
- }, [message?.editTimestamp]);
+ };
- const htmlReply = useMemo(() => {
- if (reply?.messageText) {
- const isHtml = isHtmlString(reply?.messageText);
- if (isHtml) return reply?.messageText;
- return generateHTML(reply?.messageText, [
- StarterKit,
- Underline,
- Highlight,
- Mention,
- TextStyle,
- ]);
- }
- }, [reply?.editTimestamp]);
+ getInfo();
+ }, [message?.sender, getIndividualUserInfo]);
- const userAvatarUrl = useMemo(() => {
- return message?.senderName
- ? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${
- message?.senderName
- }/qortal_avatar?async=true`
- : '';
- }, []);
+ const htmlText = useMemo(() => {
+ if (message?.messageText) {
+ const isHtml = isHtmlString(message?.messageText);
+ if (isHtml) return message?.messageText;
+ return generateHTML(message?.messageText, [
+ StarterKit,
+ Underline,
+ Highlight,
+ Mention,
+ TextStyle,
+ ]);
+ }
+ }, [message?.editTimestamp]);
- const onSeenFunc = useCallback(() => {
- onSeen(message.id);
- }, [message?.id]);
+ const htmlReply = useMemo(() => {
+ if (reply?.messageText) {
+ const isHtml = isHtmlString(reply?.messageText);
+ if (isHtml) return reply?.messageText;
+ return generateHTML(reply?.messageText, [
+ StarterKit,
+ Underline,
+ Highlight,
+ Mention,
+ TextStyle,
+ ]);
+ }
+ }, [reply?.editTimestamp]);
- const theme = useTheme();
- const { t } = useTranslation([
- 'auth',
- 'core',
- 'group',
- 'question',
- 'tutorial',
- ]);
+ const userAvatarUrl = useMemo(() => {
+ return message?.senderName
+ ? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${
+ message?.senderName
+ }/qortal_avatar?async=true`
+ : '';
+ }, []);
- return (
- <>
- {message?.divide && (
-
- {t('core:message.generic.unread_messages', {
- postProcess: 'capitalizeFirstChar',
- })}
-
- )}
+ const onSeenFunc = useCallback(() => {
+ onSeen(message.id);
+ }, [message?.id]);
-
+ {message?.divide && (
+
+ {t('core:message.generic.unread_messages', {
+ postProcess: 'capitalizeFirstChar',
+ })}
+
+ )}
+
+
+
-
+ ) : (
+
+
+
+ {message?.senderName?.charAt(0)}
+
+
+
+
+ )}
+
+
- {isShowingAsReply ? (
-
- ) : (
+
+
+
+ {message?.senderName || message?.sender}
+
+
+
-
- {
+ onEdit(message);
+ }}
+ >
+
+
+ )}
+
+ {!isShowingAsReply && (
+ {
+ onReply(message);
}}
- alt={message?.senderName}
- src={userAvatarUrl}
>
- {message?.senderName?.charAt(0)}
-
-
-
+
+
+ )}
+
+ {!isShowingAsReply && handleReaction && (
+ {
+ if (
+ reactions &&
+ reactions[val] &&
+ reactions[val]?.find(
+ (item) => item?.sender === myAddress
+ )
+ ) {
+ handleReaction(val, message, false);
+ } else {
+ handleReaction(val, message, true);
+ }
+ }}
+ />
+ )}
+
+
+ {reply && (
+ <>
+
+
+ {
+ scrollToItem(replyIndex);
+ }}
+ >
+
+
+
+
+ {t('core:message.generic.replied_to', {
+ person: reply?.senderName || reply?.senderAddress,
+ postProcess: 'capitalizeFirstChar',
+ })}
+
+
+ {reply?.messageText && (
+
+ )}
+
+ {reply?.decryptedData?.type === 'notification' ? (
+
+ ) : (
+
+ )}
+
+
+ >
+ )}
+
+ {htmlText && }
+
+ {message?.decryptedData?.type === 'notification' ? (
+
+ ) : (
+
+ )}
+ {message?.images && messageHasImage(message) && (
+
)}
-
-
- {message?.senderName || message?.sender}
-
-
-
-
- {message?.sender === myAddress &&
- (!message?.isNotEncrypted || isPrivate === false) && (
+ {reactions &&
+ Object.keys(reactions).map((reaction) => {
+ const numberOfReactions = reactions[reaction]?.length;
+ if (numberOfReactions === 0) return null;
+ return (
{
- onEdit(message);
+ key={reaction}
+ sx={{
+ background: theme.palette.background.surface,
+ borderRadius: '7px',
+ height: '30px',
+ minWidth: '45px',
+ }}
+ onClick={(event) => {
+ event.stopPropagation(); // Prevent event bubbling
+ setAnchorEl(event.currentTarget);
+ setSelectedReaction(reaction);
}}
>
-
+
+ {reaction}
+
{' '}
+ {numberOfReactions > 1 && (
+
+ {numberOfReactions}
+
+ )}
- )}
+ );
+ })}
+
- {!isShowingAsReply && (
- {
- onReply(message);
+ {selectedReaction && (
+ {
+ setAnchorEl(null);
+ setSelectedReaction(null);
+ }}
+ anchorOrigin={{
+ vertical: 'top',
+ horizontal: 'center',
+ }}
+ transformOrigin={{
+ vertical: 'bottom',
+ horizontal: 'center',
+ }}
+ slotProps={{
+ paper: {
+ style: {
+ backgroundColor: theme.palette.background.default,
+ color: theme.palette.text.primary,
+ },
+ },
+ }}
+ >
+
+
+ {t('core:message.generic.people_reaction', {
+ reaction: selectedReaction,
+ postProcess: 'capitalizeFirstChar',
+ })}
+
+
+
-
-
- )}
+ {reactions[selectedReaction]?.map((reactionItem) => (
+
+
+
+ ))}
+
- {!isShowingAsReply && handleReaction && (
- {
+
-
-
- {reply && (
- <>
-
-
- {
- scrollToItem(replyIndex);
- }}
- >
-
-
-
-
- {t('core:message.generic.replied_to', {
- person: reply?.senderName || reply?.senderAddress,
- postProcess: 'capitalizeFirstChar',
- })}
-
-
- {reply?.messageText && (
-
- )}
-
- {reply?.decryptedData?.type === 'notification' ? (
-
- ) : (
-
- )}
-
+ {reactions[selectedReaction]?.find(
+ (item) => item?.sender === myAddress
+ )
+ ? t('core:action.remove_reaction', {
+ postProcess: 'capitalizeFirstChar',
+ })
+ : t('core:action.add_reaction', {
+ postProcess: 'capitalizeFirstChar',
+ })}
+
- >
- )}
-
- {htmlText && }
-
- {message?.decryptedData?.type === 'notification' ? (
-
- ) : (
-
- )}
- {message?.images && messageHasImage(message) && (
-
+
)}
-
- {reactions &&
- Object.keys(reactions).map((reaction) => {
- const numberOfReactions = reactions[reaction]?.length;
- if (numberOfReactions === 0) return null;
- return (
- {
- event.stopPropagation(); // Prevent event bubbling
- setAnchorEl(event.currentTarget);
- setSelectedReaction(reaction);
- }}
- >
-
- {reaction}
-
{' '}
- {numberOfReactions > 1 && (
-
- {numberOfReactions}
-
- )}
-
- );
- })}
-
-
- {selectedReaction && (
- {
- setAnchorEl(null);
- setSelectedReaction(null);
+ {message?.isNotEncrypted && isPrivate && (
+
-
-
- {t('core:message.generic.people_reaction', {
- reaction: selectedReaction,
- postProcess: 'capitalizeFirstChar',
- })}
-
-
-
- {reactions[selectedReaction]?.map((reactionItem) => (
-
-
-
- ))}
-
-
-
-
-
+ />
)}
-
- {message?.isNotEncrypted && isPrivate && (
-
- )}
-
- {isUpdating ? (
-
- {message?.status === 'failed-permanent'
- ? t('core:message.error.update_failed', {
- postProcess: 'capitalizeFirstChar',
- })
- : t('core:message.generic.updating', {
- postProcess: 'capitalizeFirstChar',
- })}
-
- ) : isTemp ? (
-
- {message?.status === 'failed-permanent'
- ? t('core:message.error.send_failed', {
- postProcess: 'capitalizeFirstChar',
- })
- : t('core:message.generic.sending', {
- postProcess: 'capitalizeFirstChar',
- })}
-
- ) : (
- <>
- {message?.isEdit && (
-
- {t('core:message.generic.edited', {
- postProcess: 'capitalizeFirstChar',
- })}
-
- )}
-
+ {isUpdating ? (
+
+ {message?.status === 'failed-permanent'
+ ? t('core:message.error.update_failed', {
+ postProcess: 'capitalizeFirstChar',
+ })
+ : t('core:message.generic.updating', {
+ postProcess: 'capitalizeFirstChar',
+ })}
+
+ ) : isTemp ? (
+
+ {message?.status === 'failed-permanent'
+ ? t('core:message.error.send_failed', {
+ postProcess: 'capitalizeFirstChar',
+ })
+ : t('core:message.generic.sending', {
+ postProcess: 'capitalizeFirstChar',
+ })}
+
+ ) : (
+ <>
+ {message?.isEdit && (
- {formatTimestamp(message.timestamp)}
+ {t('core:message.generic.edited', {
+ postProcess: 'capitalizeFirstChar',
+ })}
- >
- )}
-
+ )}
+
+
+ {formatTimestamp(message.timestamp)}
+
+ >
+ )}
-
-
- >
- );
- }
-);
+
+
+
+ >
+ );
+};
+
+const MemoizedMessageItem = memo(MessageItemComponent);
+MemoizedMessageItem.displayName = 'MessageItem'; // It ensures React DevTools shows MessageItem as the name (instead of "Anonymous" or "Memo")
+
+export const MessageItem = MemoizedMessageItem;
export const ReplyPreview = ({ message, isEdit = false }) => {
const theme = useTheme();