mirror of
https://github.com/irislib/iris-messenger.git
synced 2024-10-18 14:13:21 +00:00
use localState for caching chats & their latest msgs
This commit is contained in:
parent
0e5e9e2188
commit
9e787af125
@ -18,7 +18,7 @@ import FeedTypeSelector from './FeedTypeSelector';
|
||||
import ImageGrid from './ImageGrid';
|
||||
import ShowMore from './ShowMore';
|
||||
import ShowNewEvents from './ShowNewEvents';
|
||||
import SortedEventMap from './SortedEventMap';
|
||||
import SortedEventMap from '../../utils/SortedEventMap';
|
||||
import { FeedProps, FeedState } from './types';
|
||||
|
||||
const INITIAL_PAGE_SIZE = 10;
|
||||
|
@ -377,6 +377,24 @@ const Events = {
|
||||
}
|
||||
this.zapsByNote.get(zappedNote)?.add(event);
|
||||
},
|
||||
async saveDM(event: Event) {
|
||||
const myPub = Key.getPubKey();
|
||||
let user = event.pubkey;
|
||||
if (event.pubkey === myPub) {
|
||||
user = event.tags?.find((tag) => tag[0] === 'p')?.[1] || user;
|
||||
} else {
|
||||
const forMe = event.tags?.some((tag) => tag[0] === 'p' && tag[1] === myPub);
|
||||
if (!forMe) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const latest = localState.get('chats').get(user).get('latest');
|
||||
latest.once((e) => {
|
||||
if (!e || !e.created_at || e.created_at < event.created_at) {
|
||||
latest.put({ id: event.id, created_at: event.created_at, text: '' });
|
||||
}
|
||||
});
|
||||
},
|
||||
async handleDirectMessage(event: Event) {
|
||||
const myPub = Key.getPubKey();
|
||||
let user = event.pubkey;
|
||||
@ -426,6 +444,7 @@ const Events = {
|
||||
const byUser = this.directMessagesByUser.get(user) ?? new SortedLimitedEventSet(500);
|
||||
byUser.add(event);
|
||||
this.directMessagesByUser.set(user, byUser);
|
||||
this.saveDM(event);
|
||||
},
|
||||
handleKeyValue(event: Event) {
|
||||
if (event.pubkey !== Key.getPubKey()) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Event } from 'nostr-tools';
|
||||
|
||||
import Events from '../../nostr/Events';
|
||||
import Events from '../nostr/Events';
|
||||
|
||||
export default class SortedEventMap {
|
||||
private eventMap: Map<string, Event>; // or should we store just strings, getting Events from loki?
|
||||
@ -8,7 +8,7 @@ export default class SortedEventMap {
|
||||
private sortBy: string;
|
||||
private sortDirection: string;
|
||||
|
||||
constructor(sortBy: string, sortDirection: string) {
|
||||
constructor(sortBy: string, sortDirection = 'desc') {
|
||||
this.sortBy = sortBy;
|
||||
this.sortDirection = sortDirection;
|
||||
this.eventMap = new Map<string, Event>();
|
@ -70,6 +70,12 @@ export default class SortedMap<K, V> {
|
||||
}
|
||||
}
|
||||
|
||||
*entries(): IterableIterator<[K, V]> {
|
||||
for (const key of this.sortedKeys) {
|
||||
yield [key, this.map.get(key) as V];
|
||||
}
|
||||
}
|
||||
|
||||
has(key: K): boolean {
|
||||
return this.map.has(key);
|
||||
}
|
||||
|
@ -2,21 +2,25 @@ import { useEffect, useRef, useState } from 'react';
|
||||
import $ from 'jquery';
|
||||
|
||||
import localState from '../../LocalState';
|
||||
import Events from '../../nostr/Events';
|
||||
import Key from '../../nostr/Key';
|
||||
import SocialNetwork from '../../nostr/SocialNetwork';
|
||||
import { translate as t } from '../../translations/Translation.mjs';
|
||||
import SortedMap from '../../utils/SortedMap';
|
||||
|
||||
import ChatListItem from './ChatListItem';
|
||||
import NewChatButton from './NewChatButton';
|
||||
|
||||
const loadedTime = Date.now();
|
||||
const sortChats = (a: { key: string; value: any }, b: { key: string; value: any }) => {
|
||||
const aLatest = a.value.latest;
|
||||
const bLatest = b.value.latest;
|
||||
if (!aLatest) return 1;
|
||||
if (!bLatest) return -1;
|
||||
return bLatest.created_at - aLatest.created_at;
|
||||
};
|
||||
|
||||
const ChatList = ({ activeChat, className }) => {
|
||||
const [directMessages, setDirectMessages] = useState(new Map());
|
||||
const [groups, setGroups] = useState(new Map());
|
||||
const [sortedChats, setSortedChats] = useState([] as string[]);
|
||||
const [shouldWait] = useState(Date.now() - loadedTime < 1000);
|
||||
const [chats, setChats] = useState(new SortedMap<string, any>(sortChats) as any);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [_renderCount, setRenderCount] = useState(0); // new state variable
|
||||
const chatListRef = useRef(null as any);
|
||||
|
||||
const enableDesktopNotifications = () => {
|
||||
@ -32,31 +36,15 @@ const ChatList = ({ activeChat, className }) => {
|
||||
|
||||
useEffect(() => {
|
||||
const unsubs = [] as any[];
|
||||
const go = () => {
|
||||
unsubs.push(
|
||||
Events.getDirectMessages(async (incomingChats) => {
|
||||
let keys = Array.from(incomingChats.keys()) as string[];
|
||||
const maxFollowDistance = await localState
|
||||
.get('globalFilter')
|
||||
.get('maxFollowDistance')
|
||||
.once();
|
||||
const blockedUsers = Object.keys((await localState.get('blockedUsers').once()) || {});
|
||||
keys = keys.filter(
|
||||
(key: string) =>
|
||||
!blockedUsers.includes(key) &&
|
||||
SocialNetwork.getFollowDistance(key) <= maxFollowDistance,
|
||||
);
|
||||
|
||||
const filteredChats = new Map(keys.map((k) => [k, incomingChats.get(k)]));
|
||||
setDirectMessages(filteredChats);
|
||||
}),
|
||||
);
|
||||
};
|
||||
if (shouldWait) {
|
||||
setTimeout(go, 1000); // timeout always helps
|
||||
} else {
|
||||
go();
|
||||
}
|
||||
unsubs.push(
|
||||
localState.get('chats').map((value, key) => {
|
||||
setChats((prevChats) => {
|
||||
prevChats.set(key, { ...value });
|
||||
return prevChats;
|
||||
});
|
||||
setRenderCount((prevCount) => prevCount + 1);
|
||||
}),
|
||||
);
|
||||
|
||||
unsubs.push(
|
||||
localState.get('scrollUp').on(() => {
|
||||
@ -67,6 +55,7 @@ const ChatList = ({ activeChat, className }) => {
|
||||
}),
|
||||
);
|
||||
|
||||
/*
|
||||
unsubs.push(
|
||||
localState.get('groups').map((group, localKey) => {
|
||||
if (!(group && localKey)) return;
|
||||
@ -74,27 +63,11 @@ const ChatList = ({ activeChat, className }) => {
|
||||
setGroups((prevGroups) => new Map(prevGroups.set(localKey, group)));
|
||||
}),
|
||||
);
|
||||
*/
|
||||
|
||||
return () => unsubs.forEach((unsub) => unsub());
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const chats: Map<string, any> = new Map(directMessages);
|
||||
groups.forEach((value, key) => {
|
||||
chats.set(key, value);
|
||||
});
|
||||
const sorted = Array.from(chats.keys()).sort((a, b) => {
|
||||
if (a.length < b.length) return -1; // show groups first until their last msg is implemented
|
||||
const aEventIds = chats.get(a).eventIds;
|
||||
const bEventIds = chats.get(b).eventIds;
|
||||
const aLatestEvent = aEventIds.length ? Events.db.by('id', aEventIds[0]) : null;
|
||||
const bLatestEvent = bEventIds.length ? Events.db.by('id', bEventIds[0]) : null;
|
||||
|
||||
return bLatestEvent?.created_at - aLatestEvent?.created_at;
|
||||
}) as string[];
|
||||
setSortedChats(sorted);
|
||||
}, [directMessages, groups]);
|
||||
|
||||
const activeChatHex = (activeChat && Key.toNostrHexAddress(activeChat)) || activeChat;
|
||||
|
||||
return (
|
||||
@ -110,12 +83,12 @@ const ChatList = ({ activeChat, className }) => {
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col">
|
||||
<NewChatButton active={activeChatHex === 'new'} />
|
||||
{sortedChats.map((pubkey) => (
|
||||
{Array.from<[string, any]>(chats.entries() as any).map(([pubkey, data]) => (
|
||||
<ChatListItem
|
||||
active={pubkey === activeChatHex}
|
||||
key={pubkey}
|
||||
chat={pubkey}
|
||||
latestMsgId={directMessages.get(pubkey)?.eventIds[0]}
|
||||
latestMsg={data?.latest}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
@ -11,10 +11,9 @@ import Events from '../../nostr/Events';
|
||||
import Key from '../../nostr/Key';
|
||||
import { translate as t } from '../../translations/Translation.mjs';
|
||||
|
||||
const ChatListItem = ({ chat, active = false, latestMsgId = null }) => {
|
||||
const ChatListItem = ({ chat, active = false, latestMsg = {} as any }) => {
|
||||
const [name, setName] = useState(null);
|
||||
const [latest, setLatest] = useState({} as any);
|
||||
const [latestText, setLatestText] = useState('');
|
||||
const [latestText, setLatestText] = useState(latestMsg.text || '');
|
||||
|
||||
useEffect(() => {
|
||||
const isGroup = chat.length < 20;
|
||||
@ -28,12 +27,21 @@ const ChatListItem = ({ chat, active = false, latestMsgId = null }) => {
|
||||
}
|
||||
});
|
||||
}
|
||||
getLatestMsg();
|
||||
}, [chat]);
|
||||
|
||||
useEffect(() => {
|
||||
getLatestMsg();
|
||||
}, [latestMsgId]);
|
||||
if (latestMsg.text || !latestMsg.id) {
|
||||
return;
|
||||
}
|
||||
Events.getEventById(latestMsg.id, false, (event) => {
|
||||
if (event) {
|
||||
Key.decryptMessage(latestMsg.id, (text: string) => {
|
||||
setLatestText(text);
|
||||
localState.get('chats').get(chat).get('latest').get('text').put(text);
|
||||
});
|
||||
}
|
||||
});
|
||||
}, [latestMsg.id]);
|
||||
|
||||
const onKeyUp = (e: KeyboardEvent) => {
|
||||
// if enter was pressed, click the element
|
||||
@ -42,22 +50,10 @@ const ChatListItem = ({ chat, active = false, latestMsgId = null }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const getLatestMsg = () => {
|
||||
if (!latestMsgId) {
|
||||
return;
|
||||
}
|
||||
const event = Events.db.by('id', latestMsgId);
|
||||
if (event) {
|
||||
setLatest(event);
|
||||
Key.decryptMessage(latestMsgId, (text: string) => {
|
||||
setLatestText(text);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const activeClass = active ? 'bg-neutral-800' : 'hover:bg-neutral-900';
|
||||
const time =
|
||||
(latest.created_at && Helpers.getRelativeTimeText(new Date(latest.created_at * 1000))) || '';
|
||||
(latestMsg.created_at && Helpers.getRelativeTimeText(new Date(latestMsg.created_at * 1000))) ||
|
||||
'';
|
||||
|
||||
const npub = Key.toNostrBech32Address(chat, 'npub');
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user