Persist authentication, make integration editor look good

pull/34/head
Sander Vocke 5 years ago
parent b4e4ac0162
commit 8111633a02
  1. 72
      client/src/components/windows/settings/IntegrationSettingsEditor.tsx
  2. 33
      client/src/lib/useAuth.tsx

@ -1,6 +1,6 @@
import React, { useState, useEffect, useCallback } from 'react'; import React, { useState, useEffect, useCallback } from 'react';
import { useAuth } from '../../../lib/useAuth'; import { useAuth } from '../../../lib/useAuth';
import { Box, CircularProgress, IconButton, Typography, FormControl, Select, MenuItem, TextField, Menu, Button } from '@material-ui/core'; import { Box, CircularProgress, IconButton, Typography, FormControl, Select, MenuItem, TextField, Menu, Button, Card, CardHeader, CardContent, CardActions } from '@material-ui/core';
import { getIntegrations, createIntegration, modifyIntegration, deleteIntegration } from '../../../lib/backend/integrations'; import { getIntegrations, createIntegration, modifyIntegration, deleteIntegration } from '../../../lib/backend/integrations';
import AddIcon from '@material-ui/icons/Add'; import AddIcon from '@material-ui/icons/Add';
import EditIcon from '@material-ui/icons/Edit'; import EditIcon from '@material-ui/icons/Edit';
@ -31,57 +31,61 @@ function EditSpotifyClientCredentialsDetails(props: {
onChangeClientSecret: (v: string) => void, onChangeClientSecret: (v: string) => void,
}) { }) {
return <Box> return <Box>
<Box display="flex" alignItems="center"> <Box mt={1} mb={1}>
<Typography>Client id:</Typography>
{props.editing ?
<TextField <TextField
variant="outlined" variant="outlined"
disabled={!props.editing}
value={props.clientId || ""} value={props.clientId || ""}
label="Client Id:" label="Client Id:"
fullWidth
onChange={(e: any) => props.onChangeClientId(e.target.value)} onChange={(e: any) => props.onChangeClientId(e.target.value)}
/> : />
<Typography>{props.clientId}</Typography>}
</Box> </Box>
<Box display="flex" alignItems="center"> <Box mt={1} mb={1}>
<Typography>Client secret:</Typography>
{props.editing ?
<TextField <TextField
variant="outlined" variant="outlined"
disabled={!props.editing}
value={props.clientSecret || ""} value={props.clientSecret || ""}
label="Client secret:" label="Client secret:"
fullWidth
onChange={(e: any) => props.onChangeClientSecret(e.target.value)} onChange={(e: any) => props.onChangeClientSecret(e.target.value)}
/> : />
<Typography>{props.clientSecret}</Typography>}
</Box> </Box>
</Box>; </Box>;
} }
function EditIntegration(props: EditIntegrationProps) { function EditIntegration(props: EditIntegrationProps) {
let IntegrationHeaders: Record<any, any> = { let IntegrationHeaders: Record<any, any> = {
[serverApi.IntegrationType.SpotifyClientCredentials]: <Box display="flex" alignItems="center"> [serverApi.IntegrationType.SpotifyClientCredentials]:
<StoreLinkIcon <Box display="flex" alignItems="center">
<Box mr={1}><StoreLinkIcon
style={{ height: '40px', width: '40px' }} style={{ height: '40px', width: '40px' }}
whichStore={ExternalStore.Spotify} whichStore={ExternalStore.Spotify}
/> /></Box>
<Typography>Spotify</Typography> <Typography>Spotify</Typography>
</Box> </Box>
} }
return <Box display="flex" flexDirection="column" border={1}> return <Card variant="outlined">
{IntegrationHeaders[props.integration.type]} <CardHeader
<Box display="flex" alignItems="center"> avatar={
<Typography>Name:</Typography> IntegrationHeaders[props.integration.type]
{props.editing ? }
>
</CardHeader>
<CardContent>
<Box mt={1} mb={1}>
<TextField <TextField
variant="outlined" variant="outlined"
value={props.integration.name || ""} value={props.integration.name || ""}
label="Name" label="Name"
fullWidth
disabled={!props.editing}
onChange={(e: any) => props.onChange({ onChange={(e: any) => props.onChange({
...props.integration, ...props.integration,
name: e.target.value, name: e.target.value,
}, props.editing)} }, props.editing)}
/> : />
<Typography>{props.integration.name}</Typography>}
</Box> </Box>
{props.integration.type === serverApi.IntegrationType.SpotifyClientCredentials && {props.integration.type === serverApi.IntegrationType.SpotifyClientCredentials &&
<EditSpotifyClientCredentialsDetails <EditSpotifyClientCredentialsDetails
@ -104,6 +108,8 @@ function EditIntegration(props: EditIntegrationProps) {
}, props.editing)} }, props.editing)}
/> />
} }
</CardContent>
<CardActions>
{!props.editing && !props.submitting && <IconButton {!props.editing && !props.submitting && <IconButton
onClick={() => { props.onChange(props.integration, true); }} onClick={() => { props.onChange(props.integration, true); }}
><EditIcon /></IconButton>} ><EditIcon /></IconButton>}
@ -117,7 +123,8 @@ function EditIntegration(props: EditIntegrationProps) {
onClick={() => testSpotify(props.upstreamId || 0)} onClick={() => testSpotify(props.upstreamId || 0)}
>Test</Button>} >Test</Button>}
{props.submitting && <CircularProgress />} {props.submitting && <CircularProgress />}
</Box > </CardActions>
</Card>
} }
function AddIntegrationMenu(props: { function AddIntegrationMenu(props: {
@ -198,10 +205,11 @@ export default function IntegrationSettingsEditor(props: {}) {
} }
const deleteEditor = (state: EditorState) => { const deleteEditor = (state: EditorState) => {
if (!state.upstreamId) { let promise: Promise<void> = state.upstreamId ?
throw new Error('Cannot delete integration: has no upstream') deleteIntegration(state.upstreamId) :
} (async () => {})();
deleteIntegration(state.upstreamId).then((response: any) => {
promise.then((response: any) => {
let cpy = _.cloneDeep(editors).filter( let cpy = _.cloneDeep(editors).filter(
(e: any) => e.id !== state.id (e: any) => e.id !== state.id
); );
@ -225,13 +233,12 @@ export default function IntegrationSettingsEditor(props: {}) {
}); });
}, []); }, []);
// FIXME: add button should show a drop-down to choose a fixed integration type.
// Otherwise we need dynamic switching of the type's fields.
return <> return <>
<Box> <Box>
{editors === null && <CircularProgress />} {editors === null && <CircularProgress />}
{editors && <> {editors && <Box display="flex" flexDirection="column" alignItems="center" flexWrap="wrap">
{editors.map((state: EditorState) => <EditIntegration {editors.map((state: EditorState) => <Box m={1} width="90%">
<EditIntegration
upstreamId={state.upstreamId} upstreamId={state.upstreamId}
integration={state.integration} integration={state.integration}
original={state.original} original={state.original}
@ -276,11 +283,12 @@ export default function IntegrationSettingsEditor(props: {}) {
setEditors(cpy); setEditors(cpy);
deleteEditor(state); deleteEditor(state);
}} }}
/>)} />
</Box>)}
<IconButton onClick={onOpenAddMenu}> <IconButton onClick={onOpenAddMenu}>
<AddIcon /> <AddIcon />
</IconButton> </IconButton>
</>} </Box>}
</Box> </Box>
<AddIntegrationMenu <AddIntegrationMenu
position={addMenuPos} position={addMenuPos}

