Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

user typing indicator + layout fix #46

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5,241 changes: 5,191 additions & 50 deletions client/package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"react-router": "^5.2.1",
"react-router-dom": "^5.3.0",
"react-scroll-to-bottom": "^4.1.2",
"react-transition-group": "^4.4.2",
"socket.io-client": "^4.2.0",
"zustand": "^3.6.0"
},
Expand Down
39 changes: 38 additions & 1 deletion client/src/components/Chat.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import InfoBar from './InfoBar';
import Input from './Input';
import Messages from './Messages';
import Navbar from './Navbar';
import Typers from './Typers';
import Roommates from './Roommates';

let socket;

const Chat = ({ location }) => {
const [message, setMessage] = useState('');
const [typers, setTypers] = useState(new Set());

const { name, room, pfpSrc, messages, setRoommates, addMessage, resetStore } = useStore((state) => state);

Expand Down Expand Up @@ -51,10 +53,44 @@ const Chat = ({ location }) => {

const sendMessage = (message, callback) => {
if (message) {
timeoutHandler();
socket.emit('sendMessage', message, () => callback());
}
};

useEffect(() => {
socket.on('typing', ({ user, typing }) => {
console.log(`${user} has ${typing ? 'started' : 'stopped'} typing.`);
if (typing) setTypers((_typers) => new Set(_typers).add(user));
else
setTypers((_typers) => {
const dummy = new Set(_typers);
dummy.delete(user);
return dummy;
});
});
}, []);

const timeoutHandler = () => {
document.typing = false;
clearTimeout(document.timeout);
console.log('Timeout for typing finished...');
document.timeout = null;
socket.emit('sendTyping', document.typing);
};

const onChange = (e) => {
setMessage(e.target.value);
document.typing = true;
if (document.timeout) {
console.log('Already started writing...');
} else {
socket.emit('sendTyping', document.typing);
}
clearTimeout(document.timeout);
document.timeout = setTimeout(timeoutHandler, 4000);
};

return (
<div className="w-screen h-screen bg-green-50">
<Navbar />
Expand All @@ -74,7 +110,8 @@ const Chat = ({ location }) => {
<div className="grid grid-cols-5 w-full h-full">
<div className="col-span-4 flex flex-col border-r-2 border-blue-50">
<Messages messages={messages} name={name} />
<Input message={message} setMessage={setMessage} sendMessage={sendMessage} />
<Typers typers={typers} />
<Input message={message} setMessage={setMessage} sendMessage={sendMessage} onChange={onChange} />
</div>
<div className="col-span-1 bg-white">
<Roommates />
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/Input.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';

import sendIcon from '../assets/icons/sendIcon-green.png';

const Input = ({ message, setMessage, sendMessage }) => {
const Input = ({ message, setMessage, sendMessage, onChange }) => {
return (
<form
className="flex justify-between items-center bg-white shadow-xl w-full border-t-4 border-blue-50 h-16"
Expand All @@ -17,7 +17,7 @@ const Input = ({ message, setMessage, sendMessage }) => {
<input
type="text"
value={message}
onChange={(event) => setMessage(event.target.value)}
onChange={(event) => onChange(event)}
placeholder="Type a message..."
className="w-full h-full px-4 outline-none text-gray-600 font-medium"
/>
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/Messages.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import ScrollToBottom from 'react-scroll-to-bottom';
import Message from './Message';

const Messages = ({ messages, name }) => (
<ScrollToBottom className="overflow-auto flex-grow w-full p-4 bg-white text-sm md:text-lg">
<ScrollToBottom className="overflow-auto flex-grow w-full h-4 p-4 bg-white text-sm md:text-lg">
{messages.map((message, i) => (
<Message message={message} name={name} key={`${message.user}-${i}`} />
))}
Expand Down
48 changes: 48 additions & 0 deletions client/src/components/Typers.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, { useEffect, useState } from 'react';
import { CSSTransition } from 'react-transition-group';
import '../styles/Typers.css';
const Typers = ({ typers }) => {
let typerExists = Boolean(typers.size);
const typerArr = Array.from(typers);
let message;

if (typers.size) {
typerExists = true;
console.log(typers);
const subjective =
typers.size == 1
? typerArr[0]
: typers.size == 2
? `${typerArr[0]} and ${typerArr[1]}`
: `${typerArr[0]} and ${typers.size - 1} other users`;
const verbal = typers.size == 1 ? 'is typing' : 'are typing';
message = `${subjective} ${verbal}`;
}

return (
<CSSTransition
in={typerExists}
timeout={300}
classNames="alert"
unmountOnExit
onEnter={() => (typerExists = true)}
onExited={() => (typerExists = false)}
>
<div
className={`flex mx-auto px-3 py-1${typerExists ? ' bg-blue-100' : ''} rounded-2xl w-max my-2 overflow-hidden`}
>
<p className="text-blue-400 w-auto">{message}</p>
<div className="relative w-11 h-6 rounded-full">
<div className="delay-150 absolute m-2 w-2 h-2 rounded-full bg-green-300 animate-ping"></div>
<div className="absolute m-2 w-2 h-2 rounded-full bg-green-300"></div>
<div className="absolute my-2 ml-5 w-2 h-2 rounded-full bg-green-300 animate-ping"></div>
<div className="absolute my-2 ml-5 w-2 h-2 rounded-full bg-green-300"></div>
<div className="absolute my-2 ml-8 w-2 h-2 rounded-full bg-green-300 animate-ping"></div>
<div className="absolute my-2 ml-8 w-2 h-2 rounded-full bg-green-300"></div>
</div>
</div>
</CSSTransition>
);
};

export default Typers;
4 changes: 2 additions & 2 deletions client/src/components/atoms/Notification.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const NotificationBar = ({ variant, header, message, onClose, onBodyClick
const color = VARIANTS[variant];
return (
<div
className={`bg-${color}-100 border border-${color}-400 text-${color}-700 px-4 py-3 rounded relative`}
className={`bg-${color}-100 border border-${color}-400 text-${color}-700 px-4 py-3 rounded relative overflow-hidden`}
role="alert"
>
<strong className="font-bold mr-1.5">{header}</strong>
Expand Down Expand Up @@ -41,7 +41,7 @@ export const NotificationBar = ({ variant, header, message, onClose, onBodyClick
e.stopPropagation();
e.key === 'Enter' && onClose(e);
}}
className="absolute top-0 bottom-0 right-0 px-4 py-3"
className="absolute top-0 bottom-0 right-0 px-4 py-3 overflow-hidden"
>
<svg
className={`fill-current h-6 w-6 text-${color}-500`}
Expand Down
7 changes: 1 addition & 6 deletions client/src/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,4 @@ import ReactDOM from 'react-dom';

import App from './App';

ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root'),
);
ReactDOM.render(<App />, document.getElementById('root'));
17 changes: 17 additions & 0 deletions client/src/styles/Typers.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.alert-enter {
opacity: 0;
transform: scale(0.9);
}
.alert-enter-active {
opacity: 1;
transform: translateX(0);
transition: opacity 300ms, transform 300ms;
}
.alert-exit {
opacity: 1;
}
.alert-exit-active {
opacity: 0;
transform: scale(0.9);
transition: opacity 300ms, transform 300ms;
}
Comment on lines +1 to +17
Copy link
Owner

@manuelalferez manuelalferez Nov 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would like to avoid, as much as possible, having .css files in our project. Could you perform these transactions using tailwindcss only?

Loading