fix: ensure messages are always ordered

Also:
- remove messages that have a timestamp further than tomorrow.
- remove dupes
This commit is contained in:
fryorcraken.eth 2023-09-13 16:10:21 +10:00
parent 0b494e4069
commit 47b57bbe2c
No known key found for this signature in database
GPG Key ID: A82ED75A8DFC50A4
5 changed files with 64 additions and 11 deletions

View File

@ -3,7 +3,7 @@ import { Message } from "./Message";
import type { ChatListProps } from "./types"; import type { ChatListProps } from "./types";
export default function ChatList(props: ChatListProps) { export default function ChatList(props: ChatListProps) {
const renderedMessages = props.messages.map((message) => ( const renderedMessages = props.messages.array.map((message) => (
<div <div
key={ key={
message.nick + message.nick +
@ -24,7 +24,7 @@ export default function ChatList(props: ChatListProps) {
return ( return (
<div className="overflow-y-auto h-full"> <div className="overflow-y-auto h-full">
{renderedMessages} {renderedMessages}
<AlwaysScrollToBottom messages={props.messages} /> <AlwaysScrollToBottom messages={props.messages.array} />
</div> </div>
); );
} }

View File

@ -11,6 +11,18 @@ export class Message {
this.sentTimestamp = sentTimestamp; this.sentTimestamp = sentTimestamp;
} }
static cmp(left: Message, right: Message): boolean {
return left.timestamp.getTime() < right.timestamp.getTime();
}
static isEqual(left: Message, right: Message): boolean {
return (
left.timestamp.valueOf() === right.timestamp.valueOf() &&
left.chatMessage.nick === right.chatMessage.nick &&
left.chatMessage.payloadAsUtf8 === right.chatMessage.payloadAsUtf8
);
}
static fromWakuMessage(wakuMsg: IDecodedMessage): Message | undefined { static fromWakuMessage(wakuMsg: IDecodedMessage): Message | undefined {
if (wakuMsg.payload) { if (wakuMsg.payload) {
try { try {

View File

@ -12,6 +12,7 @@ import type {
UsePeersParams, UsePeersParams,
UsePeersResults, UsePeersResults,
} from "./types"; } from "./types";
import { OrderedSet } from "./ordered_array";
export const usePersistentNick = (): [ export const usePersistentNick = (): [
string, string,
@ -40,15 +41,22 @@ export const useMessages = (params: UseMessagesParams): UseMessagesResult => {
setLocalMessages((prev) => [...prev, ...msgs]); setLocalMessages((prev) => [...prev, ...msgs]);
}; };
const allMessages = React.useMemo((): Message[] => { const allMessages = React.useMemo((): OrderedSet<Message> => {
return [...storedMessages, ...newMessages] const allMessages = new OrderedSet(Message.cmp, Message.isEqual);
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
const _msgs = [...storedMessages, ...newMessages]
.map(Message.fromWakuMessage) .map(Message.fromWakuMessage)
.concat(localMessages)
.filter((v): v is Message => !!v) .filter((v): v is Message => !!v)
.filter((v) => v.payloadAsUtf8 !== "") .filter((v) => v.payloadAsUtf8 !== "")
.sort( // Filter out messages that are "sent" tomorrow are they are likely to be flukes
(left, right) => left.timestamp.getTime() - right.timestamp.getTime() .filter((m) => m.timestamp.valueOf() < tomorrow.valueOf());
); allMessages.push(..._msgs);
allMessages.push(...localMessages);
return allMessages;
}, [storedMessages, newMessages, localMessages]); }, [storedMessages, newMessages, localMessages]);
return [allMessages, pushMessages]; return [allMessages, pushMessages];

View File

@ -0,0 +1,32 @@
export class OrderedSet<T> {
array: Array<T>;
constructor(
public orderCmp: (a: T, b: T) => boolean,
public isEqual: (a: T, b: T) => boolean
) {
this.array = [];
}
push(...items: T[]): void {
for (const item of items) {
this.insertInOrder(this.array, item);
}
}
insertInOrder(array: T[], item: T): T[] {
let i = 0;
while (i < array.length) {
if (this.isEqual(item, array[i])) {
continue;
}
if (this.orderCmp(item, array[i])) {
break;
}
i++;
}
array.splice(i, 0, item);
return array;
}
}

View File

@ -2,6 +2,7 @@ import type { PeerId } from "@libp2p/interface-peer-id";
import type { LightNode, StoreQueryOptions, Waku } from "@waku/interfaces"; import type { LightNode, StoreQueryOptions, Waku } from "@waku/interfaces";
import type { Decoder } from "@waku/sdk"; import type { Decoder } from "@waku/sdk";
import type { Message } from "./Message"; import type { Message } from "./Message";
import { OrderedSet } from "./ordered_array";
export type UsePeersParams = { export type UsePeersParams = {
node: undefined | Waku; node: undefined | Waku;
@ -20,10 +21,10 @@ export type UseMessagesParams = {
options: StoreQueryOptions; options: StoreQueryOptions;
}; };
export type UseMessagesResult = [Message[], (v: Message[]) => void]; export type UseMessagesResult = [OrderedSet<Message>, (v: Message[]) => void];
export interface ChatListProps { export interface ChatListProps {
messages: Message[]; messages: OrderedSet<Message>;
} }
export interface MessageInputProps { export interface MessageInputProps {
@ -32,7 +33,7 @@ export interface MessageInputProps {
} }
export interface RoomProps { export interface RoomProps {
messages: Message[]; messages: OrderedSet<Message>;
commandHandler: (cmd: string) => void; commandHandler: (cmd: string) => void;
nick: string; nick: string;
} }