From 6736bdf3591b58b1f8e943b63287da9bd890ce3f Mon Sep 17 00:00:00 2001 From: Kieran Date: Tue, 17 Jan 2023 10:47:00 +0000 Subject: [PATCH] Unread DMS count --- src/element/DM.tsx | 11 +++--- src/element/Nip5Service.tsx | 2 -- src/pages/Layout.tsx | 10 ++++-- src/pages/MessagesPage.tsx | 71 ++++++++++++++++++++++++++++++------- 4 files changed, 73 insertions(+), 21 deletions(-) diff --git a/src/element/DM.tsx b/src/element/DM.tsx index c7766f1d..96d45ee6 100644 --- a/src/element/DM.tsx +++ b/src/element/DM.tsx @@ -3,14 +3,11 @@ import { useEffect, useState } from "react"; import { useSelector } from "react-redux"; import { useInView } from 'react-intersection-observer'; -// @ts-ignore import useEventPublisher from "../feed/EventPublisher"; -// @ts-ignore import Event from "../nostr/Event"; -// @ts-ignore import NoteTime from "./NoteTime"; -// @ts-ignore import Text from "./Text"; +import { lastReadDm, setLastReadDm } from "../pages/MessagesPage"; export type DMProps = { data: any @@ -22,9 +19,13 @@ export default function DM(props: DMProps) { const [content, setContent] = useState("Loading..."); const [decrypted, setDecrypted] = useState(false); const { ref, inView, entry } = useInView(); + const isMe = props.data.pubkey === pubKey; async function decrypt() { let e = new Event(props.data); + if (!isMe) { + setLastReadDm(e.PubKey); + } let decrypted = await publisher.decryptDm(e); setContent(decrypted || ""); } @@ -37,7 +38,7 @@ export default function DM(props: DMProps) { }, [inView, props.data]); return ( -
+
diff --git a/src/element/Nip5Service.tsx b/src/element/Nip5Service.tsx index 9a64eef3..670126bc 100644 --- a/src/element/Nip5Service.tsx +++ b/src/element/Nip5Service.tsx @@ -11,9 +11,7 @@ import { CheckRegisterResponse } from "../nip05/ServiceProvider"; import AsyncButton from "./AsyncButton"; -// @ts-ignore import LNURLTip from "./LNURLTip"; -// @ts-ignore import Copy from "./Copy"; import useProfile from "../feed/ProfileFeed"; import useEventPublisher from "../feed/EventPublisher"; diff --git a/src/pages/Layout.tsx b/src/pages/Layout.tsx index ee27a5d3..cedeb68b 100644 --- a/src/pages/Layout.tsx +++ b/src/pages/Layout.tsx @@ -10,8 +10,9 @@ import ProfileImage from "../element/ProfileImage"; import { init } from "../state/Login"; import useLoginFeed from "../feed/LoginFeed"; import { RootState } from "../state/Store"; -import { HexKey, TaggedRawEvent } from "../nostr"; +import { HexKey, RawEvent, TaggedRawEvent } from "../nostr"; import { RelaySettings } from "../nostr/Connection"; +import { totalUnread } from "./MessagesPage"; export default function Layout() { const dispatch = useDispatch(); @@ -21,6 +22,7 @@ export default function Layout() { const relays = useSelector>(s => s.login.relays); const notifications = useSelector(s => s.login.notifications); const readNotifications = useSelector(s => s.login.readNotifications); + const dms = useSelector(s => s.login.dms); useLoginFeed(); useEffect(() => { @@ -56,11 +58,15 @@ export default function Layout() { function accountHeader() { const unreadNotifications = notifications?.filter(a => (a.created_at * 1000) > readNotifications).length; + const unreadDms = key ? totalUnread(dms, key) : 0; return ( <> -
navigate("/messages")}> +
navigate("/messages")}>
+ {unreadDms > 0 && ( + {unreadDms > 100 ? ">99" : unreadDms} + )}
goToNotifications(e)}>
diff --git a/src/pages/MessagesPage.tsx b/src/pages/MessagesPage.tsx index fc1b14a7..6681e704 100644 --- a/src/pages/MessagesPage.tsx +++ b/src/pages/MessagesPage.tsx @@ -1,27 +1,31 @@ import { useMemo } from "react"; import { useSelector } from "react-redux" -import { RawEvent } from "../nostr"; - -// @ts-ignore +import { HexKey, RawEvent } from "../nostr"; import ProfileImage from "../element/ProfileImage"; -// @ts-ignore import { hexToBech32 } from "../Util"; +type DmChat = { + pubkey: HexKey, + lastRead: number, + unreadMessages: number, + newestMessage: number +} + export default function MessagesPage() { - const pubKey = useSelector(s => s.login.publicKey); + const myPubKey = useSelector(s => s.login.publicKey); const dms = useSelector(s => s.login.dms); - const pubKeys = useMemo(() => { - return Array.from(new Set(dms.map(a => [a.pubkey, ...a.tags.filter(b => b[0] === "p").map(b => b[1])]).flat())); + const chats = useMemo(() => { + return extractChats(dms, myPubKey); }, [dms]); - function person(pubkey: string) { + function person(chat: DmChat) { return ( -
- +
+ - {dms?.filter(a => a.pubkey === pubkey && a.pubkey !== pubKey).length} + {chat.unreadMessages}
) @@ -30,7 +34,50 @@ export default function MessagesPage() { return ( <>

Messages

- {pubKeys.map(person)} + {chats.sort((a, b) => b.newestMessage - a.newestMessage).map(person)} ) +} + +export function lastReadDm(pk: HexKey) { + let k = `dm:seen:${pk}`; + return parseInt(window.localStorage.getItem(k) ?? "0"); +} + +export function setLastReadDm(pk: HexKey) { + const now = Math.floor(new Date().getTime() / 1000); + let current = lastReadDm(pk); + if (current >= now) { + return; + } + + let k = `dm:seen:${pk}`; + window.localStorage.setItem(k, now.toString()); +} + +export function totalUnread(dms: RawEvent[], myPubKey: HexKey) { + return extractChats(dms, myPubKey).reduce((acc, v) => acc += v.unreadMessages, 0); +} + +function unreadDms(dms: RawEvent[], myPubKey: HexKey, pk: HexKey) { + let lastRead = lastReadDm(pk); + return dms?.filter(a => a.pubkey === pk && a.pubkey !== myPubKey && a.created_at >= lastRead).length; +} + +function newestMessage(dms: RawEvent[], myPubKey: HexKey, pk: HexKey) { + return dms.filter(a => a.pubkey === pk && a.pubkey !== myPubKey).reduce((acc, v) => acc = v.created_at > acc ? v.created_at : acc, 0); +} + + +export function extractChats(dms: RawEvent[], myPubKey: HexKey) { + const keys = dms.map(a => [a.pubkey, ...a.tags.filter(b => b[0] === "p").map(b => b[1])]).flat(); + const filteredKeys = Array.from(new Set(keys)); + return filteredKeys.map(a => { + return { + pubkey: a, + lastRead: lastReadDm(a), + unreadMessages: unreadDms(dms, myPubKey, a), + newestMessage: newestMessage(dms, myPubKey, a) + } as DmChat; + }) } \ No newline at end of file