diff --git a/web/src/ui/composer/MessageComposer.tsx b/web/src/ui/composer/MessageComposer.tsx index 87749c6..67152d9 100644 --- a/web/src/ui/composer/MessageComposer.tsx +++ b/web/src/ui/composer/MessageComposer.tsx @@ -55,6 +55,7 @@ export interface ComposerState { replyTo: EventID | null silentReply: boolean explicitReplyInThread: boolean + startNewThread: boolean uninited?: boolean } @@ -67,6 +68,7 @@ const emptyComposer: ComposerState = { location: null, silentReply: false, explicitReplyInThread: false, + startNewThread: false, } const uninitedComposer: ComposerState = { ...emptyComposer, uninited: true } const composerReducer = ( @@ -116,7 +118,7 @@ const MessageComposer = () => { document.execCommand("insertText", false, text) }, []) roomCtx.setReplyTo = useCallback((evt: EventID | null) => { - setState({ replyTo: evt, silentReply: false, explicitReplyInThread: false }) + setState({ replyTo: evt, silentReply: false, explicitReplyInThread: false, startNewThread: false }) textInput.current?.focus() }, []) const setSilentReply = useCallback((newVal: boolean | React.MouseEvent) => { @@ -135,6 +137,14 @@ const MessageComposer = () => { setState(state => ({ explicitReplyInThread: !state.explicitReplyInThread })) } }, []) + const setStartNewThread = useCallback((newVal: boolean | React.MouseEvent) => { + if (typeof newVal === "boolean") { + setState({ startNewThread: newVal }) + } else { + newVal.stopPropagation() + setState(state => ({ startNewThread: !state.startNewThread })) + } + }, []) roomCtx.setEditing = useCallback((evt: MemDBEvent | null) => { if (evt === null) { rawSetEditing(null) @@ -160,6 +170,7 @@ const MessageComposer = () => { replyTo: null, silentReply: false, explicitReplyInThread: false, + startNewThread: false, }) textInput.current?.focus() }, [room.roomID]) @@ -204,6 +215,10 @@ const MessageComposer = () => { relates_to.rel_type = "m.thread" relates_to.event_id = replyToEvt.content?.["m.relates_to"].event_id relates_to.is_falling_back = !state.explicitReplyInThread + } else if (state.startNewThread) { + relates_to.rel_type = "m.thread" + relates_to.event_id = replyToEvt.event_id + relates_to.is_falling_back = true } } let base_content: MessageEventContent | undefined @@ -580,6 +595,8 @@ const MessageComposer = () => { onSetSilent={setSilentReply} isExplicitInThread={state.explicitReplyInThread} onSetExplicitInThread={setExplicitReplyInThread} + startNewThread={state.startNewThread} + onSetStartNewThread={setStartNewThread} />} {editing && void isExplicitInThread?: boolean onSetExplicitInThread?: (evt: React.MouseEvent) => void + startNewThread?: boolean + onSetStartNewThread?: (evt: React.MouseEvent) => void } interface ReplyIDBodyProps { @@ -83,7 +85,10 @@ const onClickReply = (evt: React.MouseEvent) => { } export const ReplyBody = ({ - room, event, onClose, isThread, isEditing, isSilent, onSetSilent, isExplicitInThread, onSetExplicitInThread, small, + room, event, onClose, isThread, isEditing, small, + isSilent, onSetSilent, + isExplicitInThread, onSetExplicitInThread, + startNewThread, onSetStartNewThread, }: ReplyBodyProps) => { const client = use(ClientContext) const memberEvt = useRoomMember(client, room, event.sender) @@ -164,6 +169,16 @@ export const ReplyBody = ({ > {isExplicitInThread ? : } } + {!isThread && onSetStartNewThread && + {startNewThread ? : } + } {onClose && } }