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
- Enable access to `WakuMessage.timestamp`.
- Examples (web chat): Use `WakuMessage.timestamp` as unique key for list items.
## [0.5.0] - 2021-05-21

View File

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

View File

@ -1,20 +1,20 @@
import { useEffect, useRef, useState } from 'react';
import { ChatMessage } from 'js-waku';
import {
Message,
Message as LiveMessage,
MessageText,
MessageGroup,
MessageList,
} from '@livechat/ui-kit';
import { Message } from './Message';
interface Props {
archivedMessages: ChatMessage[];
newMessages: ChatMessage[];
archivedMessages: Message[];
newMessages: Message[];
}
export default function ChatList(props: Props) {
const [messages, setMessages] = useState<ChatMessage[]>([]);
const [groupedMessages, setGroupedMessages] = useState<ChatMessage[][]>([]);
const [messages, setMessages] = useState<Message[]>([]);
const [groupedMessages, setGroupedMessages] = useState<Message[][]>([]);
let updatedMessages;
if (IsThereNewMessages(props.newMessages, messages)) {
@ -42,17 +42,20 @@ export default function ChatList(props: Props) {
const renderedGroupedMessages = groupedMessages.map((currentMessageGroup) => (
<MessageGroup onlyFirstWithMeta>
{currentMessageGroup.map((currentMessage) => (
<Message
<LiveMessage
key={
currentMessage.timestamp.valueOf() +
currentMessage.nick +
currentMessage.payloadAsUtf8
currentMessage.sentTimestamp
? currentMessage.sentTimestamp.valueOf()
: '' +
currentMessage.timestamp.valueOf() +
currentMessage.nick +
currentMessage.payloadAsUtf8
}
authorName={currentMessage.nick}
date={formatDisplayDate(currentMessage)}
>
<MessageText>{currentMessage.payloadAsUtf8}</MessageText>
</Message>
</LiveMessage>
))}
</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 lastNick = '';
let messagesBySender: ChatMessage[][] = [];
let messagesBySender: Message[][] = [];
let currentSenderMessage = 0;
for (let currentMessage of messageArray) {
@ -83,7 +86,7 @@ function groupMessagesBySender(messageArray: ChatMessage[]): ChatMessage[][] {
return messagesBySender;
}
function formatDisplayDate(message: ChatMessage): string {
function formatDisplayDate(message: Message): string {
return message.timestamp.toLocaleString([], {
month: 'short',
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>();
useEffect(() => {
@ -106,8 +109,8 @@ const AlwaysScrollToBottom = (props: { newMessages: ChatMessage[] }) => {
};
function IsThereNewMessages(
newValues: ChatMessage[],
currentValues: ChatMessage[]
newValues: Message[],
currentValues: Message[]
): boolean {
if (newValues.length === 0) return false;
if (currentValues.length === 0) return true;
@ -118,8 +121,8 @@ function IsThereNewMessages(
}
function copyMergeUniqueReplace(
newValues: ChatMessage[],
currentValues: ChatMessage[]
newValues: Message[],
currentValues: Message[]
) {
const copy = currentValues.slice();
newValues.forEach((msg) => {
@ -131,10 +134,11 @@ function copyMergeUniqueReplace(
return copy;
}
function isEqual(lhs: ChatMessage, rhs: ChatMessage): boolean {
function isEqual(lhs: Message, rhs: Message): boolean {
return (
lhs.nick === rhs.nick &&
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 { useWaku } from './WakuContext';
import { TitleBar } from '@livechat/ui-kit';
import { Message } from './Message';
interface Props {
newMessages: ChatMessage[];
archivedMessages: ChatMessage[];
newMessages: Message[];
archivedMessages: Message[];
commandHandler: (cmd: string) => void;
nick: string;
}
@ -52,10 +53,12 @@ async function handleMessage(
if (message.startsWith('/')) {
commandHandler(message);
} else {
const chatMessage = ChatMessage.fromUtf8String(new Date(), nick, message);
const timestamp = new Date();
const chatMessage = ChatMessage.fromUtf8String(timestamp, nick, message);
const wakuMsg = WakuMessage.fromBytes(
chatMessage.encode(),
ChatContentTopic
ChatContentTopic,
timestamp
);
return messageSender(wakuMsg);
}