update api to use new session stfuf

This commit is contained in:
Michael Fatemi 2021-07-03 00:39:10 -04:00
parent 86f327bf9e
commit 557d554db3
10 changed files with 151 additions and 53 deletions

View File

@ -1,6 +1,9 @@
import WheelShare from './WheelShare'; import { lazy, Suspense, useContext } from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom'; import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { lazy, Suspense } from 'react'; import AuthenticationContext from './Authentication/AuthenticationContext';
import logout from './Authentication/logout';
import UIButton from './UIButton';
import WheelShare from './WheelShare';
const Authenticator = lazy(() => import('./Authentication/Authenticator')); const Authenticator = lazy(() => import('./Authentication/Authenticator'));
const Group = lazy(() => import('./Group')); const Group = lazy(() => import('./Group'));
@ -11,23 +14,37 @@ const ION_AUTHORIZATION_ENDPOINT = dev
: 'https://ion.tjhsst.edu/oauth/authorize?response_type=code&client_id=rNa6n9YSg8ftINdyVPpUsaMuxNbHLo9dh1OsOktR&scope=read&redirect_uri=https%3A%2F%2Fwheelshare.space%2Fauth%2Fion%2Fcallback'; : 'https://ion.tjhsst.edu/oauth/authorize?response_type=code&client_id=rNa6n9YSg8ftINdyVPpUsaMuxNbHLo9dh1OsOktR&scope=read&redirect_uri=https%3A%2F%2Fwheelshare.space%2Fauth%2Fion%2Fcallback';
export default function App() { export default function App() {
const { isLoggedIn, user } = useContext(AuthenticationContext);
return ( return (
<>
<a href={ION_AUTHORIZATION_ENDPOINT}>Login Link for Testing Oauth</a>
<div style={{ padding: '1rem' }}> <div style={{ padding: '1rem' }}>
{isLoggedIn ? (
<div
style={{
display: 'flex',
flexDirection: 'row',
backgroundColor: '#f6f6f6',
borderRadius: '0.5rem',
padding: '1rem',
alignItems: 'center',
}}
>
{user!.name}{' '}
<UIButton style={{ marginTop: 0 }} onClick={logout}>
Log out
</UIButton>
</div>
) : (
<a href={ION_AUTHORIZATION_ENDPOINT}>Log in</a>
)}
<BrowserRouter> <BrowserRouter>
<Switch> <Switch>
<Route path="/" exact component={WheelShare} /> <Route path="/" exact component={WheelShare} />
<Suspense fallback={null}> <Suspense fallback={null}>
<Route path="/groups/:id" component={Group} /> <Route path="/groups/:id" component={Group} />
<Route <Route component={Authenticator} path="/auth/:provider/callback" />
component={Authenticator}
path="/auth/:provider/callback"
/>
</Suspense> </Suspense>
</Switch> </Switch>
</BrowserRouter> </BrowserRouter>
</div> </div>
</>
); );
} }

View File

