mirror of
https://github.com/tulir/gomuks.git
synced 2025-04-20 10:33:41 -05:00
web/timeline: allow jumping to reply if it's loaded in the timeline
This commit is contained in:
parent
e6121149b3
commit
3fdaf8ae4e
4 changed files with 40 additions and 4 deletions
|
@ -180,7 +180,10 @@ const MessageComposer = ({ room, scrollToBottomRef, setReplyToRef }: MessageComp
|
||||||
}, [room, state])
|
}, [room, state])
|
||||||
const openFilePicker = useCallback(() => fileInput.current!.click(), [])
|
const openFilePicker = useCallback(() => fileInput.current!.click(), [])
|
||||||
const clearMedia = useCallback(() => setState({ media: null }), [])
|
const clearMedia = useCallback(() => setState({ media: null }), [])
|
||||||
const closeReply = useCallback(() => setState({ replyTo: null }), [])
|
const closeReply = useCallback((evt: React.MouseEvent) => {
|
||||||
|
evt.stopPropagation()
|
||||||
|
setState({ replyTo: null })
|
||||||
|
}, [])
|
||||||
return <div className="message-composer">
|
return <div className="message-composer">
|
||||||
{replyToEvt && <ReplyBody room={room} event={replyToEvt} onClose={closeReply}/>}
|
{replyToEvt && <ReplyBody room={room} event={replyToEvt} onClose={closeReply}/>}
|
||||||
{loadingMedia && <div className="composer-media"><ScaleLoader/></div>}
|
{loadingMedia && <div className="composer-media"><ScaleLoader/></div>}
|
||||||
|
|
|
@ -25,7 +25,7 @@ import "./ReplyBody.css"
|
||||||
interface ReplyBodyProps {
|
interface ReplyBodyProps {
|
||||||
room: RoomStateStore
|
room: RoomStateStore
|
||||||
event: MemDBEvent
|
event: MemDBEvent
|
||||||
onClose?: () => void
|
onClose?: (evt: React.MouseEvent) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ReplyIDBodyProps {
|
interface ReplyIDBodyProps {
|
||||||
|
@ -45,10 +45,31 @@ export const ReplyIDBody = ({ room, eventID }: ReplyIDBodyProps) => {
|
||||||
return <ReplyBody room={room} event={event}/>
|
return <ReplyBody room={room} event={event}/>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onClickReply = (evt: React.MouseEvent) => {
|
||||||
|
const targetEvt = document.querySelector(`div[data-event-id="${evt.currentTarget.getAttribute("data-reply-to")}"]`)
|
||||||
|
if (targetEvt) {
|
||||||
|
targetEvt.scrollIntoView({
|
||||||
|
block: "center",
|
||||||
|
})
|
||||||
|
targetEvt.classList.add("jump-highlight")
|
||||||
|
setTimeout(() => {
|
||||||
|
targetEvt.classList.add("jump-highlight-fadeout")
|
||||||
|
targetEvt.classList.remove("jump-highlight")
|
||||||
|
setTimeout(() => {
|
||||||
|
targetEvt.classList.remove("jump-highlight-fadeout")
|
||||||
|
}, 1500)
|
||||||
|
}, 3000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const ReplyBody = ({ room, event, onClose }: ReplyBodyProps) => {
|
export const ReplyBody = ({ room, event, onClose }: ReplyBodyProps) => {
|
||||||
const memberEvt = room.getStateEvent("m.room.member", event.sender)
|
const memberEvt = room.getStateEvent("m.room.member", event.sender)
|
||||||
const memberEvtContent = memberEvt?.content as MemberEventContent | undefined
|
const memberEvtContent = memberEvt?.content as MemberEventContent | undefined
|
||||||
return <blockquote className={`reply-body ${onClose ? "composer" : ""}`}>
|
return <blockquote
|
||||||
|
data-reply-to={event.event_id}
|
||||||
|
className={`reply-body ${onClose ? "composer" : ""}`}
|
||||||
|
onClick={onClickReply}
|
||||||
|
>
|
||||||
<div className="reply-sender">
|
<div className="reply-sender">
|
||||||
<div className="sender-avatar" title={event.sender}>
|
<div className="sender-avatar" title={event.sender}>
|
||||||
<img
|
<img
|
||||||
|
|
|
@ -14,12 +14,24 @@ div.timeline-event {
|
||||||
background-color: rgba(255, 255, 0, .1);
|
background-color: rgba(255, 255, 0, .1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.jump-highlight {
|
||||||
|
background-color: rgba(0, 255, 0, .2);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.jump-highlight-fadeout {
|
||||||
|
transition: background-color 1s;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
|
|
||||||
&.highlight {
|
&.highlight {
|
||||||
background-color: #eec;
|
background-color: #eec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.jump-highlight {
|
||||||
|
background-color: #cec;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> div.sender-avatar {
|
> div.sender-avatar {
|
||||||
|
|
|
@ -135,7 +135,7 @@ const TimelineEvent = ({ room, evt, prevEvt, setReplyToRef }: TimelineEventProps
|
||||||
const shortTime = formatShortTime(eventTS)
|
const shortTime = formatShortTime(eventTS)
|
||||||
const editTime = editEventTS ? `Edited at ${fullTimeFormatter.format(editEventTS)}` : null
|
const editTime = editEventTS ? `Edited at ${fullTimeFormatter.format(editEventTS)}` : null
|
||||||
const replyTo = (evt.orig_content ?? evt.content)["m.relates_to"]?.["m.in_reply_to"]?.event_id
|
const replyTo = (evt.orig_content ?? evt.content)["m.relates_to"]?.["m.in_reply_to"]?.event_id
|
||||||
const mainEvent = <div className={wrapperClassNames.join(" ")}>
|
const mainEvent = <div data-event-id={evt.event_id} className={wrapperClassNames.join(" ")}>
|
||||||
<div className="sender-avatar" title={evt.sender}>
|
<div className="sender-avatar" title={evt.sender}>
|
||||||
<img
|
<img
|
||||||
className={`${smallAvatar ? "small" : ""} avatar`}
|
className={`${smallAvatar ? "small" : ""} avatar`}
|
||||||
|
|
Loading…
Add table
Reference in a new issue