Files
colanode/apps/desktop/src/renderer/components/messages/message.tsx

190 lines
6.2 KiB
TypeScript
Raw Normal View History

import { Avatar } from '@/renderer/components/avatars/avatar';
2024-08-03 19:14:38 +02:00
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from '@/renderer/components/ui/tooltip';
import { formatDate, timeAgo } from '@/shared/lib/utils';
2024-08-03 19:14:38 +02:00
import { InView } from 'react-intersection-observer';
import { MessageReactionCreatePopover } from '@/renderer/components/messages/message-reaction-create-popover';
import { MessageDeleteButton } from '@/renderer/components/messages/message-delete-button';
import { NodeRenderer } from '@/renderer/editor/renderers/node';
import { MessageNode } from '@/shared/types/messages';
import { MessageReactions } from '@/renderer/components/messages/message-reactions';
2024-10-03 17:34:11 +02:00
import { useMutation } from '@/renderer/hooks/use-mutation';
2024-10-03 17:35:28 +02:00
import { useWorkspace } from '@/renderer/contexts/workspace';
2024-10-27 09:14:09 +01:00
import { MessagesSquare, Reply } from 'lucide-react';
import { useConversation } from '@/renderer/contexts/conversation';
2024-11-14 17:11:53 +01:00
import { useRadar } from '@/renderer/contexts/radar';
2024-08-03 19:14:38 +02:00
interface MessageProps {
2024-08-22 20:17:36 +02:00
message: MessageNode;
previousMessage?: MessageNode | null;
2024-08-03 19:14:38 +02:00
}
2024-09-15 16:29:47 +02:00
const shouldDisplayUserInfo = (
message: MessageNode,
previousMessage?: MessageNode | null
2024-09-15 16:29:47 +02:00
) => {
if (!previousMessage) {
return true;
}
2024-09-15 16:29:47 +02:00
const previousMessageDate = new Date(previousMessage.createdAt);
const currentMessageDate = new Date(message.createdAt);
2024-09-15 16:29:47 +02:00
if (previousMessageDate.getDate() !== currentMessageDate.getDate()) {
return true;
}
2024-09-15 16:29:47 +02:00
return previousMessage.author.id !== message.author.id;
};
2024-08-03 19:14:38 +02:00
export const Message = ({ message, previousMessage }: MessageProps) => {
2024-10-03 17:11:39 +02:00
const workspace = useWorkspace();
const conversation = useConversation();
2024-11-14 17:11:53 +01:00
const radar = useRadar();
2024-10-03 17:11:39 +02:00
const { mutate, isPending } = useMutation();
2024-08-03 19:14:38 +02:00
const canDelete = conversation.canDeleteMessage(message);
const canReplyInThread = false;
2024-09-15 16:29:47 +02:00
const displayUserInfo = shouldDisplayUserInfo(message, previousMessage);
2024-08-27 15:20:16 +02:00
return (
<div
id={`message-${message.id}`}
key={`message-${message.id}`}
className={`group flex flex-row px-1 hover:bg-gray-50 ${
2024-09-15 16:29:47 +02:00
displayUserInfo ? 'mt-2 first:mt-0' : ''
2024-08-27 15:20:16 +02:00
}`}
>
<div className="mr-2 w-10 pt-1">
2024-09-15 16:29:47 +02:00
{displayUserInfo && (
<Avatar
id={message.author.id}
name={message.author.name}
2024-10-27 22:15:17 +01:00
avatar={message.author.avatar}
size="medium"
/>
2024-08-27 15:20:16 +02:00
)}
</div>
2024-08-03 19:14:38 +02:00
2024-08-27 15:20:16 +02:00
<div className="relative w-full">
2024-09-15 16:29:47 +02:00
{displayUserInfo && (
<div className="font-medium">
<span>{message.author.name}</span>
2024-08-27 15:20:16 +02:00
<Tooltip>
<TooltipTrigger asChild>
<span className="ml-2 text-xs text-muted-foreground">
{timeAgo(message.createdAt)}
</span>
</TooltipTrigger>
<TooltipContent>
<span className="text-sm shadow-md">
2024-08-27 15:20:16 +02:00
{formatDate(message.createdAt)}
</span>
</TooltipContent>
</Tooltip>
</div>
2024-08-27 15:20:16 +02:00
)}
2024-08-03 19:14:38 +02:00
2024-08-27 15:20:16 +02:00
<InView
rootMargin="50px"
onChange={(inView) => {
2024-10-28 20:35:30 +01:00
if (inView) {
2024-11-14 17:11:53 +01:00
radar.markAsSeen(workspace.userId, message.id, message.versionId);
2024-10-28 20:35:30 +01:00
}
2024-08-27 15:20:16 +02:00
}}
>
<ul className="invisible absolute -top-2 right-1 z-10 flex flex-row bg-gray-100 text-muted-foreground shadow group-hover:visible">
{canReplyInThread && (
2024-09-15 16:29:47 +02:00
<li className="flex h-8 w-7 cursor-pointer items-center justify-center hover:bg-gray-200">
2024-10-27 09:14:09 +01:00
<MessagesSquare className="size-4 cursor-pointer" />
2024-08-27 15:20:16 +02:00
</li>
)}
<li className="flex h-8 w-7 cursor-pointer items-center justify-center hover:bg-gray-200">
2024-09-15 16:29:47 +02:00
<MessageReactionCreatePopover
onReactionClick={(reaction) => {
2024-10-03 17:11:39 +02:00
if (isPending) {
2024-09-15 16:29:47 +02:00
return;
}
2024-10-03 17:11:39 +02:00
mutate({
input: {
type: 'node_reaction_create',
nodeId: message.id,
userId: workspace.userId,
reaction,
},
});
2024-08-27 15:20:16 +02:00
}}
/>
</li>
{conversation.canCreateMessage && (
<li className="flex h-8 w-7 cursor-pointer items-center justify-center hover:bg-gray-200">
<Reply
className="size-4 cursor-pointer"
onClick={() => {
conversation.onReply(message);
}}
/>
</li>
)}
2024-08-27 15:20:16 +02:00
{canDelete && (
<li className="flex h-8 w-7 cursor-pointer items-center justify-center hover:bg-gray-200">
<MessageDeleteButton id={message.id} />
2024-08-03 19:14:38 +02:00
</li>
2024-08-27 15:20:16 +02:00
)}
</ul>
<div className="text-foreground">
{message.content.map((node) => (
2024-10-09 11:29:08 +02:00
<NodeRenderer
key={node.attrs?.id}
node={node}
keyPrefix={node.attrs?.id}
/>
))}
2024-08-27 15:20:16 +02:00
</div>
{message.reactionCounts?.length > 0 && (
2024-09-15 16:29:47 +02:00
<MessageReactions
message={message}
onReactionClick={(reaction) => {
2024-10-03 17:11:39 +02:00
if (isPending) {
2024-09-15 16:29:47 +02:00
return;
}
if (
message.reactionCounts.some(
(reactionCount) =>
reactionCount.reaction === reaction &&
reactionCount.isReactedTo
)
) {
2024-10-03 17:11:39 +02:00
mutate({
input: {
type: 'node_reaction_delete',
nodeId: message.id,
userId: workspace.userId,
reaction,
},
});
2024-09-15 16:29:47 +02:00
} else {
2024-10-03 17:11:39 +02:00
mutate({
input: {
type: 'node_reaction_create',
nodeId: message.id,
userId: workspace.userId,
reaction,
},
});
2024-09-15 16:29:47 +02:00
}
}}
/>
)}
2024-08-27 15:20:16 +02:00
</InView>
2024-08-03 19:14:38 +02:00
</div>
2024-08-27 15:20:16 +02:00
</div>
);
};