Skip to content

Commit

Permalink
feat: update demo
Browse files Browse the repository at this point in the history
  • Loading branch information
ttypic committed Jan 2, 2024
1 parent b5ffd64 commit d685187
Show file tree
Hide file tree
Showing 8 changed files with 265 additions and 57 deletions.
1 change: 0 additions & 1 deletion demo/api/conversations/inMemoryDb.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { ulid } from 'ulidx';
import exp = require('constants');

export interface Conversation {
id: string;
Expand Down
33 changes: 20 additions & 13 deletions demo/src/components/Message/Message.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
import React, { ReactNode } from 'react';
import React, { ReactNode, useCallback } from 'react';
import clsx from 'clsx';

interface MessageProps {
id: string;
self?: boolean;
children?: ReactNode | undefined;
onMessageClick?(id: string): void;
}
export const Message: React.FC<MessageProps> = ({ self = false, children }) => {
export const Message: React.FC<MessageProps> = ({ id, self = false, children, onMessageClick }) => {
const handleMessageClick = useCallback(() => {
onMessageClick?.(id);
}, [id, onMessageClick]);

return (
<div className="chat-message">
<div
className="chat-message"
onClick={handleMessageClick}
>
<div className={clsx('flex items-end', { ['justify-end']: self, ['justify-start']: !self })}>
<div
className={clsx('flex flex-col space-y-2 text-xs max-w-xs mx-2', {
className={clsx('flex flex-col space-y-2 text max-w-xs mx-2', {
['items-end order-1']: self,
['items-start order-2']: !self,
})}
>
<div>
<span
className={clsx('px-4 py-2 rounded-lg inline-block', {
['rounded-br bg-blue-600 text-white']: self,
['rounded-bl justify-start bg-gray-300 text-gray-600']: !self,
})}
>
{children}
</span>
<div
className={clsx('px-4 py-2 rounded-lg inline-block', {
['rounded-br bg-blue-600 text-white']: self,
['rounded-bl justify-start bg-gray-300 text-gray-600']: !self,
})}
>
{children}
</div>
</div>
</div>
Expand Down
14 changes: 9 additions & 5 deletions demo/src/components/MessageInput/MessageInput.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import { useState, ChangeEventHandler, FormEventHandler } from 'react';
import { FC, ChangeEventHandler, FormEventHandler } from 'react';

interface MessageInputProps {
disabled: boolean;
value: string;
onValueChange(text: string): void;
onSend(text: string): void;
}

export const MessageInput: React.FC<MessageInputProps> = ({ onSend }) => {
const [value, setValue] = useState('');
export const MessageInput: FC<MessageInputProps> = ({ value, disabled, onValueChange, onSend }) => {
const handleValueChange: ChangeEventHandler<HTMLInputElement> = ({ target }) => {
setValue(target.value);
onValueChange(target.value);
};

const handleFormSubmit: FormEventHandler<HTMLFormElement> = (event) => {
event.preventDefault();
event.stopPropagation();
onSend(value);
setValue('');
onValueChange('');
};

return (
Expand All @@ -26,11 +28,13 @@ export const MessageInput: React.FC<MessageInputProps> = ({ onSend }) => {
type="text"
value={value}
onChange={handleValueChange}
disabled={disabled}
placeholder="Type.."
className="w-full focus:outline-none focus:placeholder-gray-400 text-gray-600 placeholder-gray-600 pl-2 bg-gray-200 rounded-md py-1"
/>
<div className="absolute right-0 items-center inset-y-0 hidden sm:flex">
<button
disabled={disabled}
type="submit"
className="inline-flex items-center justify-center rounded-md px-3 py-1 transition duration-500 ease-in-out text-white bg-blue-500 hover:bg-blue-400 focus:outline-none"
>
Expand Down
125 changes: 108 additions & 17 deletions demo/src/containers/Chat/Chat.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,119 @@
import { Message } from '../../components/Message';
import { useCallback, useState } from 'react';
import { Message } from '@ably-labs/chat';
import { Message as MessageComponent } from '../../components/Message';
import { MessageInput } from '../../components/MessageInput';
import { useMessages } from '../../hooks/useMessages.ts';
import { useMessages } from '../../hooks/useMessages';

export const Chat = () => {
const { clientId, messages, sendMessage } = useMessages();
const { loading, clientId, messages, sendMessage, editMessage, deleteMessage, addReaction, removeReaction } =
useMessages();
const [value, setValue] = useState('');
const [selectedMessage, setSelectedMessage] = useState<Message | null>(null);

const handleMessageClick = useCallback(
(id: string) => {
const message = messages.find((message) => message.id === id) ?? null;
const alreadySelected = selectedMessage?.id === id;
setSelectedMessage(alreadySelected ? null : message);
setValue(!alreadySelected && message?.client_id === clientId ? message?.content ?? '' : '');
},
[clientId, selectedMessage, messages],
);

const handleMessageSend = useCallback(
(text: string) => {
if (selectedMessage && selectedMessage?.client_id === clientId) {
setSelectedMessage(null);
editMessage(selectedMessage.id, text);
} else {
sendMessage(text);
}
},
[clientId, selectedMessage, sendMessage, editMessage],
);

const handleDeleteMessage = useCallback(() => {
if (!selectedMessage) return;
deleteMessage(selectedMessage.id);
setSelectedMessage(null);
setValue('');
}, [selectedMessage, deleteMessage]);

const handleLikeReaction = useCallback(() => {
if (!selectedMessage) return;
addReaction(selectedMessage.id, 'like');
setSelectedMessage(null);
setValue('');
}, [selectedMessage, addReaction]);

const handleRemoveReaction = useCallback(() => {
if (!selectedMessage) return;
removeReaction(selectedMessage.reactions.mine[0].id);
setSelectedMessage(null);
setValue('');
}, [selectedMessage, removeReaction]);

return (
<>
<div className="flex-1 p:2 sm:p-12 justify-between flex flex-col h-screen">
<div
id="messages"
className="w-96 flex flex-col space-y-4 p-3 overflow-y-auto scrollbar-thumb-blue scrollbar-thumb-rounded scrollbar-track-blue-lighter scrollbar-w-2 scrolling-touch"
>
{messages.map((msg) => (
<Message
key={msg.id}
self={msg.client_id === clientId}
>
{msg.content}
</Message>
))}
</div>
{selectedMessage && (
<div className="flex flex-none space-x-4 p-2">
{selectedMessage.client_id === clientId && (
<button
onClick={handleDeleteMessage}
className="rounded-md px-3 py-1 transition duration-500 ease-in-out text-white bg-blue-500 hover:bg-blue-400 focus:outline-none"
>
Delete
</button>
)}
{!selectedMessage.reactions.mine.length && (
<button
onClick={handleLikeReaction}
className="rounded-md px-3 py-1 transition duration-500 ease-in-out text-white bg-blue-500 hover:bg-blue-400 focus:outline-none"
>
Like
</button>
)}
{!!selectedMessage.reactions.mine.length && (
<button
onClick={handleRemoveReaction}
className="rounded-md px-3 py-1 transition duration-500 ease-in-out text-white bg-blue-500 hover:bg-blue-400 focus:outline-none"
>
Unlike
</button>
)}
</div>
)}
{loading && <div>loading...</div>}
{!loading && (
<div
id="messages"
className="w-96 flex flex-auto flex-col space-y-4 p-3 overflow-y-auto scrollbar-thumb-blue scrollbar-thumb-rounded scrollbar-track-blue-lighter scrollbar-w-2 scrolling-touch"
>
{messages.map((msg) => (
<MessageComponent
id={msg.id}
key={msg.id}
self={msg.client_id === clientId}
onMessageClick={handleMessageClick}
>
<div className="flex flex-col">
<div>{msg.content}</div>
{!!msg.reactions.counts.like && (
<div className="flex flex-row-reverse mt-4 text-xs">{msg.reactions.counts.like} ❤️</div>
)}
</div>
</MessageComponent>
))}
</div>
)}
<div className="border-t-2 border-gray-200 px-4 pt-4 mb-2 sm:mb-0">
<MessageInput onSend={sendMessage} />
<MessageInput
value={value}
disabled={loading}
onValueChange={setValue}
onSend={handleMessageSend}
/>
</div>
</div>
</>
Expand Down
13 changes: 13 additions & 0 deletions demo/src/hooks/useConversation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useContext } from 'react';
import { ConversationContext } from '../containers/ConversationContext';

export const useConversation = () => {
const context = useContext(ConversationContext);

if (!context) throw Error('Client is not setup!');

return {
conversation: context.conversation,
clientId: context.client.clientId,
};
};
Loading

0 comments on commit d685187

Please sign in to comment.