diff --git a/packages/chat/src/components/message.tsx b/packages/chat/src/components/message.tsx index de819fc..e2785ec 100644 --- a/packages/chat/src/components/message.tsx +++ b/packages/chat/src/components/message.tsx @@ -1,15 +1,7 @@ import * as React from "react"; import type { RawMessage } from "../values"; import Text from "@tensamin/markdown/text"; -import { - AlertTriangle, - Check, - Ellipse, - Forward, - Laugh, - RefreshCw, - Reply, -} from "lucide-react"; +import { AlertTriangle, Check, Ellipse, RefreshCw } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; import type { User } from "@tensamin/user/context"; @@ -18,9 +10,7 @@ import { AvatarFallback, AvatarImage, Button, - Card, cn, - Separator, Skeleton, } from "@tensamin/ui"; import MessageContextMenu from "./messageContextMenu"; @@ -205,9 +195,10 @@ function MessageComponent({ {user && message.Content ? (
)} -
- - {[0, 1, 2].map((item) => ( - - ))} - - - - - +
{!grouped && (

{user.Display}

@@ -283,8 +259,9 @@ function MessageComponent({
)} {editing ? ( -
+
Cool item; } +let shiftPressed = false; +const shiftListeners = new Set<() => void>(); + +function setShiftPressed(value: boolean) { + if (shiftPressed === value) return; + + shiftPressed = value; + shiftListeners.forEach((listener) => listener()); +} + +function subscribeToShift(listener: () => void) { + shiftListeners.add(listener); + + if (shiftListeners.size === 1) { + window.addEventListener("keydown", handleShiftKey); + window.addEventListener("keyup", handleShiftKey); + window.addEventListener("blur", handleWindowBlur); + } + + return () => { + shiftListeners.delete(listener); + if (shiftListeners.size === 0) { + window.removeEventListener("keydown", handleShiftKey); + window.removeEventListener("keyup", handleShiftKey); + window.removeEventListener("blur", handleWindowBlur); + shiftPressed = false; + } + }; +} + +function handleShiftKey(event: KeyboardEvent) { + setShiftPressed(event.shiftKey); +} + +function handleWindowBlur() { + setShiftPressed(false); +} + +function useShiftPressed() { + return useSyncExternalStore( + subscribeToShift, + () => shiftPressed, + () => false, + ); +} + +function MiniMessageMenu({ + isOwnMessage, + onDelete, + onEdit, + onOpenMenu, + onReply, + shiftIsPressed, +}: { + isOwnMessage: boolean; + onDelete: () => void; + onEdit: () => void; + onOpenMenu: (event: ReactMouseEvent) => void; + onReply: () => void; + shiftIsPressed: boolean; +}) { + return ( + + {[0, 1, 2].map((item) => ( + + ))} + + + + + + {isOwnMessage && shiftIsPressed ? ( + + ) : ( + + )} + + ); +} + function MessageMenuContent({ components, content, devEnabled, + isOwnMessage, messageId, onAddReaction, showReactionItems = true, onSetEditing, - senderId, }: { components: MenuComponents; content: string; devEnabled: boolean; + isOwnMessage: boolean; messageId: number; onAddReaction?: () => void | Promise; showReactionItems?: boolean; onSetEditing: (value: boolean) => void; - senderId: number; }) { const { Content, Group, Item, Separator, Sub, SubContent, SubTrigger } = components; - const { load } = useStorage(); - const { setReplyTo } = useChat(); - - const [ownId, setOwnId] = useState(0); - useEffect(() => { - load("user_id").then(setOwnId); - }, [load]); + const { deleteMessage, setReplyTo } = useChat(); return ( @@ -183,7 +300,7 @@ function MessageMenuContent({ - {senderId === ownId && ( + {isOwnMessage && ( { @@ -205,12 +322,20 @@ function MessageMenuContent({

Forward

- - - -

Delete Message

-
-
+ {isOwnMessage && ( + <> + + + deleteMessage(messageId)} + > +

Delete Message

+
+
+ + )} {devEnabled && ( <> @@ -231,17 +356,21 @@ function MessageMenuContent({ export default function MessageContextMenu({ children, content, + hideMiniMenu = false, + isOwnMessage, messageId, onSetEditing, - senderId, }: { children: ReactElement; content: string; + hideMiniMenu?: boolean; + isOwnMessage: boolean; messageId: number; onSetEditing: (value: boolean) => void; - senderId: number; }) { const isMobile = useIsMobile(); + const shiftIsPressed = useShiftPressed(); + const { deleteMessage, setReplyTo } = useChat(); const devEnabled = useMemo( () => Number(localStorage.getItem("log_level")) >= 3, [], @@ -267,20 +396,50 @@ export default function MessageContextMenu({ [], ); + function renderMessage( + openMenu: (event: ReactMouseEvent) => void, + ) { + return ( +
+ {children} + {!hideMiniMenu && ( + deleteMessage(messageId)} + onEdit={() => onSetEditing(true)} + onOpenMenu={openMenu} + onReply={() => setReplyTo(messageId)} + shiftIsPressed={shiftIsPressed} + /> + )} +
+ ); + } + if (isMobile) { + const child = renderMessage(() => setMainDrawerOpen(true)); + return ( <> - {children} + {cloneElement(child, { + onContextMenu: (event: ReactMouseEvent) => { + child.props.onContextMenu?.(event); + if (event.defaultPrevented) return; + + event.preventDefault(); + setMainDrawerOpen(true); + }, + })} setReactionDrawerOpen(true)} showReactionItems={false} onSetEditing={onSetEditing} - senderId={senderId} /> @@ -298,16 +457,29 @@ export default function MessageContextMenu({ ); } + const child = renderMessage((event) => { + event.preventDefault(); + event.stopPropagation(); + event.currentTarget.dispatchEvent( + new MouseEvent("contextmenu", { + bubbles: true, + button: 2, + clientX: event.clientX, + clientY: event.clientY, + }), + ); + }); + return ( - + ); diff --git a/type-maps.yaml b/type-maps.yaml index dddd7a2..67aa0f3 100644 --- a/type-maps.yaml +++ b/type-maps.yaml @@ -73,6 +73,7 @@ type_maps: MessageChunk: 61 MessageGet: 143 MessagesGet: 62 + MessageDelete: 149 PushNotification: 63 ReadNotification: 64 GetNotifications: 65