From d11fe88b588e89f651d161aed151595c16fc9bd8 Mon Sep 17 00:00:00 2001 From: Titouan Rigoudy Date: Mon, 1 Nov 2021 22:47:58 +0100 Subject: [PATCH] Display message timestamps. --- src/modules/room/RoomChatMessageList.tsx | 20 ++++----- src/modules/room/message.ts | 23 ++++++++-- src/modules/room/slice.ts | 57 ++++++++++++------------ 3 files changed, 58 insertions(+), 42 deletions(-) diff --git a/src/modules/room/RoomChatMessageList.tsx b/src/modules/room/RoomChatMessageList.tsx index e7111f3..ea06074 100644 --- a/src/modules/room/RoomChatMessageList.tsx +++ b/src/modules/room/RoomChatMessageList.tsx @@ -9,31 +9,31 @@ interface Props { const messageShape = "px-3 py-1 rounded-xl"; -function boolToString(b: boolean): string { - return b ? "true" : "false"; -} - const RoomChatMessageList: FC = ({ loginUserName, messages }) => { // Append all messages in the chat room. const children = []; let i = 0; - for (const { userName, message, acked } of messages) { - const ackedString = boolToString(acked); + for (const { userName, message, receivedAt } of messages) { + let time = ""; + if (receivedAt !== null) { + time = receivedAt.toLocaleTimeString(); + } + if (userName === loginUserName) { children.push(
  • + {time}
    - {message}, acked: {ackedString} + {message}
  • ); } else { children.push(
  • + {time}
    {userName}
    -
    - {message}, acked: {ackedString} -
    +
    {message}
  • ); } diff --git a/src/modules/room/message.ts b/src/modules/room/message.ts index 08ac6ad..9cab095 100644 --- a/src/modules/room/message.ts +++ b/src/modules/room/message.ts @@ -2,7 +2,7 @@ import { AppDispatch } from "app/store"; import { RoomMembership, RoomMessage, - RoomMessagePayload, + RoomSendMessagePayload, RoomState, roomGetAll, roomReceiveMessage, @@ -32,12 +32,24 @@ function convertMembership(membership: string): RoomMembership { } } +interface SystemTime { + secs_since_epoch: number; + nanos_since_epoch: number; +} + +function convertDate(time: SystemTime): Date { + return new Date( + time.secs_since_epoch * 1000 + time.nanos_since_epoch / 1000000 + ); +} + function convertMessages(messages: any[]): RoomMessage[] { const result = []; for (const message of messages) { result.push({ userName: message.user_name, message: message.message, + receivedAt: convertDate(message.received_at), acked: true, }); } @@ -78,8 +90,11 @@ function handleRoomMessageResponse(dispatch: AppDispatch, response: any): void { dispatch( roomReceiveMessage({ roomName: response.room_name, - userName: response.user_name, - message: response.message, + message: { + userName: response.message.user_name, + message: response.message.message, + receivedAt: convertDate(response.message.received_at), + }, }) ); } @@ -97,7 +112,7 @@ function roomListRequest(): SocketMessage { return "RoomListRequest"; } -function roomMessageRequest({ roomName, message }: RoomMessagePayload) { +function roomMessageRequest({ roomName, message }: RoomSendMessagePayload) { return { RoomMessageRequest: { room_name: roomName, diff --git a/src/modules/room/slice.ts b/src/modules/room/slice.ts index bcacea2..199dcb5 100644 --- a/src/modules/room/slice.ts +++ b/src/modules/room/slice.ts @@ -20,31 +20,32 @@ export interface RoomMessage { // The message contents. message: string; - // Whether the server has acknowledged the message in a response. - // Only ever false for messages we sent. - acked: boolean; - - // TODO: timestamp. + // The time at which this message was received. `null` if unacked. + receivedAt: Date | null; } -// Attempts to ack `message` upon receival of `messageContents` from `userName`. +// Attempts to ack `existing` upon receival of `received`. // -// Returns true if `message` was unacked, was sent by `userName` and contains -// `messageContents`, in which case `message.acked` is set to `true`. +// Returns true if `existing` was unacked and matched `received`, in which case +// `existing.receivedAt` is set to `received.receivedAt`. // Returns false otherwise. function maybeAckMessage( - message: RoomMessage, - userName: string, - messageContents: string + existing: RoomMessage, + received: RoomMessage ): boolean { - if (message.acked) { + if (existing.receivedAt !== null) { return false; // Already acked. } - if (message.userName !== userName || message.message !== messageContents) { + + if (existing.userName !== received.userName) { + return false; // Wrong user. + } + + if (existing.message !== received.message) { return false; // Wrong message. } - message.acked = true; + existing.receivedAt = received.receivedAt; return true; } @@ -95,8 +96,12 @@ const initialState: RoomSliceState = { rooms: {}, }; -// The payload of an action to send a message to a chat room. -export interface RoomMessagePayload { +export interface RoomReceiveMessagePayload { + roomName: string; + message: RoomMessage; +} + +export interface RoomSendMessagePayload { roomName: string; userName: string; message: string; @@ -125,16 +130,12 @@ export const roomSlice = createSlice({ }, roomReceiveMessage: ( state: RoomSliceState, - action: PayloadAction + action: PayloadAction ) => { - const { roomName, userName } = action.payload; - const messageContents = action.payload.message; + const { roomName, message } = action.payload; const room = state.rooms[roomName]; if (room === undefined) { - console.log( - `Unknown room ${roomName} received message from ` + - `${userName}: ${messageContents}` - ); + console.log(`Unknown room ${roomName} received message:`, message); return; } @@ -145,17 +146,17 @@ export const roomSlice = createSlice({ // - using a map of unacked contents to messages // for (let i = room.messages.length - 1; i >= 0; i--) { - const message = room.messages[i]; - if (maybeAckMessage(message, userName, messageContents)) { + const existing = room.messages[i]; + if (maybeAckMessage(existing, message)) { return; } } - room.messages.push({ userName, message: messageContents, acked: true }); + room.messages.push(message); }, roomSendMessage: ( state: RoomSliceState, - action: PayloadAction + action: PayloadAction ) => { const { roomName, userName, message } = action.payload; const room = state.rooms[roomName]; @@ -166,7 +167,7 @@ export const roomSlice = createSlice({ return; } - room.messages.push({ userName, message, acked: false }); + room.messages.push({ userName, message, receivedAt: null }); }, roomSetAll: (state: RoomSliceState, action: PayloadAction) => { state.rooms = {};