Rudimentary usernames
This commit is contained in:
parent
2080d58c06
commit
128b092824
3 changed files with 149 additions and 69 deletions
2
src/lib
2
src/lib
|
@ -1 +1 @@
|
||||||
Subproject commit cde3d62b1c0a1c95643037ca03e95bac178118d2
|
Subproject commit a618eda73268a675e0c90dace9751ad05d5e0443
|
|
@ -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,13 +205,22 @@ 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>
|
||||||
|
<div id="container">
|
||||||
|
<section id="message-area">
|
||||||
<ol ref={messageContainer} id="messages-container">
|
<ol ref={messageContainer} id="messages-container">
|
||||||
{messageHistory.map((message, idx) => (
|
{messageHistory.map((message, idx) => (
|
||||||
<li className="message" key={idx}>
|
<li className="message" key={idx}>
|
||||||
<MessageComponent
|
<MessageComponent
|
||||||
message={message}
|
message={message}
|
||||||
authorNickname={getNickname(message.author)}
|
authorNickname={getName(message.author)}
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
@ -204,11 +233,35 @@ export default function Index(): JSX.Element {
|
||||||
placeholder="Type here..."
|
placeholder="Type here..."
|
||||||
id="message-input"
|
id="message-input"
|
||||||
onInput={handleInput}
|
onInput={handleInput}
|
||||||
|
onKeyPress={(keyEvent) => {
|
||||||
|
if (keyEvent.key === "Enter") {
|
||||||
|
trySendMessage();
|
||||||
|
}
|
||||||
|
}}
|
||||||
ref={messageInput}
|
ref={messageInput}
|
||||||
/>
|
/>
|
||||||
<button onClick={handleClickSendMessage}>Send</button>
|
<button onClick={trySendMessage}>Send</button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</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>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,6 +42,16 @@ main {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
min-height: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
#message-area {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 80;
|
||||||
#messages-container {
|
#messages-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -84,3 +94,20 @@ main {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#user-area {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 20;
|
||||||
|
|
||||||
|
ol {
|
||||||
|
overflow-y: scroll;
|
||||||
|
max-height: 100%;
|
||||||
|
|
||||||
|
> li:nth-of-type(even) {
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Reference in a new issue