Working towards spotify integration.

pull/34/head
Sander Vocke 5 years ago
parent 5a073fb3b8
commit 3d094c024b
  1. 6
      client/src/api.ts
  2. 83
      client/src/components/windows/settings/IntegrationSettingsEditor.tsx
  3. 11
      client/src/lib/integration/spotify/spotify.tsx
  4. 15
      server/integrations/spotifyClientCreds.ts

@ -359,15 +359,15 @@ export const LoginEndpoint = "/login";
export const LogoutEndpoint = "/logout"; export const LogoutEndpoint = "/logout";
export enum IntegrationType { export enum IntegrationType {
spotify = "spotify", SpotifyClientCredentials = "SpotifyClientCredentials",
} }
export interface SpotifyIntegrationDetails { export interface SpotifyClientCredentialsDetails {
clientId: string, clientId: string,
clientSecret: string, clientSecret: string,
} }
export type IntegrationDetails = SpotifyIntegrationDetails; export type IntegrationDetails = SpotifyClientCredentialsDetails;
// Create a new integration (POST). // Create a new integration (POST).
export const CreateIntegrationEndpoint = '/integration'; export const CreateIntegrationEndpoint = '/integration';

@ -1,7 +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 } from '@material-ui/core'; import { Box, CircularProgress, IconButton, Typography, FormControl, Select, MenuItem, TextField, Menu, Button } from '@material-ui/core';
import { IntegrationDetails } from '../../../api';
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';
@ -10,6 +9,7 @@ import DeleteIcon from '@material-ui/icons/Delete';
import * as serverApi from '../../../api'; import * as serverApi from '../../../api';
import StoreLinkIcon, { ExternalStore } from '../../common/StoreLinkIcon'; import StoreLinkIcon, { ExternalStore } from '../../common/StoreLinkIcon';
import { v4 as genUuid } from 'uuid'; import { v4 as genUuid } from 'uuid';
import { getAuthToken } from '../../../lib/integration/spotify/spotify';
let _ = require('lodash') let _ = require('lodash')
interface EditIntegrationProps { interface EditIntegrationProps {
@ -22,9 +22,42 @@ interface EditIntegrationProps {
onDelete: () => void, onDelete: () => void,
} }
function EditSpotifyClientCredentialsDetails(props: {
clientId: string,
clientSecret: string,
editing: boolean,
onChangeClientId: (v: string) => void,
onChangeClientSecret: (v: string) => void,
}) {
return <Box>
<Box display="flex" alignItems="center">
<Typography>Client id:</Typography>
{props.editing ?
<TextField
variant="outlined"
value={props.clientId || ""}
label="Client Id:"
onChange={(e: any) => props.onChangeClientId(e.target.value)}
/> :
<Typography>{props.clientId}</Typography>}
</Box>
<Box display="flex" alignItems="center">
<Typography>Client secret:</Typography>
{props.editing ?
<TextField
variant="outlined"
value={props.clientSecret || ""}
label="Client secret:"
onChange={(e: any) => props.onChangeClientSecret(e.target.value)}
/> :
<Typography>{props.clientSecret}</Typography>}
</Box>
</Box>;
}
function EditIntegration(props: EditIntegrationProps) { function EditIntegration(props: EditIntegrationProps) {
let IntegrationHeaders: Record<any, any> = { let IntegrationHeaders: Record<any, any> = {
[serverApi.IntegrationType.spotify]: <Box display="flex" alignItems="center"> [serverApi.IntegrationType.SpotifyClientCredentials]: <Box display="flex" alignItems="center">
<StoreLinkIcon <StoreLinkIcon
style={{ height: '40px', width: '40px' }} style={{ height: '40px', width: '40px' }}
whichStore={ExternalStore.Spotify} whichStore={ExternalStore.Spotify}
@ -49,41 +82,27 @@ function EditIntegration(props: EditIntegrationProps) {
/> : /> :
<Typography>{props.integration.name}</Typography>} <Typography>{props.integration.name}</Typography>}
</Box> </Box>
{props.integration.type === serverApi.IntegrationType.spotify && <> {props.integration.type === serverApi.IntegrationType.SpotifyClientCredentials &&
<Box display="flex" alignItems="center"> <EditSpotifyClientCredentialsDetails
<Typography>Client id:</Typography> clientId={props.integration.details.clientId}
{props.editing ? clientSecret={props.integration.details.clientSecret}
<TextField editing={props.editing}
variant="outlined" onChangeClientId={(v: string) => props.onChange({
value={props.integration.details.clientId || ""}
label="Client Id:"
onChange={(e: any) => props.onChange({
...props.integration, ...props.integration,
details: { details: {
...props.integration.details, ...props.integration.details,
clientId: e.target.value, clientId: v,
} }
}, props.editing)} }, props.editing)}
/> : onChangeClientSecret={(v: string) => props.onChange({
<Typography>{props.integration.details.clientId}</Typography>}
</Box>
<Box display="flex" alignItems="center">
<Typography>Client secret:</Typography>
{props.editing ?
<TextField
variant="outlined"
value={props.integration.details.clientSecret || ""}
label="Client secret:"
onChange={(e: any) => props.onChange({
...props.integration, ...props.integration,
details: { details: {
...props.integration.details, ...props.integration.details,
clientSecret: e.target.value, clientSecret: v,
} }
}, props.editing)} }, props.editing)}
/> : />
<Typography>{props.integration.details.clientSecret}</Typography>} }
</Box>
{!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>}
@ -93,8 +112,10 @@ function EditIntegration(props: EditIntegrationProps) {
{!props.submitting && <IconButton {!props.submitting && <IconButton
onClick={() => { props.onDelete(); }} onClick={() => { props.onDelete(); }}
><DeleteIcon /></IconButton>} ><DeleteIcon /></IconButton>}
{!props.submitting && <Button
onClick={() => { }}
>Test</Button>}
{props.submitting && <CircularProgress />} {props.submitting && <CircularProgress />}
</>}
</Box > </Box >
} }
@ -117,7 +138,7 @@ function AddIntegrationMenu(props: {
> >
<MenuItem <MenuItem
onClick={() => { onClick={() => {
props.onAdd(serverApi.IntegrationType.spotify); props.onAdd(serverApi.IntegrationType.SpotifyClientCredentials);
props.onClose(); props.onClose();
}} }}
>Spotify</MenuItem> >Spotify</MenuItem>
@ -267,7 +288,7 @@ export default function IntegrationSettingsEditor(props: {}) {
let cpy = _.cloneDeep(editors); let cpy = _.cloneDeep(editors);
cpy.push({ cpy.push({
integration: { integration: {
type: serverApi.IntegrationType.spotify, type: serverApi.IntegrationType.SpotifyClientCredentials,
details: { details: {
clientId: '', clientId: '',
clientSecret: '', clientSecret: '',

@ -0,0 +1,11 @@
export async function getAuthToken(clientId: string, clientSecret: string) {
let requestOpts = {
method: "POST",
headers: { "Authorization": "Basic " + clientId + ":" + clientSecret },
}
const response = await fetch("https://accounts.spotify.com/api/token?grant_type=client_credentials", requestOpts)
return await response.json();
}
export default {}

@ -0,0 +1,15 @@
// The authorization token to use with the Spotify API.
// Will need to be refreshed once in a while.
let authToken: string | null = null;
export async function getAuthToken(clientId: string, clientSecret: string) {
let requestOpts = {
method: "POST",
headers: { "Authorization": "Basic " + clientId + ":" + clientSecret },
}
const response = await fetch("https://accounts.spotify.com/api/token?grant_type=client_credentials", requestOpts)
return await response.json();
}
export async function
Loading…
Cancel
Save