Chat is working with a local chat-server
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
//react
|
//react
|
||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { BrowserRouter, Switch } from 'react-router-dom';
|
import { BrowserRouter, Switch } from 'react-router-dom';
|
||||||
|
import { TokenContext } from './utilities/token-provider';
|
||||||
|
|
||||||
//library components
|
//library components
|
||||||
import LazyRoute from './lazy-route';
|
import LazyRoute from './lazy-route';
|
||||||
@@ -15,6 +16,8 @@ import Footer from './panels/footer';
|
|||||||
import PopupChat from './panels/popup-chat';
|
import PopupChat from './panels/popup-chat';
|
||||||
|
|
||||||
const App = props => {
|
const App = props => {
|
||||||
|
const authTokens = useContext(TokenContext);
|
||||||
|
|
||||||
//default render
|
//default render
|
||||||
return (
|
return (
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
@@ -33,7 +36,7 @@ const App = props => {
|
|||||||
|
|
||||||
<LazyRoute path='*' component={() => import('./pages/not-found')} />
|
<LazyRoute path='*' component={() => import('./pages/not-found')} />
|
||||||
</Switch>
|
</Switch>
|
||||||
<PopupChat />
|
{ authTokens.accessToken ? <PopupChat /> : <></> }
|
||||||
<Footer />
|
<Footer />
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,38 +1,38 @@
|
|||||||
import React, { useState, useRef } from 'react';
|
import React, { useState, useEffect, useRef, useContext } from 'react';
|
||||||
//import { io } from 'socket.io-client';
|
import { TokenContext } from '../utilities/token-provider';
|
||||||
|
import { io } from 'socket.io-client';
|
||||||
|
|
||||||
import '../../styles/popup-chat.css';
|
import '../../styles/popup-chat.css';
|
||||||
|
|
||||||
//const socket = io(`${process.env.CHAT_URI}/chat`);
|
const socket = io(`${process.env.CHAT_URI}/chat`);
|
||||||
|
|
||||||
const PopupChat = props => {
|
const PopupChat = props => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [chatlog, setChatlog] = useState([
|
const [chatlog, setChatlog] = useState([]);
|
||||||
{ username: 'foo', text: 'bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar '},
|
|
||||||
{ username: 'foo', text: 'bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar '},
|
|
||||||
{ username: 'foo', text: 'bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar '},
|
|
||||||
{ username: 'foo', text: 'bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar '},
|
|
||||||
{ username: 'foo', text: 'bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar '},
|
|
||||||
{ username: 'foo', text: 'bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar '},
|
|
||||||
{ username: 'foo', text: 'bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar '},
|
|
||||||
{ username: 'foo', text: 'bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar '},
|
|
||||||
{ username: 'foo', text: 'bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar '},
|
|
||||||
{ username: 'foo', text: 'bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar '},
|
|
||||||
{ username: 'foo', text: 'bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar '},
|
|
||||||
{ username: 'foo', text: 'bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar '},
|
|
||||||
{ username: 'foo', text: 'bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar '},
|
|
||||||
{ username: 'foo', text: 'bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar '},
|
|
||||||
{ username: 'foo', text: 'bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar '},
|
|
||||||
{ username: 'foo', text: 'bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar '},
|
|
||||||
{ username: 'foo', text: 'bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar '}
|
|
||||||
]);
|
|
||||||
|
|
||||||
const inputRef = useRef();
|
const inputRef = useRef();
|
||||||
|
const sendRef = useRef();
|
||||||
|
const endRef = useRef();
|
||||||
|
|
||||||
|
const authTokens = useContext(TokenContext);
|
||||||
|
|
||||||
|
const pushChatlog = line => setChatlog(prevChatlog => [...prevChatlog, line]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
socket.on('message', message => pushChatlog(message));
|
||||||
|
socket.on('disconnect', reason => pushChatlog({ emphasis: true, text: 'Lost connection' }));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open) {
|
||||||
|
endRef.current.scrollIntoView();
|
||||||
|
}
|
||||||
|
}, [chatlog, open]);
|
||||||
|
|
||||||
if (!open) {
|
if (!open) {
|
||||||
return (
|
return (
|
||||||
<div className='chat'>
|
<div className='chat'>
|
||||||
<button type='button' className='open' onClick={() => handleOpen(setOpen)}>Open Chat</button>
|
<button type='button' className='open' onClick={() => authTokens.tokenCallback(accessToken => handleOpen(setOpen, accessToken))}>Open Chat</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -41,32 +41,57 @@ const PopupChat = props => {
|
|||||||
<div className='chat'>
|
<div className='chat'>
|
||||||
<div className='log'>
|
<div className='log'>
|
||||||
<ul className='scrollable'>
|
<ul className='scrollable'>
|
||||||
{chatlog.map((line, index) => <li key={index} className='line'><span className='username'>{line.username}: </span><span className='text'>{line.text}</span></li>)}
|
{chatlog.map(processLine)}
|
||||||
|
<li ref={endRef} />
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<input type='text' className='input' placeholder='message' ref={inputRef}></input>
|
<input type='text' className='input' placeholder='message' onKeyPress={evt => evt.key == 'Enter' ? sendRef.current.click() : ''} ref={inputRef} />
|
||||||
<button type='button' className='send' onClick={() => handleSend(inputRef)}>Send</button>
|
<button type='button' className='send' onClick={() => authTokens.tokenCallback(accessToken => handleSend(inputRef, pushChatlog, authTokens.getPayload().username, accessToken))} ref={sendRef}>Send</button>
|
||||||
<button type='button' className='close' onClick={() => handleClose(setOpen)}>Close Chat</button>
|
<button type='button' className='close' onClick={() => handleClose(setOpen)}>Close Chat</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOpen = setOpen => {
|
//handlers
|
||||||
|
const handleOpen = (setOpen, accessToken) => {
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
|
|
||||||
//TODO
|
socket.emit('open chat', {
|
||||||
|
accessToken
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = setOpen => {
|
const handleClose = setOpen => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
|
|
||||||
//TODO
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSend = inputRef => {
|
const handleSend = (inputRef, pushChatlog, username, accessToken) => {
|
||||||
//TODO
|
socket.emit('message', {
|
||||||
|
accessToken,
|
||||||
|
text: inputRef.current.value
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!inputRef.current.value.startsWith('/')) {
|
||||||
|
pushChatlog({ username: username, text: inputRef.current.value });
|
||||||
|
}
|
||||||
|
|
||||||
inputRef.current.value = '';
|
inputRef.current.value = '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//render each line
|
||||||
|
const processLine = (line, index) => {
|
||||||
|
let content = <span>{line.username ? <span className='username'>{line.username}: </span> : ''}{line.text ? <span className='text'>{line.text}</span> : ''}</span>;
|
||||||
|
|
||||||
|
//decorators
|
||||||
|
if (line.emphasis) {
|
||||||
|
content = <em>{content}</em>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.strong) {
|
||||||
|
content = <strong>{content}</strong>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return<li key={index} className='line'>{content}</li>;
|
||||||
|
};
|
||||||
|
|
||||||
export default PopupChat;
|
export default PopupChat;
|
||||||
@@ -11,7 +11,7 @@ const TokenProvider = props => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setAccessToken(localStorage.getItem("accessToken") || '');
|
setAccessToken(localStorage.getItem("accessToken") || '');
|
||||||
setRefreshToken(localStorage.getItem("refreshToken") || '');
|
setRefreshToken(localStorage.getItem("refreshToken") || '');
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
localStorage.setItem("accessToken", accessToken);
|
localStorage.setItem("accessToken", accessToken);
|
||||||
@@ -79,11 +79,42 @@ const TokenProvider = props => {
|
|||||||
|
|
||||||
//access the refreshed token via callback
|
//access the refreshed token via callback
|
||||||
const tokenCallback = async (cb) => {
|
const tokenCallback = async (cb) => {
|
||||||
//TODO: tokenCallback
|
//if expired (10 minutes, normally)
|
||||||
|
const expired = new Date(decode(accessToken).exp * 1000) < Date.now();
|
||||||
|
|
||||||
|
if (expired) {
|
||||||
|
//ping the auth server for a new token
|
||||||
|
const response = await fetch(`${process.env.AUTH_URI}/auth/token`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Access-Control-Allow-Origin': '*'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
token: refreshToken
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
//any errors, throw them
|
||||||
|
if (!response.ok) {
|
||||||
|
throw `${response.status}: ${await response.text()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
//save the new auth stuff (setting bearer as well)
|
||||||
|
const newAuth = await response.json();
|
||||||
|
|
||||||
|
setAccessToken(newAuth.accessToken);
|
||||||
|
setRefreshToken(newAuth.refreshToken);
|
||||||
|
|
||||||
|
//finally
|
||||||
|
return cb(newAuth.accessToken);
|
||||||
|
} else {
|
||||||
|
return cb(accessToken);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TokenContext.Provider value={{ accessToken, refreshToken, setAccessToken, setRefreshToken, tokenFetch, getPayload: () => decode(accessToken) }}>
|
<TokenContext.Provider value={{ accessToken, refreshToken, setAccessToken, setRefreshToken, tokenFetch, tokenCallback, getPayload: () => decode(accessToken) }}>
|
||||||
{props.children}
|
{props.children}
|
||||||
</TokenContext.Provider>
|
</TokenContext.Provider>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user