diff --git a/web/src/ui/timeline/TimelineEvent.tsx b/web/src/ui/timeline/TimelineEvent.tsx index 54c0726..760428e 100644 --- a/web/src/ui/timeline/TimelineEvent.tsx +++ b/web/src/ui/timeline/TimelineEvent.tsx @@ -27,7 +27,7 @@ import ReadReceipts from "./ReadReceipts.tsx" import { ReplyIDBody } from "./ReplyBody.tsx" import URLPreviews from "./URLPreviews.tsx" import { ContentErrorBoundary, HiddenEvent, getBodyType, isSmallEvent } from "./content" -import { EventFullMenu, EventHoverMenu, getModalStyleFromMouse } from "./menu" +import { EventFixedMenu, EventFullMenu, EventHoverMenu, getModalStyleFromMouse } from "./menu" import ErrorIcon from "@/icons/error.svg?react" import PendingIcon from "@/icons/pending.svg?react" import SentIcon from "@/icons/sent.svg?react" @@ -98,6 +98,19 @@ const TimelineEvent = ({ evt, prevEvt, disableMenu, smallReplies }: TimelineEven />, }) } + const onClick = (mouseEvt: React.MouseEvent) => { + const targetElem = mouseEvt.target as HTMLElement + if ( + targetElem.tagName === "A" + || targetElem.tagName === "IMG" + ) { + return + } + mouseEvt.preventDefault() + openModal({ + content: , + }) + } const memberEvt = useRoomMember(client, roomCtx.store, evt.sender) const memberEvtContent = memberEvt?.content as MemberEventContent | undefined const BodyType = getBodyType(evt) @@ -175,11 +188,12 @@ const TimelineEvent = ({ evt, prevEvt, disableMenu, smallReplies }: TimelineEven data-event-id={evt.event_id} className={wrapperClassNames.join(" ")} onContextMenu={onContextMenu} + onClick={!disableMenu && isMobileDevice ? onClick : undefined} > {!disableMenu && !isMobileDevice &&
- +
} {replyAboveMessage} {renderAvatar &&
void } -export const EventHoverMenu = ({ evt, setForceOpen }: EventHoverMenuProps) => { - const elements = usePrimaryItems(use(ClientContext)!, useRoomContext(), evt, true, undefined, setForceOpen) +export const EventHoverMenu = ({ evt, roomCtx, setForceOpen }: EventHoverMenuProps) => { + const elements = usePrimaryItems(use(ClientContext)!, roomCtx, evt, true, false, undefined, setForceOpen) return
{elements}
} @@ -43,7 +44,7 @@ export const EventExtraMenu = ({ evt, roomCtx, style }: EventContextMenuProps) = export const EventFullMenu = ({ evt, roomCtx, style }: EventContextMenuProps) => { const client = use(ClientContext)! - const primary = usePrimaryItems(client, roomCtx, evt, false, style, undefined) + const primary = usePrimaryItems(client, roomCtx, evt, false, false, style, undefined) const secondary = useSecondaryItems(client, roomCtx, evt) return
{primary} @@ -51,3 +52,13 @@ export const EventFullMenu = ({ evt, roomCtx, style }: EventContextMenuProps) => {secondary}
} + +export const EventFixedMenu = ({ evt, roomCtx }: Omit) => { + const client = use(ClientContext)! + const primary = usePrimaryItems(client, roomCtx, evt, false, true, undefined, undefined) + const secondary = useSecondaryItems(client, roomCtx, evt, false) + return
+ {primary} + {secondary} +
+} diff --git a/web/src/ui/timeline/menu/index.css b/web/src/ui/timeline/menu/index.css index 9005345..da720c7 100644 --- a/web/src/ui/timeline/menu/index.css +++ b/web/src/ui/timeline/menu/index.css @@ -2,13 +2,9 @@ div.event-hover-menu { position: absolute; right: .5rem; top: -1.5rem; - background-color: var(--background-color); border: 1px solid var(--border-color); border-radius: .5rem; - display: flex; - gap: .25rem; padding: .125rem; - z-index: 1; > button { width: 2rem; @@ -16,6 +12,28 @@ div.event-hover-menu { } } +div.event-hover-menu, div.event-fixed-menu { + display: flex; + gap: .25rem; + background-color: var(--background-color); + z-index: 1; +} + +div.event-fixed-menu { + position: fixed; + inset: 0 0 auto; + height: 3rem; + padding: .25rem; + border-bottom: 1px solid var(--border-color); + justify-content: right; + flex-direction: row-reverse; + + > button { + width: 3rem; + height: 3rem; + } +} + div.event-context-menu { position: fixed; background-color: var(--background-color); diff --git a/web/src/ui/timeline/menu/index.ts b/web/src/ui/timeline/menu/index.ts index fc25eb0..4f89140 100644 --- a/web/src/ui/timeline/menu/index.ts +++ b/web/src/ui/timeline/menu/index.ts @@ -13,5 +13,5 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -export { EventExtraMenu, EventFullMenu, EventHoverMenu } from "./EventMenu.tsx" +export { EventExtraMenu, EventFixedMenu, EventFullMenu, EventHoverMenu } from "./EventMenu.tsx" export { getModalStyleFromMouse } from "./util.ts" diff --git a/web/src/ui/timeline/menu/usePrimaryItems.tsx b/web/src/ui/timeline/menu/usePrimaryItems.tsx index 1ebb0a9..d1f374c 100644 --- a/web/src/ui/timeline/menu/usePrimaryItems.tsx +++ b/web/src/ui/timeline/menu/usePrimaryItems.tsx @@ -37,9 +37,11 @@ export const usePrimaryItems = ( roomCtx: RoomContextData, evt: MemDBEvent, isHover: boolean, + isFixed: boolean, style?: CSSProperties, setForceOpen?: (forceOpen: boolean) => void, ) => { + const names = !isHover && !isFixed const closeModal = !isHover ? use(ModalCloseContext) : noop const openModal = use(ModalContext) @@ -108,11 +110,11 @@ export const usePrimaryItems = ( return <> {didFail && } {canReact && } {canSend && } {canEdit && } {isHover && } diff --git a/web/src/ui/timeline/menu/useSecondaryItems.tsx b/web/src/ui/timeline/menu/useSecondaryItems.tsx index f577aca..134d4f7 100644 --- a/web/src/ui/timeline/menu/useSecondaryItems.tsx +++ b/web/src/ui/timeline/menu/useSecondaryItems.tsx @@ -32,6 +32,7 @@ export const useSecondaryItems = ( client: Client, roomCtx: RoomContextData, evt: MemDBEvent, + names = true, ) => { const closeModal = use(ModalCloseContext) const openModal = use(ModalContext) @@ -102,20 +103,22 @@ export const useSecondaryItems = ( && (evt.sender === client.userID || ownPL >= redactOtherPL) return <> - + {ownPL >= pinPL && (pins.includes(evt.event_id) ? : )} - + {canRedact && } + >{names && "Remove"}} }