Fill out some more components + add application state enums
This commit is contained in:
@@ -1,5 +1,46 @@
|
||||
interface Props {}
|
||||
import { Menu, Dropdown } from 'antd';
|
||||
import { DownOutlined } from '@ant-design/icons';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { ChatVisibilityState, ChatState } from '../interfaces/application-state';
|
||||
import { chatVisibility as chatVisibilityAtom } from './stores/ClientConfigStore';
|
||||
|
||||
interface Props {
|
||||
username: string;
|
||||
chatState: ChatState;
|
||||
}
|
||||
|
||||
export default function UserDropdown(props: Props) {
|
||||
return <div>User settings dropdown component goes here</div>;
|
||||
const { username, chatState } = props;
|
||||
|
||||
const chatEnabled = chatState !== ChatState.NotAvailable;
|
||||
const [chatVisibility, setChatVisibility] =
|
||||
useRecoilState<ChatVisibilityState>(chatVisibilityAtom);
|
||||
|
||||
const toggleChatVisibility = () => {
|
||||
if (chatVisibility === ChatVisibilityState.Hidden) {
|
||||
setChatVisibility(ChatVisibilityState.Visible);
|
||||
} else {
|
||||
setChatVisibility(ChatVisibilityState.Hidden);
|
||||
}
|
||||
};
|
||||
|
||||
const menu = (
|
||||
<Menu>
|
||||
<Menu.Item key="0">Change name</Menu.Item>
|
||||
<Menu.Item key="1">Authenticate</Menu.Item>
|
||||
{chatEnabled && (
|
||||
<Menu.Item key="3" onClick={() => toggleChatVisibility()}>
|
||||
Toggle chat
|
||||
</Menu.Item>
|
||||
)}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
return (
|
||||
<Dropdown overlay={menu} trigger={['click']}>
|
||||
<button type="button" className="ant-dropdown-link" onClick={e => e.preventDefault()}>
|
||||
{username} <DownOutlined />
|
||||
</button>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
import { Spin } from 'antd';
|
||||
import { ChatMessage } from '../../interfaces/chat-message.model';
|
||||
import { ChatState } from '../../interfaces/application-state';
|
||||
|
||||
interface Props {
|
||||
messages: ChatMessage[];
|
||||
state: ChatState;
|
||||
}
|
||||
|
||||
export default function ChatContainer(props: Props) {
|
||||
return <div>Chat container goes here</div>;
|
||||
const { messages, state } = props;
|
||||
const loading = state === ChatState.Loading;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Spin tip="Loading..." spinning={loading}>
|
||||
Chat container with scrolling chat messages go here
|
||||
</Spin>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,35 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
interface Props {}
|
||||
|
||||
export default function ChatTextField(props: Props) {
|
||||
return <div>Component goes here</div>;
|
||||
const [value, setValue] = useState('');
|
||||
const [showEmojis, setShowEmojis] = useState(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
value={value}
|
||||
onChange={e => setValue(e.target.value)}
|
||||
placeholder="Type a message here then hit ENTER"
|
||||
/>
|
||||
<button type="button" onClick={() => setShowEmojis(!showEmojis)}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="icon"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M14.828 14.828a4 4 0 01-5.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,21 @@ import { ChatMessage } from '../../interfaces/chat-message.model';
|
||||
|
||||
interface Props {
|
||||
message: ChatMessage;
|
||||
showModeratorMenu: boolean;
|
||||
}
|
||||
|
||||
export default function ChatUserMessage(props: Props) {
|
||||
return <div>Component goes here</div>;
|
||||
const { message, showModeratorMenu } = props;
|
||||
const { body, user, timestamp } = message;
|
||||
const { displayName, displayColor } = user;
|
||||
|
||||
// TODO: Convert displayColor (a hue) to a usable color.
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>{displayName}</div>
|
||||
<div>{body}</div>
|
||||
{showModeratorMenu && <div>Moderator menu</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { useEffect } from 'react';
|
||||
import { ReactElement } from 'react-markdown/lib/react-markdown';
|
||||
import { atom, useRecoilState } from 'recoil';
|
||||
import { makeEmptyClientConfig, ClientConfig } from '../../interfaces/client-config.model';
|
||||
import ClientConfigService from '../../services/client-config-service';
|
||||
import ChatService from '../../services/chat-service';
|
||||
import { ChatMessage } from '../../interfaces/chat-message.model';
|
||||
import { getLocalStorage, setLocalStorage } from '../../utils/helpers';
|
||||
import { ChatVisibilityState } from '../../interfaces/application-state';
|
||||
|
||||
// The config that comes from the API.
|
||||
export const clientConfigState = atom({
|
||||
@@ -11,14 +13,19 @@ export const clientConfigState = atom({
|
||||
default: makeEmptyClientConfig(),
|
||||
});
|
||||
|
||||
export const chatCurrentlyVisible = atom({
|
||||
key: 'chatvisible',
|
||||
default: false,
|
||||
export const chatVisibility = atom<ChatVisibilityState>({
|
||||
key: 'chatVisibility',
|
||||
default: ChatVisibilityState.Hidden,
|
||||
});
|
||||
|
||||
export const chatDisplayName = atom({
|
||||
key: 'chatDisplayName',
|
||||
default: '',
|
||||
default: null,
|
||||
});
|
||||
|
||||
export const accessTokenAtom = atom({
|
||||
key: 'accessToken',
|
||||
default: null,
|
||||
});
|
||||
|
||||
export const chatMessages = atom({
|
||||
@@ -26,8 +33,11 @@ export const chatMessages = atom({
|
||||
default: [] as ChatMessage[],
|
||||
});
|
||||
|
||||
export function ClientConfigStore(): ReactElement {
|
||||
export function ClientConfigStore() {
|
||||
const [, setClientConfig] = useRecoilState<ClientConfig>(clientConfigState);
|
||||
const [, setChatMessages] = useRecoilState<ChatMessage[]>(chatMessages);
|
||||
const [accessToken, setAccessToken] = useRecoilState<string>(accessTokenAtom);
|
||||
const [, setChatDisplayName] = useRecoilState<string>(chatDisplayName);
|
||||
|
||||
const updateClientConfig = async () => {
|
||||
try {
|
||||
@@ -39,9 +49,41 @@ export function ClientConfigStore(): ReactElement {
|
||||
}
|
||||
};
|
||||
|
||||
const handleUserRegistration = async (optionalDisplayName: string) => {
|
||||
try {
|
||||
const response = await ChatService.registerUser(optionalDisplayName);
|
||||
console.log(`ChatService -> registerUser() response: \n${JSON.stringify(response)}`);
|
||||
const { accessToken: newAccessToken, displayName } = response;
|
||||
if (!newAccessToken) {
|
||||
return;
|
||||
}
|
||||
setAccessToken(accessToken);
|
||||
setLocalStorage('accessToken', newAccessToken);
|
||||
setChatDisplayName(displayName);
|
||||
} catch (e) {
|
||||
console.error(`ChatService -> registerUser() ERROR: \n${e}`);
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Requires access token.
|
||||
const getChatHistory = async () => {
|
||||
try {
|
||||
const messages = await ChatService.getChatHistory(accessToken);
|
||||
console.log(`ChatService -> getChatHistory() messages: \n${JSON.stringify(messages)}`);
|
||||
setChatMessages(messages);
|
||||
} catch (error) {
|
||||
console.error(`ChatService -> getChatHistory() ERROR: \n${error}`);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
updateClientConfig();
|
||||
handleUserRegistration();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
getChatHistory();
|
||||
}, [accessToken]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useEffect } from 'react';
|
||||
import { ReactElement } from 'react-markdown/lib/react-markdown';
|
||||
import { atom, useRecoilState } from 'recoil';
|
||||
import { ServerStatus, makeEmptyServerStatus } from '../../interfaces/server-status.model';
|
||||
import ServerStatusService from '../../services/status-service';
|
||||
@@ -9,7 +8,7 @@ export const serverStatusState = atom({
|
||||
default: makeEmptyServerStatus(),
|
||||
});
|
||||
|
||||
export function ServerStatusStore(): ReactElement {
|
||||
export function ServerStatusStore() {
|
||||
const [, setServerStatus] = useRecoilState<ServerStatus>(serverStatusState);
|
||||
|
||||
const updateServerStatus = async () => {
|
||||
|
||||
@@ -2,14 +2,16 @@ import Sider from 'antd/lib/layout/Sider';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { ChatMessage } from '../../../interfaces/chat-message.model';
|
||||
import ChatContainer from '../../chat/ChatContainer';
|
||||
import { chatMessages } from '../../stores/ClientConfigStore';
|
||||
import { chatMessages, chatVisibility as chatVisibilityAtom } from '../../stores/ClientConfigStore';
|
||||
import { ChatVisibilityState } from '../../../interfaces/application-state';
|
||||
|
||||
export default function Sidebar() {
|
||||
const messages = useRecoilValue<ChatMessage[]>(chatMessages);
|
||||
const chatVisibility = useRecoilValue<ChatVisibilityState>(chatVisibilityAtom);
|
||||
|
||||
return (
|
||||
<Sider
|
||||
collapsed={false}
|
||||
collapsed={chatVisibility === ChatVisibilityState.Hidden}
|
||||
width={300}
|
||||
style={{
|
||||
position: 'fixed',
|
||||
|
||||
Reference in New Issue
Block a user