2023-01-12 09:48:39 +00:00
|
|
|
import { useMemo } from "react";
|
2023-02-08 21:10:26 +00:00
|
|
|
import { FormattedMessage } from "react-intl";
|
2023-02-07 20:04:50 +00:00
|
|
|
import { useDispatch, useSelector } from "react-redux";
|
2023-01-12 09:48:39 +00:00
|
|
|
|
2023-01-20 11:11:50 +00:00
|
|
|
import { HexKey, RawEvent } from "Nostr";
|
|
|
|
import UnreadCount from "Element/UnreadCount";
|
|
|
|
import ProfileImage from "Element/ProfileImage";
|
2023-01-12 09:48:39 +00:00
|
|
|
import { hexToBech32 } from "../Util";
|
2023-01-20 11:11:50 +00:00
|
|
|
import { incDmInteraction } from "State/Login";
|
|
|
|
import { RootState } from "State/Store";
|
|
|
|
import NoteToSelf from "Element/NoteToSelf";
|
2023-01-26 11:34:18 +00:00
|
|
|
import useModeration from "Hooks/useModeration";
|
2023-01-12 09:48:39 +00:00
|
|
|
|
2023-02-08 21:10:26 +00:00
|
|
|
import messages from "./messages";
|
|
|
|
|
2023-01-17 10:47:00 +00:00
|
|
|
type DmChat = {
|
2023-02-07 20:04:50 +00:00
|
|
|
pubkey: HexKey;
|
|
|
|
unreadMessages: number;
|
|
|
|
newestMessage: number;
|
|
|
|
};
|
2023-01-17 10:47:00 +00:00
|
|
|
|
2023-01-12 09:48:39 +00:00
|
|
|
export default function MessagesPage() {
|
2023-02-07 20:04:50 +00:00
|
|
|
const dispatch = useDispatch();
|
2023-02-09 12:26:54 +00:00
|
|
|
const myPubKey = useSelector<RootState, HexKey | undefined>(s => s.login.publicKey);
|
|
|
|
const dms = useSelector<RootState, RawEvent[]>(s => s.login.dms);
|
|
|
|
const dmInteraction = useSelector<RootState, number>(s => s.login.dmInteraction);
|
2023-02-07 20:04:50 +00:00
|
|
|
const { isMuted } = useModeration();
|
|
|
|
|
|
|
|
const chats = useMemo(() => {
|
|
|
|
return extractChats(
|
2023-02-09 12:26:54 +00:00
|
|
|
dms.filter(a => !isMuted(a.pubkey)),
|
2023-02-07 19:47:57 +00:00
|
|
|
myPubKey ?? ""
|
2023-02-07 20:04:50 +00:00
|
|
|
);
|
|
|
|
}, [dms, myPubKey, dmInteraction]);
|
|
|
|
|
|
|
|
function noteToSelf(chat: DmChat) {
|
2023-01-12 09:48:39 +00:00
|
|
|
return (
|
2023-02-07 20:04:50 +00:00
|
|
|
<div className="flex mb10" key={chat.pubkey}>
|
|
|
|
<NoteToSelf
|
|
|
|
clickable={true}
|
|
|
|
className="f-grow"
|
|
|
|
link={`/messages/${hexToBech32("npub", chat.pubkey)}`}
|
|
|
|
pubkey={chat.pubkey}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function person(chat: DmChat) {
|
|
|
|
if (chat.pubkey === myPubKey) return noteToSelf(chat);
|
|
|
|
return (
|
|
|
|
<div className="flex mb10" key={chat.pubkey}>
|
2023-02-09 12:26:54 +00:00
|
|
|
<ProfileImage pubkey={chat.pubkey} className="f-grow" link={`/messages/${hexToBech32("npub", chat.pubkey)}`} />
|
2023-02-07 20:04:50 +00:00
|
|
|
<UnreadCount unread={chat.unreadMessages} />
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function markAllRead() {
|
2023-02-07 19:47:57 +00:00
|
|
|
for (const c of chats) {
|
2023-02-07 20:04:50 +00:00
|
|
|
setLastReadDm(c.pubkey);
|
|
|
|
}
|
|
|
|
dispatch(incDmInteraction());
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="main-content">
|
|
|
|
<div className="flex">
|
2023-02-08 21:10:26 +00:00
|
|
|
<h3 className="f-grow">
|
|
|
|
<FormattedMessage {...messages.Messages} />
|
|
|
|
</h3>
|
2023-02-07 20:04:50 +00:00
|
|
|
<button type="button" onClick={() => markAllRead()}>
|
2023-02-08 21:10:26 +00:00
|
|
|
<FormattedMessage {...messages.MarkAllRead} />
|
2023-02-07 20:04:50 +00:00
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
{chats
|
|
|
|
.sort((a, b) => {
|
2023-02-09 12:26:54 +00:00
|
|
|
return a.pubkey === myPubKey ? -1 : b.pubkey === myPubKey ? 1 : b.newestMessage - a.newestMessage;
|
2023-02-07 20:04:50 +00:00
|
|
|
})
|
|
|
|
.map(person)}
|
|
|
|
</div>
|
|
|
|
);
|
2023-01-17 10:47:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export function lastReadDm(pk: HexKey) {
|
2023-02-07 19:47:57 +00:00
|
|
|
const k = `dm:seen:${pk}`;
|
2023-02-07 20:04:50 +00:00
|
|
|
return parseInt(window.localStorage.getItem(k) ?? "0");
|
2023-01-17 10:47:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export function setLastReadDm(pk: HexKey) {
|
2023-02-07 20:04:50 +00:00
|
|
|
const now = Math.floor(new Date().getTime() / 1000);
|
2023-02-07 19:47:57 +00:00
|
|
|
const current = lastReadDm(pk);
|
2023-02-07 20:04:50 +00:00
|
|
|
if (current >= now) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-02-07 19:47:57 +00:00
|
|
|
const k = `dm:seen:${pk}`;
|
2023-02-07 20:04:50 +00:00
|
|
|
window.localStorage.setItem(k, now.toString());
|
2023-01-17 10:47:00 +00:00
|
|
|
}
|
|
|
|
|
2023-01-18 18:53:34 +00:00
|
|
|
export function dmTo(e: RawEvent) {
|
2023-02-09 12:26:54 +00:00
|
|
|
const firstP = e.tags.find(b => b[0] === "p");
|
2023-02-07 20:04:50 +00:00
|
|
|
return firstP ? firstP[1] : "";
|
2023-01-18 18:53:34 +00:00
|
|
|
}
|
|
|
|
|
2023-01-17 11:30:42 +00:00
|
|
|
export function isToSelf(e: RawEvent, pk: HexKey) {
|
2023-02-07 20:04:50 +00:00
|
|
|
return e.pubkey === pk && dmTo(e) === pk;
|
2023-01-17 11:30:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export function dmsInChat(dms: RawEvent[], pk: HexKey) {
|
2023-02-09 12:26:54 +00:00
|
|
|
return dms.filter(a => a.pubkey === pk || dmTo(a) === pk);
|
2023-01-17 11:30:42 +00:00
|
|
|
}
|
|
|
|
|
2023-01-17 10:47:00 +00:00
|
|
|
export function totalUnread(dms: RawEvent[], myPubKey: HexKey) {
|
2023-02-09 12:26:54 +00:00
|
|
|
return extractChats(dms, myPubKey).reduce((acc, v) => (acc += v.unreadMessages), 0);
|
2023-01-17 10:47:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function unreadDms(dms: RawEvent[], myPubKey: HexKey, pk: HexKey) {
|
2023-02-07 20:04:50 +00:00
|
|
|
if (pk === myPubKey) return 0;
|
2023-02-07 19:47:57 +00:00
|
|
|
const lastRead = lastReadDm(pk);
|
2023-02-09 12:26:54 +00:00
|
|
|
return dmsInChat(dms, pk).filter(a => a.created_at >= lastRead && a.pubkey !== myPubKey).length;
|
2023-01-17 10:47:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function newestMessage(dms: RawEvent[], myPubKey: HexKey, pk: HexKey) {
|
2023-02-07 20:04:50 +00:00
|
|
|
if (pk === myPubKey) {
|
|
|
|
return dmsInChat(
|
2023-02-09 12:26:54 +00:00
|
|
|
dms.filter(d => isToSelf(d, myPubKey)),
|
2023-02-07 20:04:50 +00:00
|
|
|
pk
|
|
|
|
).reduce((acc, v) => (acc = v.created_at > acc ? v.created_at : acc), 0);
|
|
|
|
}
|
|
|
|
|
2023-02-09 12:26:54 +00:00
|
|
|
return dmsInChat(dms, pk).reduce((acc, v) => (acc = v.created_at > acc ? v.created_at : acc), 0);
|
2023-01-17 10:47:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export function extractChats(dms: RawEvent[], myPubKey: HexKey) {
|
2023-02-09 12:26:54 +00:00
|
|
|
const keys = dms.map(a => [a.pubkey, dmTo(a)]).flat();
|
2023-02-07 20:04:50 +00:00
|
|
|
const filteredKeys = Array.from(new Set<string>(keys));
|
2023-02-09 12:26:54 +00:00
|
|
|
return filteredKeys.map(a => {
|
2023-02-07 20:04:50 +00:00
|
|
|
return {
|
|
|
|
pubkey: a,
|
|
|
|
unreadMessages: unreadDms(dms, myPubKey, a),
|
|
|
|
newestMessage: newestMessage(dms, myPubKey, a),
|
|
|
|
} as DmChat;
|
|
|
|
});
|
2023-01-25 18:08:53 +00:00
|
|
|
}
|