forked from Kieran/snort
feat: mark all dms read
This commit is contained in:
parent
2f3c46629d
commit
621bb5f6ee
@ -1,33 +1,38 @@
|
|||||||
import "./DM.css";
|
import "./DM.css";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { useInView } from 'react-intersection-observer';
|
import { useInView } from 'react-intersection-observer';
|
||||||
|
|
||||||
import useEventPublisher from "../feed/EventPublisher";
|
import useEventPublisher from "../feed/EventPublisher";
|
||||||
import Event from "../nostr/Event";
|
import Event from "../nostr/Event";
|
||||||
import NoteTime from "./NoteTime";
|
import NoteTime from "./NoteTime";
|
||||||
import Text from "./Text";
|
import Text from "./Text";
|
||||||
import { lastReadDm, setLastReadDm } from "../pages/MessagesPage";
|
import { setLastReadDm } from "../pages/MessagesPage";
|
||||||
|
import { RootState } from "../state/Store";
|
||||||
|
import { HexKey, TaggedRawEvent } from "../nostr";
|
||||||
|
import { incDmInteraction } from "../state/Login";
|
||||||
|
|
||||||
export type DMProps = {
|
export type DMProps = {
|
||||||
data: any
|
data: TaggedRawEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DM(props: DMProps) {
|
export default function DM(props: DMProps) {
|
||||||
const pubKey = useSelector<any>(s => s.login.publicKey);
|
const dispatch = useDispatch();
|
||||||
|
const pubKey = useSelector<RootState, HexKey | undefined>(s => s.login.publicKey);
|
||||||
const publisher = useEventPublisher();
|
const publisher = useEventPublisher();
|
||||||
const [content, setContent] = useState("Loading...");
|
const [content, setContent] = useState("Loading...");
|
||||||
const [decrypted, setDecrypted] = useState(false);
|
const [decrypted, setDecrypted] = useState(false);
|
||||||
const { ref, inView, entry } = useInView();
|
const { ref, inView } = useInView();
|
||||||
const isMe = props.data.pubkey === pubKey;
|
const isMe = props.data.pubkey === pubKey;
|
||||||
|
|
||||||
async function decrypt() {
|
async function decrypt() {
|
||||||
let e = new Event(props.data);
|
let e = new Event(props.data);
|
||||||
if (!isMe) {
|
|
||||||
setLastReadDm(e.PubKey);
|
|
||||||
}
|
|
||||||
let decrypted = await publisher.decryptDm(e);
|
let decrypted = await publisher.decryptDm(e);
|
||||||
setContent(decrypted || "<ERROR>");
|
setContent(decrypted || "<ERROR>");
|
||||||
|
if (!isMe) {
|
||||||
|
setLastReadDm(e.PubKey);
|
||||||
|
dispatch(incDmInteraction());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useSelector } from "react-redux"
|
import { useDispatch, useSelector } from "react-redux"
|
||||||
|
|
||||||
import { HexKey, RawEvent } from "../nostr";
|
import { HexKey, RawEvent } from "../nostr";
|
||||||
import ProfileImage from "../element/ProfileImage";
|
import ProfileImage from "../element/ProfileImage";
|
||||||
import { hexToBech32 } from "../Util";
|
import { hexToBech32 } from "../Util";
|
||||||
|
import { incDmInteraction } from "../state/Login";
|
||||||
|
import { RootState } from "../state/Store";
|
||||||
|
|
||||||
type DmChat = {
|
type DmChat = {
|
||||||
pubkey: HexKey,
|
pubkey: HexKey,
|
||||||
@ -12,12 +14,14 @@ type DmChat = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function MessagesPage() {
|
export default function MessagesPage() {
|
||||||
const myPubKey = useSelector<any, string>(s => s.login.publicKey);
|
const dispatch = useDispatch();
|
||||||
const dms = useSelector<any, RawEvent[]>(s => s.login.dms);
|
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);
|
||||||
|
|
||||||
const chats = useMemo(() => {
|
const chats = useMemo(() => {
|
||||||
return extractChats(dms, myPubKey);
|
return extractChats(dms, myPubKey!);
|
||||||
}, [dms]);
|
}, [dms, myPubKey, dmInteraction]);
|
||||||
|
|
||||||
function person(chat: DmChat) {
|
function person(chat: DmChat) {
|
||||||
return (
|
return (
|
||||||
@ -30,9 +34,19 @@ export default function MessagesPage() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function markAllRead() {
|
||||||
|
for (let c of chats) {
|
||||||
|
setLastReadDm(c.pubkey);
|
||||||
|
}
|
||||||
|
dispatch(incDmInteraction());
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h3>Messages</h3>
|
<div className="flex">
|
||||||
|
<h3 className="f-grow">Messages</h3>
|
||||||
|
<div className="btn" onClick={() => markAllRead()}>Mark All Read</div>
|
||||||
|
</div>
|
||||||
{chats.sort((a, b) => b.newestMessage - a.newestMessage).map(person)}
|
{chats.sort((a, b) => b.newestMessage - a.newestMessage).map(person)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
@ -81,7 +95,6 @@ function newestMessage(dms: RawEvent[], myPubKey: HexKey, pk: HexKey) {
|
|||||||
return dmsInChat(dms, pk).reduce((acc, v) => acc = v.created_at > acc ? v.created_at : acc, 0);
|
return dmsInChat(dms, pk).reduce((acc, v) => acc = v.created_at > acc ? v.created_at : acc, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function extractChats(dms: RawEvent[], myPubKey: HexKey) {
|
export function extractChats(dms: RawEvent[], myPubKey: HexKey) {
|
||||||
const keys = dms.map(a => [a.pubkey, dmTo(a)]).flat();
|
const keys = dms.map(a => [a.pubkey, dmTo(a)]).flat();
|
||||||
const filteredKeys = Array.from(new Set<string>(keys));
|
const filteredKeys = Array.from(new Set<string>(keys));
|
||||||
|
@ -52,9 +52,27 @@ interface LoginStore {
|
|||||||
/**
|
/**
|
||||||
* Encrypted DM's
|
* Encrypted DM's
|
||||||
*/
|
*/
|
||||||
dms: TaggedRawEvent[]
|
dms: TaggedRawEvent[],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counter to trigger refresh of unread dms
|
||||||
|
*/
|
||||||
|
dmInteraction: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const InitState = {
|
||||||
|
loggedOut: undefined,
|
||||||
|
publicKey: undefined,
|
||||||
|
privateKey: undefined,
|
||||||
|
relays: {},
|
||||||
|
latestRelays: 0,
|
||||||
|
follows: [],
|
||||||
|
notifications: [],
|
||||||
|
readNotifications: 0,
|
||||||
|
dms: [],
|
||||||
|
dmInteraction: 0
|
||||||
|
} as LoginStore;
|
||||||
|
|
||||||
export interface SetRelaysPayload {
|
export interface SetRelaysPayload {
|
||||||
relays: Record<string, RelaySettings>,
|
relays: Record<string, RelaySettings>,
|
||||||
createdAt: number
|
createdAt: number
|
||||||
@ -62,14 +80,7 @@ export interface SetRelaysPayload {
|
|||||||
|
|
||||||
const LoginSlice = createSlice({
|
const LoginSlice = createSlice({
|
||||||
name: "Login",
|
name: "Login",
|
||||||
initialState: <LoginStore>{
|
initialState: InitState,
|
||||||
relays: {},
|
|
||||||
latestRelays: 0,
|
|
||||||
follows: [],
|
|
||||||
notifications: [],
|
|
||||||
readNotifications: 0,
|
|
||||||
dms: []
|
|
||||||
},
|
|
||||||
reducers: {
|
reducers: {
|
||||||
init: (state) => {
|
init: (state) => {
|
||||||
state.privateKey = window.localStorage.getItem(PrivateKeyItem) ?? undefined;
|
state.privateKey = window.localStorage.getItem(PrivateKeyItem) ?? undefined;
|
||||||
@ -182,17 +193,14 @@ const LoginSlice = createSlice({
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
incDmInteraction: (state) => {
|
||||||
|
state.dmInteraction += 1;
|
||||||
|
},
|
||||||
logout: (state) => {
|
logout: (state) => {
|
||||||
window.localStorage.removeItem(PrivateKeyItem);
|
window.localStorage.clear();
|
||||||
window.localStorage.removeItem(PublicKeyItem);
|
Object.assign(state, InitState);
|
||||||
window.localStorage.removeItem(NotificationsReadItem);
|
|
||||||
state.privateKey = undefined;
|
|
||||||
state.publicKey = undefined;
|
|
||||||
state.follows = [];
|
|
||||||
state.notifications = [];
|
|
||||||
state.loggedOut = true;
|
state.loggedOut = true;
|
||||||
state.readNotifications = 0;
|
state.relays = Object.fromEntries(DefaultRelays.entries());
|
||||||
state.dms = [];
|
|
||||||
},
|
},
|
||||||
markNotificationsRead: (state) => {
|
markNotificationsRead: (state) => {
|
||||||
state.readNotifications = new Date().getTime();
|
state.readNotifications = new Date().getTime();
|
||||||
@ -210,6 +218,7 @@ export const {
|
|||||||
setFollows,
|
setFollows,
|
||||||
addNotifications,
|
addNotifications,
|
||||||
addDirectMessage,
|
addDirectMessage,
|
||||||
|
incDmInteraction,
|
||||||
logout,
|
logout,
|
||||||
markNotificationsRead
|
markNotificationsRead
|
||||||
} = LoginSlice.actions;
|
} = LoginSlice.actions;
|
||||||
|
Loading…
Reference in New Issue
Block a user