diff --git a/examples/web-chat/src/ChatList.tsx b/examples/web-chat/src/ChatList.tsx index 001e73f7..9dce4928 100644 --- a/examples/web-chat/src/ChatList.tsx +++ b/examples/web-chat/src/ChatList.tsx @@ -3,7 +3,7 @@ import { Message } from "./Message"; import type { ChatListProps } from "./types"; export default function ChatList(props: ChatListProps) { - const renderedMessages = props.messages.map((message) => ( + const renderedMessages = props.messages.array.map((message) => (
{renderedMessages} - +
); } diff --git a/examples/web-chat/src/Message.ts b/examples/web-chat/src/Message.ts index 11c5dabb..c853402d 100644 --- a/examples/web-chat/src/Message.ts +++ b/examples/web-chat/src/Message.ts @@ -11,6 +11,18 @@ export class Message { 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 { if (wakuMsg.payload) { try { diff --git a/examples/web-chat/src/hooks.ts b/examples/web-chat/src/hooks.ts index 32b0be69..97a01907 100644 --- a/examples/web-chat/src/hooks.ts +++ b/examples/web-chat/src/hooks.ts @@ -12,6 +12,7 @@ import type { UsePeersParams, UsePeersResults, } from "./types"; +import { OrderedSet } from "./ordered_array"; export const usePersistentNick = (): [ string, @@ -40,15 +41,22 @@ export const useMessages = (params: UseMessagesParams): UseMessagesResult => { setLocalMessages((prev) => [...prev, ...msgs]); }; - const allMessages = React.useMemo((): Message[] => { - return [...storedMessages, ...newMessages] + const allMessages = React.useMemo((): OrderedSet => { + const allMessages = new OrderedSet(Message.cmp, Message.isEqual); + + const tomorrow = new Date(); + tomorrow.setDate(tomorrow.getDate() + 1); + + const _msgs = [...storedMessages, ...newMessages] .map(Message.fromWakuMessage) - .concat(localMessages) .filter((v): v is Message => !!v) .filter((v) => v.payloadAsUtf8 !== "") - .sort( - (left, right) => left.timestamp.getTime() - right.timestamp.getTime() - ); + // Filter out messages that are "sent" tomorrow are they are likely to be flukes + .filter((m) => m.timestamp.valueOf() < tomorrow.valueOf()); + allMessages.push(..._msgs); + allMessages.push(...localMessages); + + return allMessages; }, [storedMessages, newMessages, localMessages]); return [allMessages, pushMessages]; diff --git a/examples/web-chat/src/ordered_array.ts b/examples/web-chat/src/ordered_array.ts new file mode 100644 index 00000000..1bfbb52e --- /dev/null +++ b/examples/web-chat/src/ordered_array.ts @@ -0,0 +1,32 @@ +export class OrderedSet { + array: Array; + + 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; + } +} diff --git a/examples/web-chat/src/types.ts b/examples/web-chat/src/types.ts index c4dcd927..2b7202a7 100644 --- a/examples/web-chat/src/types.ts +++ b/examples/web-chat/src/types.ts @@ -2,6 +2,7 @@ import type { PeerId } from "@libp2p/interface-peer-id"; import type { LightNode, StoreQueryOptions, Waku } from "@waku/interfaces"; import type { Decoder } from "@waku/sdk"; import type { Message } from "./Message"; +import { OrderedSet } from "./ordered_array"; export type UsePeersParams = { node: undefined | Waku; @@ -20,10 +21,10 @@ export type UseMessagesParams = { options: StoreQueryOptions; }; -export type UseMessagesResult = [Message[], (v: Message[]) => void]; +export type UseMessagesResult = [OrderedSet, (v: Message[]) => void]; export interface ChatListProps { - messages: Message[]; + messages: OrderedSet; } export interface MessageInputProps { @@ -32,7 +33,7 @@ export interface MessageInputProps { } export interface RoomProps { - messages: Message[]; + messages: OrderedSet; commandHandler: (cmd: string) => void; nick: string; }