import React, { useState, useEffect, useCallback } from 'react'; import { useAuth } from '../../../lib/useAuth'; 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 AddIcon from '@material-ui/icons/Add'; import EditIcon from '@material-ui/icons/Edit'; import CheckIcon from '@material-ui/icons/Check'; import DeleteIcon from '@material-ui/icons/Delete'; import * as serverApi from '../../../api'; import StoreLinkIcon, { ExternalStore } from '../../common/StoreLinkIcon'; import { v4 as genUuid } from 'uuid'; import { testSpotify } from '../../../lib/integration/spotify/spotifyClientCreds'; let _ = require('lodash') interface EditorIntegrationState extends serverApi.IntegrationDetailsResponse { secretDetails?: any, } interface EditIntegrationProps { upstreamId: number | null, integration: EditorIntegrationState, original: EditorIntegrationState, editing: boolean, submitting: boolean, onChange: (p: EditorIntegrationState, editing: boolean) => void, onSubmit: () => void, onDelete: () => void, } function EditSpotifyClientCredentialsDetails(props: { clientId: string, clientSecret: string | null, editing: boolean, onChangeClientId: (v: string) => void, onChangeClientSecret: (v: string) => void, }) { return props.onChangeClientId(e.target.value)} /> { props.onChangeClientSecret(e.target.value) }} onFocus={(e: any) => { if(props.clientSecret === null) { // Change from dots to empty input console.log("Focus!") props.onChangeClientSecret(''); } }} /> ; } function EditIntegration(props: EditIntegrationProps) { let IntegrationHeaders: Record = { [serverApi.IntegrationType.SpotifyClientCredentials]: Spotify (using Client Credentials) } let IntegrationDescription: Record = { [serverApi.IntegrationType.SpotifyClientCredentials]: This integration allows using the Spotify API to make requests that are tied to any specific user, such as searching items and retrieving item metadata.
Please see the Spotify API documentation on how to generate a client ID and client secret. Once set, you will only be able to overwrite the secret here, not read it.
} return {IntegrationDescription[props.integration.type]} props.onChange({ ...props.integration, name: e.target.value, }, props.editing)} /> {props.integration.type === serverApi.IntegrationType.SpotifyClientCredentials && props.onChange({ ...props.integration, details: { ...props.integration.details, clientId: v, } }, props.editing)} onChangeClientSecret={(v: string) => props.onChange({ ...props.integration, secretDetails: { ...props.integration.secretDetails, clientSecret: v, } }, props.editing)} /> } {!props.editing && !props.submitting && { props.onChange(props.integration, true); }} >} {props.editing && !props.submitting && { props.onSubmit(); }} >} {!props.submitting && { props.onDelete(); }} >} {!props.submitting && !props.editing && props.upstreamId !== null && } {props.submitting && } } function AddIntegrationMenu(props: { position: null | number[], open: boolean, onClose: () => void, onAdd: (type: serverApi.IntegrationType) => void, }) { const pos = props.open && props.position ? { left: props.position[0], top: props.position[1] } : { left: 0, top: 0 } return { props.onAdd(serverApi.IntegrationType.SpotifyClientCredentials); props.onClose(); }} >Spotify } export default function IntegrationSettingsEditor(props: {}) { interface EditorState { id: string, //uniquely identifies this editor in the window. upstreamId: number | null, //back-end ID for this integration if any. integration: EditorIntegrationState, original: EditorIntegrationState, editing: boolean, submitting: boolean, } let [editors, setEditors] = useState(null); const [addMenuPos, setAddMenuPos] = React.useState(null); const onOpenAddMenu = (e: any) => { setAddMenuPos([e.clientX, e.clientY]) }; const onCloseAddMenu = () => { setAddMenuPos(null); }; const submitEditor = (state: EditorState) => { let integration: any = state.integration; if (state.upstreamId === null) { if (!state.integration.secretDetails) { throw new Error('Cannot create an integration without its secret details set.') } createIntegration(integration).then((response: any) => { if (!response.id) { throw new Error('failed to submit integration.') } let cpy = _.cloneDeep(editors); cpy.forEach((s: any) => { if (s.id === state.id) { s.submitting = false; s.editing = false; s.upstreamId = response.id; } }) setEditors(cpy); }) } else { modifyIntegration(state.upstreamId, integration).then(() => { let cpy = _.cloneDeep(editors); cpy.forEach((s: any) => { if (s.id === state.id) { s.submitting = false; s.editing = false; } }) setEditors(cpy); }) } } const deleteEditor = (state: EditorState) => { let promise: Promise = state.upstreamId ? deleteIntegration(state.upstreamId) : (async () => { })(); promise.then((response: any) => { let cpy = _.cloneDeep(editors).filter( (e: any) => e.id !== state.id ); setEditors(cpy); }) } useEffect(() => { getIntegrations() .then((integrations: serverApi.ListIntegrationsResponse) => { setEditors(integrations.map((i: any, idx: any) => { return { integration: { ...i }, original: { ...i }, id: genUuid(), editing: false, submitting: false, upstreamId: i.id, } })); }); }, []); return <> {editors === null && } {editors && {editors.map((state: EditorState) => { if (!editors) { throw new Error('cannot change editors before loading integrations.') } let cpy: EditorState[] = _.cloneDeep(editors); cpy.forEach((s: any) => { if (s.id === state.id) { s.integration = p; s.editing = editing; } }) setEditors(cpy); }} onSubmit={() => { if (!editors) { throw new Error('cannot submit editors before loading integrations.') } let cpy: EditorState[] = _.cloneDeep(editors); cpy.forEach((s: EditorState) => { if (s.id === state.id) { s.submitting = true; s.integration.secretDetails = undefined; } }) setEditors(cpy); submitEditor(state); }} onDelete={() => { if (!editors) { throw new Error('cannot submit editors before loading integrations.') } let cpy: EditorState[] = _.cloneDeep(editors); cpy.forEach((s: any) => { if (s.id === state.id) { s.submitting = true; } }) setEditors(cpy); deleteEditor(state); }} /> )} } { let cpy = _.cloneDeep(editors); cpy.push({ integration: { type: serverApi.IntegrationType.SpotifyClientCredentials, details: { clientId: '', }, secretDetails: { clientSecret: '', }, name: '', }, original: null, id: genUuid(), editing: true, submitting: false, upstreamId: null, }) setEditors(cpy); }} /> ; }