diff --git a/src/element/DM.css b/src/element/DM.css index cb3e343f..bd883597 100644 --- a/src/element/DM.css +++ b/src/element/DM.css @@ -7,6 +7,8 @@ min-width: 100px; max-width: 90%; overflow: hidden; + min-height: 40px; + white-space: pre-wrap; } .dm > div:first-child { diff --git a/src/element/DM.tsx b/src/element/DM.tsx index 328d13ed..70fd4962 100644 --- a/src/element/DM.tsx +++ b/src/element/DM.tsx @@ -1,6 +1,7 @@ import "./DM.css"; import { useEffect, useState } from "react"; import { useSelector } from "react-redux"; +import { useInView } from 'react-intersection-observer'; // @ts-ignore import useEventPublisher from "../feed/EventPublisher"; @@ -19,6 +20,8 @@ export default function DM(props: DMProps) { const pubKey = useSelector(s => s.login.publicKey); const publisher = useEventPublisher(); const [content, setContent] = useState("Loading..."); + const [decrypted, setDecrypted] = useState(false); + const { ref, inView, entry } = useInView(); async function decrypt() { let e = Event.FromObject(props.data); @@ -27,11 +30,14 @@ export default function DM(props: DMProps) { } useEffect(() => { - decrypt().catch(console.error); - }, [props.data]); + if (!decrypted && inView) { + setDecrypted(true); + decrypt().catch(console.error); + } + }, [inView, props.data]); return ( -
+
diff --git a/src/pages/ChatPage.css b/src/pages/ChatPage.css index 2e49d634..f5ea6be0 100644 --- a/src/pages/ChatPage.css +++ b/src/pages/ChatPage.css @@ -8,6 +8,7 @@ display: flex; flex-direction: column; margin-bottom: 10px; + scroll-padding-bottom: 40px; } .write-dm { diff --git a/src/pages/ChatPage.tsx b/src/pages/ChatPage.tsx index 397e96ff..11067bda 100644 --- a/src/pages/ChatPage.tsx +++ b/src/pages/ChatPage.tsx @@ -1,7 +1,8 @@ import "./ChatPage.css"; -import { useMemo, useState } from "react"; +import { KeyboardEvent, useEffect, useMemo, useRef, useState } from "react"; import { useSelector } from "react-redux"; import { useParams } from "react-router-dom"; +import { useInView } from 'react-intersection-observer'; // @ts-ignore import ProfileImage from "../element/ProfileImage"; @@ -23,6 +24,8 @@ export default function ChatPage() { const id = bech32ToHex(params.id); const dms = useSelector(s => filterDms(s.login.dms, s.login.publicKey)); const [content, setContent] = useState(); + const { ref, inView, entry } = useInView(); + const dmListRef = useRef(null); function filterDms(dms: RawEvent[], myPubkey: string) { return dms.filter(a => { @@ -39,24 +42,38 @@ export default function ChatPage() { return [...dms].sort((a, b) => a.created_at - b.created_at) }, [dms]); + useEffect(() => { + if (inView && dmListRef.current) { + dmListRef.current.scroll(0, dmListRef.current.scrollHeight); + } + }, [inView, dmListRef, sortedDms]); + async function sendDm() { let ev = await publisher.sendDm(content, id); console.debug(ev); publisher.broadcast(ev); - setContent(undefined); + setContent(""); + } + + async function onEnter(e: KeyboardEvent) { + let isEnter = e.code === "Enter"; + if(isEnter && !e.shiftKey) { + await sendDm(); + } } return ( <> -
+
- {sortedDms.slice(-10).map(a => )} + {sortedDms.map(a => )} +
- +
sendDm()}>Send
diff --git a/src/state/Login.js b/src/state/Login.js index c616cb11..3640b705 100644 --- a/src/state/Login.js +++ b/src/state/Login.js @@ -160,6 +160,7 @@ const LoginSlice = createSlice({ state.notifications = []; state.loggedOut = true; state.readNotifications = 0; + state.dms = []; }, markNotificationsRead: (state) => { state.readNotifications = new Date().getTime();