Use waku message timestamp as better unique key

This commit is contained in:
Franck Royer 2021-05-28 16:31:08 +10:00
parent 9e64eec2a6
commit 553c0154d9
No known key found for this signature in database
GPG Key ID: A82ED75A8DFC50A4
5 changed files with 89 additions and 39 deletions

View File

@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
- Enable access to `WakuMessage.timestamp`. - Enable access to `WakuMessage.timestamp`.
- Examples (web chat): Use `WakuMessage.timestamp` as unique key for list items.
## [0.5.0] - 2021-05-21 ## [0.5.0] - 2021-05-21

View File

@ -2,7 +2,6 @@ import PeerId from 'peer-id';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import './App.css'; import './App.css';
import { import {
ChatMessage,
getStatusFleetNodes, getStatusFleetNodes,
Environment, Environment,
StoreCodec, StoreCodec,
@ -14,6 +13,7 @@ import Room from './Room';
import { WakuContext } from './WakuContext'; import { WakuContext } from './WakuContext';
import { ThemeProvider } from '@livechat/ui-kit'; import { ThemeProvider } from '@livechat/ui-kit';
import { generate } from 'server-name-generator'; import { generate } from 'server-name-generator';
import { Message } from './Message';
const themes = { const themes = {
AuthorName: { AuthorName: {
@ -49,13 +49,17 @@ export const ChatContentTopic = '/waku/2/huilong/proto';
async function retrieveStoreMessages( async function retrieveStoreMessages(
waku: Waku, waku: Waku,
peerId: PeerId, peerId: PeerId,
setArchivedMessages: (value: ChatMessage[]) => void setArchivedMessages: (value: Message[]) => void
): Promise<number> { ): Promise<number> {
const callback = (wakuMessages: WakuMessage[]): void => { const callback = (wakuMessages: WakuMessage[]): void => {
const messages = wakuMessages const messages: Message[] = [];
.map((wakuMsg) => wakuMsg.payload) wakuMessages
.filter((payload) => !!payload) .map((wakuMsg) => Message.fromWakuMessage(wakuMsg))
.map((payload) => ChatMessage.decode(payload as Uint8Array)); .forEach((message) => {
if (message) {
messages.push(message);
}
});
setArchivedMessages(messages); setArchivedMessages(messages);
}; };
@ -70,18 +74,17 @@ async function retrieveStoreMessages(
} }
export default function App() { export default function App() {
let [newMessages, setNewMessages] = useState<ChatMessage[]>([]); let [newMessages, setNewMessages] = useState<Message[]>([]);
let [archivedMessages, setArchivedMessages] = useState<ChatMessage[]>([]); let [archivedMessages, setArchivedMessages] = useState<Message[]>([]);
let [stateWaku, setWaku] = useState<Waku | undefined>(undefined); let [stateWaku, setWaku] = useState<Waku | undefined>(undefined);
let [nick, setNick] = useState<string>(generate()); let [nick, setNick] = useState<string>(generate());
useEffect(() => { useEffect(() => {
const handleRelayMessage = (wakuMsg: WakuMessage) => { const handleRelayMessage = (wakuMsg: WakuMessage) => {
if (wakuMsg.payload) { console.log('Message received: ', wakuMsg);
const chatMsg = ChatMessage.decode(wakuMsg.payload); const msg = Message.fromWakuMessage(wakuMsg);
if (chatMsg) { if (msg) {
setNewMessages([chatMsg]); setNewMessages([msg]);
}
} }
}; };
@ -147,7 +150,7 @@ export default function App() {
setNick setNick
); );
const commandMessages = response.map((msg) => { const commandMessages = response.map((msg) => {
return ChatMessage.fromUtf8String(new Date(), command, msg); return Message.fromUtf8String(command, msg);
}); });
setNewMessages(commandMessages); setNewMessages(commandMessages);
}} }}

View File

@ -1,20 +1,20 @@
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { ChatMessage } from 'js-waku';
import { import {
Message, Message as LiveMessage,
MessageText, MessageText,
MessageGroup, MessageGroup,
MessageList, MessageList,
} from '@livechat/ui-kit'; } from '@livechat/ui-kit';
import { Message } from './Message';
interface Props { interface Props {
archivedMessages: ChatMessage[]; archivedMessages: Message[];
newMessages: ChatMessage[]; newMessages: Message[];
} }
export default function ChatList(props: Props) { export default function ChatList(props: Props) {
const [messages, setMessages] = useState<ChatMessage[]>([]); const [messages, setMessages] = useState<Message[]>([]);
const [groupedMessages, setGroupedMessages] = useState<ChatMessage[][]>([]); const [groupedMessages, setGroupedMessages] = useState<Message[][]>([]);
let updatedMessages; let updatedMessages;
if (IsThereNewMessages(props.newMessages, messages)) { if (IsThereNewMessages(props.newMessages, messages)) {
@ -42,17 +42,20 @@ export default function ChatList(props: Props) {
const renderedGroupedMessages = groupedMessages.map((currentMessageGroup) => ( const renderedGroupedMessages = groupedMessages.map((currentMessageGroup) => (
<MessageGroup onlyFirstWithMeta> <MessageGroup onlyFirstWithMeta>
{currentMessageGroup.map((currentMessage) => ( {currentMessageGroup.map((currentMessage) => (
<Message <LiveMessage
key={ key={
currentMessage.timestamp.valueOf() + currentMessage.sentTimestamp
currentMessage.nick + ? currentMessage.sentTimestamp.valueOf()
currentMessage.payloadAsUtf8 : '' +
currentMessage.timestamp.valueOf() +
currentMessage.nick +
currentMessage.payloadAsUtf8
} }
authorName={currentMessage.nick} authorName={currentMessage.nick}
date={formatDisplayDate(currentMessage)} date={formatDisplayDate(currentMessage)}
> >
<MessageText>{currentMessage.payloadAsUtf8}</MessageText> <MessageText>{currentMessage.payloadAsUtf8}</MessageText>
</Message> </LiveMessage>
))} ))}
</MessageGroup> </MessageGroup>
)); ));
@ -65,10 +68,10 @@ export default function ChatList(props: Props) {
); );
} }
function groupMessagesBySender(messageArray: ChatMessage[]): ChatMessage[][] { function groupMessagesBySender(messageArray: Message[]): Message[][] {
let currentSender = -1; let currentSender = -1;
let lastNick = ''; let lastNick = '';
let messagesBySender: ChatMessage[][] = []; let messagesBySender: Message[][] = [];
let currentSenderMessage = 0; let currentSenderMessage = 0;
for (let currentMessage of messageArray) { for (let currentMessage of messageArray) {
@ -83,7 +86,7 @@ function groupMessagesBySender(messageArray: ChatMessage[]): ChatMessage[][] {
return messagesBySender; return messagesBySender;
} }
function formatDisplayDate(message: ChatMessage): string { function formatDisplayDate(message: Message): string {
return message.timestamp.toLocaleString([], { return message.timestamp.toLocaleString([], {
month: 'short', month: 'short',
day: 'numeric', day: 'numeric',
@ -93,7 +96,7 @@ function formatDisplayDate(message: ChatMessage): string {
}); });
} }
const AlwaysScrollToBottom = (props: { newMessages: ChatMessage[] }) => { const AlwaysScrollToBottom = (props: { newMessages: Message[] }) => {
const elementRef = useRef<HTMLDivElement>(); const elementRef = useRef<HTMLDivElement>();
useEffect(() => { useEffect(() => {
@ -106,8 +109,8 @@ const AlwaysScrollToBottom = (props: { newMessages: ChatMessage[] }) => {
}; };
function IsThereNewMessages( function IsThereNewMessages(
newValues: ChatMessage[], newValues: Message[],
currentValues: ChatMessage[] currentValues: Message[]
): boolean { ): boolean {
if (newValues.length === 0) return false; if (newValues.length === 0) return false;
if (currentValues.length === 0) return true; if (currentValues.length === 0) return true;
@ -118,8 +121,8 @@ function IsThereNewMessages(
} }
function copyMergeUniqueReplace( function copyMergeUniqueReplace(
newValues: ChatMessage[], newValues: Message[],
currentValues: ChatMessage[] currentValues: Message[]
) { ) {
const copy = currentValues.slice(); const copy = currentValues.slice();
newValues.forEach((msg) => { newValues.forEach((msg) => {
@ -131,10 +134,11 @@ function copyMergeUniqueReplace(
return copy; return copy;
} }
function isEqual(lhs: ChatMessage, rhs: ChatMessage): boolean { function isEqual(lhs: Message, rhs: Message): boolean {
return ( return (
lhs.nick === rhs.nick && lhs.nick === rhs.nick &&
lhs.payloadAsUtf8 === rhs.payloadAsUtf8 && lhs.payloadAsUtf8 === rhs.payloadAsUtf8 &&
lhs.timestamp.toString() === rhs.timestamp.toString() lhs.timestamp.valueOf() === rhs.timestamp.valueOf() &&
lhs.sentTimestamp?.valueOf() === rhs.sentTimestamp?.valueOf()
); );
} }

View File

@ -0,0 +1,39 @@
import { ChatMessage, WakuMessage } from 'js-waku';
export class Message {
public chatMessage: ChatMessage;
// WakuMessage timestamp
public sentTimestamp: Date | undefined;
constructor(chatMessage: ChatMessage, sentTimestamp: Date | undefined) {
this.chatMessage = chatMessage;
this.sentTimestamp = sentTimestamp;
}
static fromWakuMessage(wakuMsg: WakuMessage): Message | undefined {
if (wakuMsg.payload) {
const chatMsg = ChatMessage.decode(wakuMsg.payload);
if (chatMsg) {
return new Message(chatMsg, wakuMsg.timestamp);
}
}
return;
}
static fromUtf8String(nick: string, text: string): Message {
const now = new Date();
return new Message(ChatMessage.fromUtf8String(now, nick, text), now);
}
get nick() {
return this.chatMessage.nick;
}
get timestamp() {
return this.chatMessage.timestamp;
}
get payloadAsUtf8() {
return this.chatMessage.payloadAsUtf8;
}
}

View File

@ -4,10 +4,11 @@ import ChatList from './ChatList';
import MessageInput from './MessageInput'; import MessageInput from './MessageInput';
import { useWaku } from './WakuContext'; import { useWaku } from './WakuContext';
import { TitleBar } from '@livechat/ui-kit'; import { TitleBar } from '@livechat/ui-kit';
import { Message } from './Message';
interface Props { interface Props {
newMessages: ChatMessage[]; newMessages: Message[];
archivedMessages: ChatMessage[]; archivedMessages: Message[];
commandHandler: (cmd: string) => void; commandHandler: (cmd: string) => void;
nick: string; nick: string;
} }
@ -52,10 +53,12 @@ async function handleMessage(
if (message.startsWith('/')) { if (message.startsWith('/')) {
commandHandler(message); commandHandler(message);
} else { } else {
const chatMessage = ChatMessage.fromUtf8String(new Date(), nick, message); const timestamp = new Date();
const chatMessage = ChatMessage.fromUtf8String(timestamp, nick, message);
const wakuMsg = WakuMessage.fromBytes( const wakuMsg = WakuMessage.fromBytes(
chatMessage.encode(), chatMessage.encode(),
ChatContentTopic ChatContentTopic,
timestamp
); );
return messageSender(wakuMsg); return messageSender(wakuMsg);
} }