@ -1,8 +1,14 @@
import { createContext } from 'react'; import { createContext } from 'react';
export type User = {
name: string;
id: number;
email?: string;
};
export type AuthState = { export type AuthState = {
isLoggedIn: boolean | null; isLoggedIn: boolean | null;
user: Carpool.User | null; user: User | null;
/** /**
* Function that can be used to trigger an auth state refresh. * Function that can be used to trigger an auth state refresh.

View File

@ -16,16 +16,21 @@ export default function AuthenticationWrapper({
}); });
const refreshAuthState = useCallback(() => { const refreshAuthState = useCallback(() => {
const loggedOut = () =>
setAuthState({ isLoggedIn: false, user: null, refreshAuthState });
if (sessionToken) { if (sessionToken) {
getMe().then((user) => { getMe()
.then((user) => {
if (user) { if (user) {
setAuthState({ isLoggedIn: true, user, refreshAuthState }); setAuthState({ isLoggedIn: true, user, refreshAuthState });
} else { } else {
setAuthState({ isLoggedIn: false, user: null, refreshAuthState }); loggedOut();
} }
}); })
.catch(loggedOut);
} else { } else {
setAuthState({ isLoggedIn: false, user: null, refreshAuthState }); loggedOut();
} }
}, [sessionToken]); }, [sessionToken]);

View File

@ -10,22 +10,31 @@ export default function Authenticator() {
const { refreshAuthState } = useContext(AuthenticationContext); const { refreshAuthState } = useContext(AuthenticationContext);
const [status, setStatus] = const [status, setStatus] =
useState<'pending' | 'errored' | 'authenticated'>('pending'); useState<'pending' | 'errored' | 'authenticated'>('pending');
const [token, setToken] = useState<string | null>(null);
useEffect(() => { useEffect(() => {
createSession(code!) createSession(code!)
.then((data) => { .then((data) => {
if (data.status === 'success') { if (data.status === 'success') {
console.log('Success! Token:', data.token);
setToken(data.token);
localStorage.setItem('session_token', data.token); localStorage.setItem('session_token', data.token);
refreshAuthState && refreshAuthState();
setStatus('authenticated'); setStatus('authenticated');
} else { } else {
console.log('Authentication failure.');
setToken(null);
localStorage.removeItem('session_token');
setStatus('errored'); setStatus('errored');
} }
}) })
.catch(() => { .catch(() => {
setStatus('errored'); setStatus('errored');
}); });
}, [code, provider, refreshAuthState]); }, [code, provider]);
useEffect(() => {
refreshAuthState && refreshAuthState();
}, [token, refreshAuthState]);
switch (status) { switch (status) {
case 'authenticated': case 'authenticated':

View File

@ -0,0 +1,4 @@
export default function logout() {
localStorage.removeItem('session_token');
window.location.href = '/';
}

View File

@ -1,5 +1,5 @@
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { post, removeEventSignup } from './api'; import { addEventSignup, removeEventSignup } from './api';
import { green, lightgrey } from './colors'; import { green, lightgrey } from './colors';
import latlongdist, { R_miles } from './latlongdist'; import latlongdist, { R_miles } from './latlongdist';
import UIButton from './UIButton'; import UIButton from './UIButton';
@ -313,15 +313,14 @@ export default function Event({ event }: { event: IEvent }) {
if ( if (
interested === true && interested === true &&
(prev.placeId !== placeId || prev.eventId !== event.id) (prev.placeId !== placeId || prev.eventId !== event.id) &&
placeId !== null
) { ) {
prev.placeId = placeId; prev.placeId = placeId;
prev.eventId = event.id; prev.eventId = event.id;
prev.interested = true; prev.interested = true;
post(`/events/${event.id}/signup`, { addEventSignup(event.id, placeId!).finally(() => setUpdating(false));
placeId,
}).finally(() => setUpdating(false));
return; return;
} }
}, [event.id, interested, placeId, updating]); }, [event.id, interested, placeId, updating]);

View File

@ -1,5 +1,5 @@
import { Dispatch, SetStateAction, useCallback, useState } from 'react'; import { Dispatch, SetStateAction, useCallback, useState } from 'react';
import { post } from './api'; import { createEvent } from './api';
import { toggleBit } from './bits'; import { toggleBit } from './bits';
import { green, lightgrey } from './colors'; import { green, lightgrey } from './colors';
import { IGroup } from './Group'; import { IGroup } from './Group';
@ -120,7 +120,7 @@ export default function EventCreator({ group }: { group: IGroup }) {
!durationIsNegative && !durationIsNegative &&
!creating; !creating;
const createEvent = useCallback(() => { const onClickedCreateEvent = useCallback(() => {
if (!creating) { if (!creating) {
if (startTime === null) { if (startTime === null) {
console.warn( console.warn(
@ -129,12 +129,19 @@ export default function EventCreator({ group }: { group: IGroup }) {
return; return;
} }
if (placeId === null) {
console.warn(
'Tried tro create an event where the placeId was unspecified.'
);
return;
}
const duration = const duration =
endTime !== null ? (endTime.getTime() - startTime.getTime()) / 60 : 0; endTime !== null ? (endTime.getTime() - startTime.getTime()) / 60 : 0;
setCreating(true); setCreating(true);
post('/events', { createEvent({
name, name,
startTime, startTime,
duration, duration,
@ -143,7 +150,6 @@ export default function EventCreator({ group }: { group: IGroup }) {
placeId, placeId,
daysOfWeek, daysOfWeek,
}) })
.then((response) => response.json())
.then(({ id }) => { .then(({ id }) => {
setCreatedEventId(id); setCreatedEventId(id);
}) })
@ -211,7 +217,7 @@ export default function EventCreator({ group }: { group: IGroup }) {
)} )}
{createdEventId === -1 ? ( {createdEventId === -1 ? (
<UIButton <UIButton
onClick={buttonEnabled ? createEvent : noop} onClick={buttonEnabled ? onClickedCreateEvent : noop}
style={!buttonEnabled ? { color: 'grey' } : {}} style={!buttonEnabled ? { color: 'grey' } : {}}
> >
{creating ? 'Creating event' : 'Create event'} {creating ? 'Creating event' : 'Create event'}

View File

@ -1,5 +1,5 @@
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import { post } from './api'; import { createGroup } from './api';
import UIButton from './UIButton'; import UIButton from './UIButton';
import UILink from './UILink'; import UILink from './UILink';
import UISecondaryBox from './UISecondaryBox'; import UISecondaryBox from './UISecondaryBox';
@ -11,13 +11,10 @@ export default function GroupCreator() {
useState<boolean | null>(null); useState<boolean | null>(null);
const [createdGroupId, setCreatedGroupId] = useState(0); const [createdGroupId, setCreatedGroupId] = useState(0);
const [creating, setCreating] = useState(false); const [creating, setCreating] = useState(false);
const createGroup = useCallback(() => { const onClickedCreateGroup = useCallback(() => {
if (!creating) { if (!creating) {
setCreating(true); setCreating(true);
post('/groups', { createGroup(name)
name,
})
.then((res) => res.json())
.then(({ id }) => { .then(({ id }) => {
setCreationSuccessful(true); setCreationSuccessful(true);
setCreatedGroupId(id); setCreatedGroupId(id);
@ -36,7 +33,7 @@ export default function GroupCreator() {
Name Name
<UITextInput onChangeText={setName} value={name} /> <UITextInput onChangeText={setName} value={name} />
<UIButton <UIButton
onClick={createGroup} onClick={onClickedCreateGroup}
style={!buttonEnabled ? { color: 'grey' } : {}} style={!buttonEnabled ? { color: 'grey' } : {}}
> >
{creating ? 'Creating group' : 'Create group'} {creating ? 'Creating group' : 'Create group'}

View File

@ -1,26 +1,32 @@
export async function post(path: string, data: any) { async function post(path: string, data: any) {
const res = await fetch('http://localhost:5000/api' + path, { const res = await fetch('http://localhost:5000/api' + path, {
method: 'post', method: 'post',
body: JSON.stringify(data), body: JSON.stringify(data),
headers: { headers: {
Authorization: 'Bearer ' + localStorage.getItem('session_token'),
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
}); });
return await res.json(); return await res.json();
} }
export async function delete$(path: string) { async function delete$(path: string) {
const res = await fetch('http://localhost:5000/api' + path, { const res = await fetch('http://localhost:5000/api' + path, {
method: 'delete', method: 'delete',
headers: { headers: {
Authorization: 'Bearer ' + localStorage.getItem('session_token'),
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
}); });
return await res.json(); return await res.json();
} }
export async function get(path: string) { async function get(path: string) {
const res = await fetch('http://localhost:5000/api' + path); const res = await fetch('http://localhost:5000/api' + path, {
headers: {
Authorization: 'Bearer ' + localStorage.getItem('session_token'),
},
});
return await res.json(); return await res.json();
} }
@ -41,10 +47,47 @@ export async function getPlaceDetails(
return await get('/place/' + placeId); return await get('/place/' + placeId);
} }
export async function addEventSignup(eventId: number, placeId: string) {
post(`/events/${eventId}/signup`, {
placeId,
});
}
export async function removeEventSignup(eventId: number) { export async function removeEventSignup(eventId: number) {
return await delete$(`/events/${eventId}/signup`); return await delete$(`/events/${eventId}/signup`);
} }
export async function createEvent({
name,
startTime,
duration,
endDate,
groupId,
placeId,
daysOfWeek,
}: {
name: string;
startTime: Date;
duration: number;
endDate: Date | null;
groupId: number;
placeId: string;
daysOfWeek: number;
}) {
const { id } = await post('/events', {
name,
startTime,
duration,
endDate,
groupId,
placeId,
daysOfWeek,
});
return {
id,
};
}
export async function getEvents() { export async function getEvents() {
return await get('/events'); return await get('/events');
} }
@ -65,6 +108,15 @@ export async function deleteGroup(id: number) {
return await delete$('/groups/' + id); return await delete$('/groups/' + id);
} }
export async function createGroup(name: string) {
const result = await post('/groups', {
name,
});
return {
id: result.id,
};
}
export async function getMe() { export async function getMe() {
return await get('/users/@me'); return await get('/users/@me');
} }

View File

@ -3,10 +3,13 @@ import ReactDOM from 'react-dom';
import './index.css'; import './index.css';
import App from './components/App'; import App from './components/App';
import reportWebVitals from './reportWebVitals'; import reportWebVitals from './reportWebVitals';
import AuthenticationWrapper from './components/Authentication/AuthenticationWrapper';
ReactDOM.render( ReactDOM.render(
<React.StrictMode> <React.StrictMode>
<AuthenticationWrapper>
<App /> <App />
</AuthenticationWrapper>
</React.StrictMode>, </React.StrictMode>,
document.getElementById('root') document.getElementById('root')
); );