diff --git a/.changeset/improved_message_history_ui.md b/.changeset/improved_message_history_ui.md new file mode 100644 index 00000000..da94e930 --- /dev/null +++ b/.changeset/improved_message_history_ui.md @@ -0,0 +1,5 @@ +--- +default: minor +--- + +Added hover menu inside Message Version Pop-out. diff --git a/src/app/components/event-history/EventHistory.css.ts b/src/app/components/event-history/EventHistory.css.ts index e50d71c3..29946b79 100644 --- a/src/app/components/event-history/EventHistory.css.ts +++ b/src/app/components/event-history/EventHistory.css.ts @@ -1,5 +1,5 @@ import { style } from '@vanilla-extract/css'; -import { DefaultReset, color, config } from 'folds'; +import { DefaultReset, color, config, toRem } from 'folds'; export const EventHistory = style([ DefaultReset, @@ -22,6 +22,7 @@ export const Content = style({ export const EventItem = style({ padding: `${config.space.S200} ${config.space.S200}`, height: 'unset', + width: '100%', borderRadius: '5px', border: '2px hidden', backgroundColor: 'inherit', @@ -31,3 +32,25 @@ export const EventItem = style({ }, }, }); +export const MessageOptionsBase = style([ + DefaultReset, + { + position: 'absolute', + top: toRem(-30), + right: 0, + zIndex: 1, + }, +]); +export const MessageOptionsBar = style([ + DefaultReset, + { + padding: config.space.S100, + }, +]); +export const MenuOptions = style({ + position: 'absolute', + right: '0', + top: '0', + display: 'flex', + transform: 'translateY(-75%)', +}); diff --git a/src/app/components/event-history/EventHistory.tsx b/src/app/components/event-history/EventHistory.tsx index ffdefa1e..2bc87a23 100644 --- a/src/app/components/event-history/EventHistory.tsx +++ b/src/app/components/event-history/EventHistory.tsx @@ -6,6 +6,7 @@ import { Icon, IconButton, Icons, + Menu, MenuItem, Scroll, Text, @@ -13,7 +14,7 @@ import { color, config, } from 'folds'; -import { MatrixEvent, Room } from '$types/matrix-sdk'; +import { IContent, MatrixEvent, Room } from '$types/matrix-sdk'; import { getMemberDisplayName } from '$utils/room'; import { getMxIdLocalPart } from '$utils/matrix'; import { useMatrixClient } from '$hooks/useMatrixClient'; @@ -21,17 +22,23 @@ import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; import { useOpenUserRoomProfile } from '$state/hooks/userRoomProfile'; import { useSpaceOptionally } from '$hooks/useSpace'; import { getMouseEventCords } from '$utils/dom'; -import { useAtomValue } from 'jotai'; +import { useAtomValue, useSetAtom } from 'jotai'; import { nicknamesAtom } from '$state/nicknames'; import { UserAvatar } from '$components/user-avatar'; import { RenderBody, Time } from '$components/message'; import { useSetting } from '$state/hooks/settings'; import { settingsAtom } from '$state/settings'; -import { useMemo } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import { getReactCustomHtmlParser, LINKIFY_OPTS } from '$plugins/react-custom-html-parser'; import { Opts as LinkifyOpts } from 'linkifyjs'; import { HTMLReactParserOptions } from 'html-react-parser'; import { useSpoilerClickHandler } from '$hooks/useSpoilerClickHandler'; +import { modalAtom, ModalType } from '$state/modal'; +import { roomIdToReplyDraftAtomFamily } from '$state/room/roomInputDrafts'; +import { useRoomPermissions } from '$hooks/useRoomPermissions'; +import { useRoomCreators } from '$hooks/useRoomCreators'; +import { usePowerLevelsContext } from '$hooks/usePowerLevels'; +import { MessageEvent } from '$types/matrix/room'; import * as css from './EventHistory.css'; export type EventHistoryProps = { @@ -71,6 +78,139 @@ export const EventHistory = as<'div', EventHistoryProps>( }), [linkifyOpts, mEvents, mx, spoilerClickHandler, useAuthentication] ); + const powerLevels = usePowerLevelsContext(); + const creators = useRoomCreators(room); + const permissions = useRoomPermissions(creators, powerLevels); + const canRedact = permissions.action('redact', mx.getSafeUserId()); + const canDeleteOwn = permissions.event(MessageEvent.RoomRedaction, mx.getSafeUserId()); + const canDelete = canRedact || (canDeleteOwn && mEvents[0].getSender() === mx.getUserId()); + + const setReplyDraft = useSetAtom(roomIdToReplyDraftAtomFamily(room.roomId)); + const triggerReply = useCallback( + (replyId: string, startThread = false) => { + const replyEvt = room.findEventById(replyId); + if (!replyEvt) return; + const content: IContent = replyEvt.getOriginalContent(); + const body = content?.['m.new_content']?.body ?? content?.body ?? ''; + const formattedBody = + content?.['m.new_content']?.formatted_body ?? content?.formatted_body ?? ''; + const { 'm.relates_to': relation } = startThread + ? { 'm.relates_to': { rel_type: 'm.thread', event_id: replyId } } + : replyEvt.getWireContent(); + const senderId = replyEvt.getSender(); + if (senderId) { + if (typeof body === 'string') { + setReplyDraft({ + userId: senderId, + eventId: replyId, + body, + formattedBody, + relation, + }); + } else { + setReplyDraft({ + userId: senderId, + eventId: replyId, + body: '', + formattedBody: '', + relation, + }); + } + } + }, + [room, setReplyDraft] + ); + + function MenuOptions({ mEvent }: { mEvent: MatrixEvent }) { + const setModal = useSetAtom(modalAtom); + return ( +
+ ); + } + + function EventItem({ mEvent, EventContent }: { mEvent: MatrixEvent; EventContent: IContent }) { + const [isHovered, setIsHovered] = useState(false); + return ( +