forked from Mirrors/gomuks
web/composer: store drafts in localStorage
This commit is contained in:
parent
3536aa1569
commit
ce43c6946c
1 changed files with 29 additions and 10 deletions
|
@ -13,9 +13,9 @@
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
import React, { use, useCallback, useRef, useState } from "react"
|
import React, { use, useCallback, useLayoutEffect, useRef, useState } from "react"
|
||||||
import { RoomStateStore } from "@/api/statestore"
|
import { RoomStateStore } from "@/api/statestore"
|
||||||
import { MemDBEvent, Mentions } from "@/api/types"
|
import { MemDBEvent, Mentions, RoomID } from "@/api/types"
|
||||||
import { ClientContext } from "./ClientContext.ts"
|
import { ClientContext } from "./ClientContext.ts"
|
||||||
import { ReplyBody } from "./timeline/ReplyBody.tsx"
|
import { ReplyBody } from "./timeline/ReplyBody.tsx"
|
||||||
import "./MessageComposer.css"
|
import "./MessageComposer.css"
|
||||||
|
@ -27,18 +27,34 @@ interface MessageComposerProps {
|
||||||
closeReply: () => void
|
closeReply: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const draftStore = {
|
||||||
|
get: (roomID: RoomID) => localStorage.getItem(`draft-${roomID}`) ?? "",
|
||||||
|
set: (roomID: RoomID, text: string) => localStorage.setItem(`draft-${roomID}`, text),
|
||||||
|
clear: (roomID: RoomID)=> localStorage.removeItem(`draft-${roomID}`),
|
||||||
|
}
|
||||||
|
|
||||||
const MessageComposer = ({ room, replyTo, setTextRows, closeReply }: MessageComposerProps) => {
|
const MessageComposer = ({ room, replyTo, setTextRows, closeReply }: MessageComposerProps) => {
|
||||||
const client = use(ClientContext)!
|
const client = use(ClientContext)!
|
||||||
const [text, setText] = useState("")
|
const [text, setText] = useState("")
|
||||||
const textRows = useRef(1)
|
const textRows = useRef(1)
|
||||||
|
const fullSetText = useCallback((text: string, setDraft: boolean) => {
|
||||||
|
setText(text)
|
||||||
|
textRows.current = text === "" ? 1 : text.split("\n").length
|
||||||
|
setTextRows(textRows.current)
|
||||||
|
if (setDraft) {
|
||||||
|
if (text === "") {
|
||||||
|
draftStore.clear(room.roomID)
|
||||||
|
} else {
|
||||||
|
draftStore.set(room.roomID, text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [setTextRows, room.roomID])
|
||||||
const sendMessage = useCallback((evt: React.FormEvent) => {
|
const sendMessage = useCallback((evt: React.FormEvent) => {
|
||||||
evt.preventDefault()
|
evt.preventDefault()
|
||||||
if (text === "") {
|
if (text === "") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setText("")
|
fullSetText("", true)
|
||||||
setTextRows(1)
|
|
||||||
textRows.current = 1
|
|
||||||
closeReply()
|
closeReply()
|
||||||
const room_id = room.roomID
|
const room_id = room.roomID
|
||||||
const mentions: Mentions = {
|
const mentions: Mentions = {
|
||||||
|
@ -50,17 +66,20 @@ const MessageComposer = ({ room, replyTo, setTextRows, closeReply }: MessageComp
|
||||||
}
|
}
|
||||||
client.sendMessage({ room_id, text, reply_to: replyTo?.event_id, mentions })
|
client.sendMessage({ room_id, text, reply_to: replyTo?.event_id, mentions })
|
||||||
.catch(err => window.alert("Failed to send message: " + err))
|
.catch(err => window.alert("Failed to send message: " + err))
|
||||||
}, [setTextRows, closeReply, replyTo, text, room, client])
|
}, [fullSetText, closeReply, replyTo, text, room, client])
|
||||||
const onKeyDown = useCallback((evt: React.KeyboardEvent) => {
|
const onKeyDown = useCallback((evt: React.KeyboardEvent) => {
|
||||||
if (evt.key === "Enter" && !evt.shiftKey) {
|
if (evt.key === "Enter" && !evt.shiftKey) {
|
||||||
sendMessage(evt)
|
sendMessage(evt)
|
||||||
}
|
}
|
||||||
}, [sendMessage])
|
}, [sendMessage])
|
||||||
const onChange = useCallback((evt: React.ChangeEvent<HTMLTextAreaElement>) => {
|
const onChange = useCallback((evt: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
setText(evt.target.value)
|
fullSetText(evt.target.value, true)
|
||||||
textRows.current = evt.target.value.split("\n").length
|
}, [fullSetText])
|
||||||
setTextRows(textRows.current)
|
// To ensure the cursor jumps to the end, do this in an effect rather than as the initial value of useState
|
||||||
}, [setTextRows])
|
// To try to avoid the input bar flashing, use useLayoutEffect instead of useEffect
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
fullSetText(draftStore.get(room.roomID), false)
|
||||||
|
}, [room.roomID, fullSetText])
|
||||||
return <div className="message-composer">
|
return <div className="message-composer">
|
||||||
{replyTo && <ReplyBody room={room} event={replyTo} onClose={closeReply}/>}
|
{replyTo && <ReplyBody room={room} event={replyTo} onClose={closeReply}/>}
|
||||||
<div className="input-area">
|
<div className="input-area">
|
||||||
|
|
Loading…
Add table
Reference in a new issue