1
Fork 0

Rudimentary usernames

This commit is contained in:
Tobias Berger 2022-01-21 13:50:41 +00:00 committed by GitHub
parent 2080d58c06
commit 128b092824
3 changed files with 149 additions and 69 deletions

@ -1 +1 @@
Subproject commit cde3d62b1c0a1c95643037ca03e95bac178118d2 Subproject commit a618eda73268a675e0c90dace9751ad05d5e0443

View file

@ -4,12 +4,16 @@ import useWebSocket, {
ReadyState as WebSocketReadyState, ReadyState as WebSocketReadyState,
} from "react-use-websocket"; } from "react-use-websocket";
import MessageComponent from "src/components/Message"; import MessageComponent from "src/components/Message";
import { import type {
isCurrentlyTypingMessage, AckMessage,
ConnectedUser,
DesiredNameMessage,
TextMessage, TextMessage,
TypingMessage, TypingMessage,
} from "src/lib/ServerMessage"; } from "src/lib/ServerMessage";
import { import {
isConnectedUsersMessage,
isCurrentlyTypingMessage,
isIdResponseMessage, isIdResponseMessage,
isServerMessage, isServerMessage,
isTextMessage, isTextMessage,
@ -24,14 +28,20 @@ export default function Index(): JSX.Element {
const [currentlyTyping, setCurrentlyTyping] = useState<string[]>([]); const [currentlyTyping, setCurrentlyTyping] = useState<string[]>([]);
const [socketUrl, setSocketUrl] = useState("wss.tobot.tk:8085/"); const [socketUrl, setSocketUrl] = useState("wss.tobot.tk:8085/");
const [authorId, setAuthorId] = useState(""); const [authorId, setAuthorId] = useState("");
const [connectedUsers, setConnectedUsers] = useState<ConnectedUser[]>([]);
const desiredNameInput = useRef<HTMLInputElement>(null);
const messageInput = useRef<HTMLInputElement>(null); const messageInput = useRef<HTMLInputElement>(null);
const messageContainer = useRef<HTMLOListElement>(null); const messageContainer = useRef<HTMLOListElement>(null);
const getNickname = useCallback( const getName = useCallback(
(id: string) => { (id: string) => {
return id === authorId ? "You" : id; if (id === authorId) {
return "You";
} else {
return connectedUsers.find((user) => user.id === id)?.desiredName ?? id;
}
}, },
[authorId] [authorId, connectedUsers]
); );
function onMessage(event: MessageEvent<string>): void { function onMessage(event: MessageEvent<string>): void {
const message = JSON.parse(event.data); const message = JSON.parse(event.data);
@ -45,9 +55,12 @@ export default function Index(): JSX.Element {
setAuthorId(message.authorId); setAuthorId(message.authorId);
} else if (isTextMessage(message)) { } else if (isTextMessage(message)) {
setMessageHistory([...messageHistory, message]); setMessageHistory([...messageHistory, message]);
console.log("scrollheight", messageContainer.current?.scrollHeight);
} else if (isCurrentlyTypingMessage(message)) { } else if (isCurrentlyTypingMessage(message)) {
setCurrentlyTyping(message.currently); setCurrentlyTyping(message.currently);
} else if (isConnectedUsersMessage(message)) {
setConnectedUsers(message.connected);
} else {
console.warn("Server sent unhandled message", message);
} }
} }
@ -59,9 +72,17 @@ export default function Index(): JSX.Element {
websocket.sendJsonMessage({ websocket.sendJsonMessage({
type: MessageType.ACK, type: MessageType.ACK,
date: Date.now(), date: Date.now(),
}); } as AckMessage);
}, 1000); }, 1000);
keepAliveIntervals.push(keepAliveInterval); keepAliveIntervals.push(keepAliveInterval);
if (desiredNameInput.current?.value.length) {
websocket.sendJsonMessage({
type: MessageType.DESIRED_NAME,
date: Date.now(),
desiredName: desiredNameInput.current.value,
} as DesiredNameMessage);
}
}, },
onClose() { onClose() {
clearInterval(keepAliveInterval); clearInterval(keepAliveInterval);
@ -71,7 +92,7 @@ export default function Index(): JSX.Element {
}, },
}); });
const handleClickSendMessage = useCallback(() => { const trySendMessage = useCallback(() => {
if (!messageInput.current) { if (!messageInput.current) {
return; return;
} }
@ -93,7 +114,7 @@ export default function Index(): JSX.Element {
const trySetSocketUrl = useCallback( const trySetSocketUrl = useCallback(
(url: string) => { (url: string) => {
try { try {
console.log(new URL("wss://" + url), url); console.debug(new URL("wss://" + url), url);
setSocketUrl(url); setSocketUrl(url);
} catch (e) { } catch (e) {
console.debug("Invalid URL"); console.debug("Invalid URL");
@ -120,7 +141,6 @@ export default function Index(): JSX.Element {
setTimeout(() => (shouldResendTyping = true), 1000); setTimeout(() => (shouldResendTyping = true), 1000);
} }
} }
console.log(handleInput);
const typingIndicator = useMemo(() => { const typingIndicator = useMemo(() => {
if (currentlyTyping.length === 0) { if (currentlyTyping.length === 0) {
@ -136,10 +156,10 @@ export default function Index(): JSX.Element {
} }
return ( return (
<> <>
{getNickname(currentlyTyping[0]) === currentlyTyping[0] ? ( {getName(currentlyTyping[0]) === currentlyTyping[0] ? (
currentlyTyping[0] currentlyTyping[0]
) : ( ) : (
<span className="nickname">{getNickname(currentlyTyping[0])}</span> <span className="nickname">{getName(currentlyTyping[0])}</span>
)}{" "} )}{" "}
is typing... is typing...
</> </>
@ -148,10 +168,10 @@ export default function Index(): JSX.Element {
if (currentlyTyping.length < 4) { if (currentlyTyping.length < 4) {
const result = currentlyTyping.map((id, idx, arr) => ( const result = currentlyTyping.map((id, idx, arr) => (
<> <>
{id === getNickname(id) ? ( {id === getName(id) ? (
id id
) : ( ) : (
<span className="nickname">{getNickname(id)}</span> <span className="nickname">{getName(id)}</span>
)} )}
{idx + 1 === arr.length ? "" : ", "} {idx + 1 === arr.length ? "" : ", "}
</> </>
@ -161,7 +181,7 @@ export default function Index(): JSX.Element {
return <>{result}</>; return <>{result}</>;
} }
return <>Several people are typing...</>; return <>Several people are typing...</>;
}, [authorId, currentlyTyping, getNickname]); }, [authorId, currentlyTyping, getName]);
return ( return (
<main> <main>
@ -185,29 +205,62 @@ export default function Index(): JSX.Element {
placeholder="wss://..." placeholder="wss://..."
onChange={(e) => trySetSocketUrl(e.target.value)} onChange={(e) => trySetSocketUrl(e.target.value)}
/> />
<label htmlFor="desired-name-input">Name:</label>
<input
ref={desiredNameInput}
id="desired-name-input"
type="text"
placeholder="..."
/>
</header> </header>
<ol ref={messageContainer} id="messages-container"> <div id="container">
{messageHistory.map((message, idx) => ( <section id="message-area">
<li className="message" key={idx}> <ol ref={messageContainer} id="messages-container">
<MessageComponent {messageHistory.map((message, idx) => (
message={message} <li className="message" key={idx}>
authorNickname={getNickname(message.author)} <MessageComponent
/> message={message}
</li> authorNickname={getName(message.author)}
))} />
</ol> </li>
<div id="message-writing-area"> ))}
<span id="typing-indicators">{typingIndicator}</span> </ol>
<span> <div id="message-writing-area">
<input <span id="typing-indicators">{typingIndicator}</span>
type="text" <span>
placeholder="Type here..." <input
id="message-input" type="text"
onInput={handleInput} placeholder="Type here..."
ref={messageInput} id="message-input"
/> onInput={handleInput}
<button onClick={handleClickSendMessage}>Send</button> onKeyPress={(keyEvent) => {
</span> if (keyEvent.key === "Enter") {
trySendMessage();
}
}}
ref={messageInput}
/>
<button onClick={trySendMessage}>Send</button>
</span>
</div>
</section>
<section id="user-area">
<h2>Users</h2>
<ol>
{connectedUsers.map(({ id }) => (
<li
className={
"user" +
(currentlyTyping.includes(id) ? " typing" : "") +
(getName(id) !== id ? " nickname" : "")
}
key={id}
>
{getName(id)}
</li>
))}
</ol>
</section>
</div> </div>
</main> </main>
); );

View file

@ -33,8 +33,8 @@ main {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: calc(100vh - 20px); height: calc(100vh - 40px);
width: calc(100vw - 20px); width: calc(100vw - 40px);
justify-content: space-between; justify-content: space-between;
padding: 10px; padding: 10px;
@ -42,45 +42,72 @@ main {
font-style: italic; font-style: italic;
} }
#messages-container { #container {
min-height: 100%;
max-height: 100%;
display: flex; display: flex;
flex-direction: column;
flex-grow: 1; flex-grow: 1;
overflow-y: scroll; #message-area {
overflow-anchor: none; display: flex;
list-style: none; flex-direction: column;
padding-inline-start: 0; flex-grow: 80;
max-width: 100%; #messages-container {
overflow-wrap: break-word; display: flex;
flex-direction: column;
flex-grow: 1;
.message { overflow-y: scroll;
display: block; overflow-anchor: none;
text-align: left; list-style: none;
width: calc(100% - 2 * 5px); padding-inline-start: 0;
padding: 5px; max-width: 100%;
white-space: pre-line; overflow-wrap: break-word;
&:nth-child(even) { .message {
background-color: #f2f2f2; display: block;
text-align: left;
width: calc(100% - 2 * 5px);
padding: 5px;
white-space: pre-line;
&:nth-child(even) {
background-color: #f2f2f2;
}
> h2 {
width: 100%;
display: inline-flex;
margin: 0;
font-size: medium;
justify-content: space-between;
}
}
} }
#message-writing-area {
display: flex;
flex-direction: column;
justify-content: center;
> h2 { #message-input {
width: 100%; width: 80%;
display: inline-flex; }
margin: 0;
font-size: medium;
justify-content: space-between;
} }
} }
}
#message-writing-area {
display: flex;
flex-direction: column;
justify-content: center;
#message-input { #user-area {
width: 80%; display: flex;
flex-direction: column;
flex-grow: 20;
ol {
overflow-y: scroll;
max-height: 100%;
> li:nth-of-type(even) {
background-color: #f2f2f2;
}
}
} }
} }
} }