@ -38,8 +38,37 @@ export const useAuth = () => {
return useContext(authContext); return useContext(authContext);
}; };
function persistAuth(auth: AuthUser | null) {
let s = window.sessionStorage;
if(auth === null) {
s.removeItem('userId');
s.removeItem('userEmail');
return;
}
s.setItem('userId', auth.id.toString());
s.setItem('userEmail', auth.email);
// TODO icon
}
function loadAuth(): AuthUser | null {
let s = window.sessionStorage;
let id = s.getItem('userId');
let email = s.getItem('userEmail');
if (id && email) {
return {
id: parseInt(id),
email: email,
icon: <PersonIcon />
}
}
return null;
}
function useProvideAuth() { function useProvideAuth() {
const [user, setUser] = useState<AuthUser | null>(null); const [user, setUser] = useState<AuthUser | null>(loadAuth());
// TODO: password maybe shouldn't be encoded into the URL. // TODO: password maybe shouldn't be encoded into the URL.
const signin = (email: string, password: string) => { const signin = (email: string, password: string) => {
@ -59,6 +88,7 @@ function useProvideAuth() {
icon: <PersonIcon />, icon: <PersonIcon />,
} }
setUser(user); setUser(user);
persistAuth(user);
return user; return user;
})(); })();
}; };
@ -89,6 +119,7 @@ function useProvideAuth() {
throw new Error("Failed to log out."); throw new Error("Failed to log out.");
} }
setUser(null); setUser(null);
persistAuth(null);
})(); })();
}; };

Loading…
Cancel
Save