import React, { useState, useEffect } from 'react';
import { Box, CircularProgress, IconButton, Typography, MenuItem, TextField, Menu, Button, Card, CardHeader, CardContent, CardActions, Dialog, DialogTitle } 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 ClearIcon from '@material-ui/icons/Clear';
import * as serverApi from '../../../api/api';
import { v4 as genUuid } from 'uuid';
import { useIntegrations, IntegrationClasses, IntegrationState, isIntegrationState, makeDefaultIntegrationProperties, makeIntegration } from '../../../lib/integration/useIntegrations';
import Alert from '@material-ui/lab/Alert';
import Integration from '../../../lib/integration/Integration';
let _ = require('lodash')
// This widget is used to either display or edit a few
// specifically needed for Spotify Client credentials integration.
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('');
}
}}
/>
;
}
// An editing widget which is meant to either display or edit properties
// of an integration.
function EditIntegration(props: {
upstreamId?: number,
integration: serverApi.PostIntegrationRequest,
editing?: boolean,
showSubmitButton?: boolean | "InProgress",
showDeleteButton?: boolean | "InProgress",
showEditButton?: boolean,
showTestButton?: boolean | "InProgress",
showCancelButton?: boolean,
flashMessage?: React.ReactFragment,
isNew: boolean,
onChange?: (p: serverApi.PostIntegrationRequest) => void,
onSubmit?: (p: serverApi.PostIntegrationRequest) => void,
onDelete?: () => void,
onEdit?: () => void,
onTest?: () => void,
onCancel?: () => void,
}) {
let IntegrationHeaders: Record = {
[serverApi.IntegrationImpl.SpotifyClientCredentials]:
{new IntegrationClasses[serverApi.IntegrationImpl.SpotifyClientCredentials](-1).getIcon({
style: { height: '40px', width: '40px' }
})}
Spotify (using Client Credentials)
,
[serverApi.IntegrationImpl.YoutubeWebScraper]:
{new IntegrationClasses[serverApi.IntegrationImpl.YoutubeWebScraper](-1).getIcon({
style: { height: '40px', width: '40px' }
})}
Youtube Music (using experimental web scraper)
,
}
let IntegrationDescription: Record = {
[serverApi.IntegrationImpl.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.
,
[serverApi.IntegrationImpl.YoutubeWebScraper]:
This integration allows using the public Youtube Music search page to scrape
for music metadata.
Because it relies on reverse-engineering of a web page that may change in the
future, this is considered to be experimental and unstable. However, the music links acquired
using this method are expected to remain reasonably stable.
,
}
return
{IntegrationDescription[props.integration.type]}
props.onChange && props.onChange({
...props.integration,
name: e.target.value,
})}
/>
{props.integration.type === serverApi.IntegrationImpl.SpotifyClientCredentials &&
props.onChange && props.onChange({
...props.integration,
details: {
...props.integration.details,
clientId: v,
}
})}
onChangeClientSecret={(v: string) => props.onChange && props.onChange({
...props.integration,
secretDetails: {
...props.integration.secretDetails,
clientSecret: v,
}
})}
/>
}
{props.flashMessage && props.flashMessage}
{props.showEditButton && }
{props.showSubmitButton && props.onSubmit && props.onSubmit(props.integration)}
>}
{props.showDeleteButton && }
{props.showCancelButton && }
{props.showTestButton && }
}
let EditorWithTest = (props: any) => {
const [testFlashMessage, setTestFlashMessage] =
React.useState(undefined);
let { integration, ...rest } = props;
return {
integration.integration.test({})
.then(() => {
setTestFlashMessage(
Integration is active.
)
})
}}
flashMessage={testFlashMessage}
showTestButton={true}
integration={integration.properties}
{...rest}
/>;
}
function AddIntegrationMenu(props: {
position: null | number[],
open: boolean,
onClose?: () => void,
onAdd?: (type: serverApi.IntegrationImpl) => void,
}) {
const pos = props.open && props.position ?
{ left: props.position[0], top: props.position[1] }
: { left: 0, top: 0 }
return
}
function EditIntegrationDialog(props: {
open: boolean,
onClose?: () => void,
upstreamId?: number,
integration: IntegrationState,
onSubmit?: (p: serverApi.PostIntegrationRequest) => void,
isNew: boolean,
}) {
let [editingIntegration, setEditingIntegration] =
useState(props.integration);
useEffect(() => { setEditingIntegration(props.integration); }, [props.integration]);
return
}
export default function IntegrationSettings(props: {}) {
const [addMenuPos, setAddMenuPos] = React.useState(null);
const [editingState, setEditingState] = React.useState(null);
let {
state: integrations,
addIntegration,
modifyIntegration,
deleteIntegration,
updateFromUpstream,
} = useIntegrations();
const onOpenAddMenu = (e: any) => {
setAddMenuPos([e.clientX, e.clientY])
};
const onCloseAddMenu = () => {
setAddMenuPos(null);
};
return <>
{integrations === null && }
{Array.isArray(integrations) &&
{integrations.map((state: IntegrationState) =>
{ setEditingState(state); }}
onDelete={() => {
deleteIntegration(state.id)
.then(updateFromUpstream)
}}
/>
)}
}
{
let p = makeDefaultIntegrationProperties(type);
setEditingState({
properties: p,
integration: makeIntegration(p, -1),
id: -1,
})
}}
/>
{editingState && { setEditingState(null); }}
integration={editingState}
isNew={editingState.id === -1}
onSubmit={(v: serverApi.PostIntegrationRequest) => {
if (editingState.id >= 0) {
const id = editingState.id;
setEditingState(null);
modifyIntegration(id, v)
.then(updateFromUpstream)
} else {
setEditingState(null);
createIntegration({
...v,
secretDetails: v.secretDetails || {},
})
.then(updateFromUpstream)
}
}}
/>}
>;
}