Merge branch 'main' of github.com:myfatemi04/wheelshare-frontend into main

This commit is contained in:
Joshua Hsueh 2021-07-06 22:44:29 -04:00
commit efa076e6d5
6 changed files with 166 additions and 35649 deletions

35636
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -3,17 +3,25 @@ import { Redirect, useLocation, useParams } from 'react-router-dom';
import AuthenticationContext from './AuthenticationContext'; import AuthenticationContext from './AuthenticationContext';
import { createSession } from './createSession'; import { createSession } from './createSession';
function useCode() { function useCodeAndError() {
const location = useLocation(); const location = useLocation();
const query = new URLSearchParams(location.search); const query = new URLSearchParams(location.search);
const code = query.get('code'); const code = query.get('code');
const error = query.get('error');
return code; return [code, error];
}
function inferRedirectUrl() {
// Strip query parameters
const { protocol, host, pathname } = window.location;
const redirectUrl = `${protocol}//${host}${pathname}`;
return redirectUrl;
} }
export default function Authenticator() { export default function Authenticator() {
const { provider } = useParams<{ provider: string }>(); const { provider } = useParams<{ provider: string }>();
const code = useCode(); const [code, error] = useCodeAndError();
const { refresh } = useContext(AuthenticationContext); const { refresh } = useContext(AuthenticationContext);
const [pending, setPending] = useState(true); const [pending, setPending] = useState(true);
@ -28,27 +36,54 @@ export default function Authenticator() {
}, [token]); }, [token]);
useEffect(() => { useEffect(() => {
setPending(true); if (code) {
createSession(code!) setPending(true);
.then(({ token }) => { createSession(code, inferRedirectUrl())
setToken(token ?? null); .then(({ token }) => {
}) setToken(token ?? null);
.finally(() => setPending(false)); })
.finally(() => setPending(false));
}
}, [code, provider]); }, [code, provider]);
useEffect(() => { useEffect(() => {
// Refresh when the token changes
refresh(); refresh();
}, [token, refresh]); }, [token, refresh]);
let children: JSX.Element; let children: JSX.Element;
if (pending) { if (error != null) {
switch (error) {
case 'access_denied':
children = (
<>
<h1>Sign In Error</h1>
We couldn't use your Ion account to log in.
<br />
<br />
<a href="/">Home</a>
</>
);
break;
default:
console.error('Unhandled OAuth error case:', error);
children = <h1>Sign In Error</h1>;
}
} else if (pending) {
children = <h1>Signing In</h1>; children = <h1>Signing In</h1>;
} else if (token) { } else if (token) {
children = <Redirect to="/" />; children = <Redirect to="/" />;
} else { } else {
// If we aren't pending anymore, but don't have a token, we must have errored // If we aren't pending anymore, but don't have a token, we must have errored
children = <h1>Sign In Error</h1>; children = (
<>
<h1>Sign In Error</h1>
<br />
<br />
<a href="/">Home</a>
</>
);
} }
return ( return (

View File

@ -1,7 +1,7 @@
export async function createSession(code: string) { export async function createSession(code: string, redirectUrl: string) {
const res = await fetch('http://localhost:5000/create_session', { const res = await fetch('http://localhost:5000/create_session', {
method: 'post', method: 'post',
body: JSON.stringify({ code }), body: JSON.stringify({ code, redirectUrl }),
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },

View File

@ -0,0 +1,25 @@
import { ICarpool } from './types';
import UISecondaryBox from './UISecondaryBox';
function MemberList({ members }: { members: ICarpool['members'] }) {
return (
<div style={{ display: 'flex', flexDirection: 'column' }}>
{members.length > 0
? members.map((member) => {
return <div key={member.id}>{member.name}</div>;
})
: 'This carpool has no members.'}
</div>
);
}
export default function Carpool({ carpool }: { carpool: ICarpool }) {
return (
<UISecondaryBox style={{ width: '100%', alignItems: 'center' }}>
<h2 style={{ textAlign: 'center' }}>{carpool.name}</h2>
{carpool.description}
<h3>Members</h3>
<MemberList members={carpool.members} />
</UISecondaryBox>
);
}

View File

@ -1,7 +1,9 @@
import { CSSProperties } from 'react'; import { CSSProperties } from 'react';
import logout from './Authentication/logout'; import logout from './Authentication/logout';
import Carpool from './Carpool';
import Events from './Events'; import Events from './Events';
import Groups from './Groups'; import Groups from './Groups';
import { useMe } from './hooks';
import UIPressable from './UIPressable'; import UIPressable from './UIPressable';
import UIPrimaryTitle from './UIPrimaryTitle'; import UIPrimaryTitle from './UIPrimaryTitle';
@ -16,10 +18,24 @@ const style: CSSProperties = {
}; };
export default function WheelShare() { export default function WheelShare() {
const user = useMe()!;
return ( return (
<div style={style}> <div style={style}>
<UIPrimaryTitle>WheelShare</UIPrimaryTitle> <UIPrimaryTitle>WheelShare</UIPrimaryTitle>
<Carpool
carpool={{
name: 'Carpool',
id: 0,
description: 'Test carpool',
eventId: null,
members: [],
invitations: [],
}}
/>
{user.name}
<UIPressable onClick={logout}>Log out</UIPressable> <UIPressable onClick={logout}>Log out</UIPressable>
<Groups /> <Groups />

77
src/components/types.ts Normal file
View File

@ -0,0 +1,77 @@
/**
* Model User
*/
export type IUser = {
id: number;
email: string;
name: string | null;
};
/**
* Model Invitation
*/
export type IInvitation = {
userId: number;
carpoolId: number;
isRequest: boolean;
sentTime: Date;
};
/**
* Model Carpool
*/
export type ICarpool = {
id: number;
name: string;
description: string;
eventId: number | null;
event?: IEvent;
members: {
id: number;
name: string;
}[];
invitations: IInvitation[];
};
/**
* Model Group
*/
export type IGroup = {
id: number;
name: string;
};
/**
* Model Event
*/
export type IEvent = {
id: number;
name: string;
groupId: number;
startTime: Date;
duration: number;
endTime: Date | null;
daysOfWeek: number;
placeId: string;
formattedAddress: string;
latitude: number;
longitude: number;
};
/**
* Model EventSignup
*/
export type IEventSignup = {
eventId: number;
userId: number;
placeId: string | null;
formattedAddress: string | null;
latitude: number | null;
longitude: number | null;
};