You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
178 lines
6.2 KiB
178 lines
6.2 KiB
import React, { useState, useContext, createContext, useReducer, useEffect } from "react"; |
|
import Integration from "./Integration"; |
|
import * as serverApi from '../../api/api'; |
|
import SpotifyClientCreds from "./spotify/SpotifyClientCreds"; |
|
import * as backend from "../backend/integrations"; |
|
import { handleNotLoggedIn, NotLoggedInError } from "../backend/request"; |
|
import { useAuth } from "../useAuth"; |
|
import YoutubeMusicWebScraper from "./youtubemusic/YoutubeMusicWebScraper"; |
|
|
|
export type IntegrationState = { |
|
id: number, |
|
integration: Integration, |
|
properties: serverApi.PostIntegrationRequest, |
|
}; |
|
export type IntegrationsState = IntegrationState[] | "Loading"; |
|
|
|
export function isIntegrationState(v: any): v is IntegrationState { |
|
return 'id' in v && 'integration' in v && 'properties' in v; |
|
} |
|
|
|
export interface Integrations { |
|
state: IntegrationsState, |
|
addIntegration: (v: serverApi.PostIntegrationRequest) => Promise<number>, |
|
deleteIntegration: (id: number) => Promise<void>, |
|
modifyIntegration: (id: number, v: serverApi.PostIntegrationRequest) => Promise<void>, |
|
updateFromUpstream: () => Promise<void>, |
|
}; |
|
|
|
export const IntegrationClasses: Record<any, any> = { |
|
[serverApi.IntegrationImpl.SpotifyClientCredentials]: SpotifyClientCreds, |
|
[serverApi.IntegrationImpl.YoutubeWebScraper]: YoutubeMusicWebScraper, |
|
} |
|
|
|
export function makeDefaultIntegrationProperties(type: serverApi.IntegrationImpl): |
|
serverApi.PostIntegrationRequest { |
|
switch (type) { |
|
case serverApi.IntegrationImpl.SpotifyClientCredentials: { |
|
return { |
|
mbApi_typename: 'integrationData', |
|
name: "Spotify App", |
|
type: type, |
|
details: { clientId: "" }, |
|
secretDetails: { clientSecret: "" }, |
|
} |
|
} |
|
case serverApi.IntegrationImpl.YoutubeWebScraper: { |
|
return { |
|
mbApi_typename: 'integrationData', |
|
name: "Youtube Music Web Scraper", |
|
type: type, |
|
details: {}, |
|
secretDetails: {}, |
|
} |
|
} |
|
default: { |
|
throw new Error("Unimplemented default integration.") |
|
} |
|
} |
|
} |
|
|
|
export function makeIntegration(p: serverApi.PostIntegrationRequest, id: number) { |
|
switch (p.type) { |
|
case serverApi.IntegrationImpl.SpotifyClientCredentials: { |
|
return new SpotifyClientCreds(id); |
|
} |
|
case serverApi.IntegrationImpl.YoutubeWebScraper: { |
|
return new YoutubeMusicWebScraper(id); |
|
} |
|
default: { |
|
throw new Error("Unimplemented integration type.") |
|
} |
|
} |
|
} |
|
|
|
const integrationsContext = createContext<Integrations>({ |
|
state: [], |
|
addIntegration: async () => 0, |
|
modifyIntegration: async () => { }, |
|
deleteIntegration: async () => { }, |
|
updateFromUpstream: async () => { }, |
|
}); |
|
|
|
export function ProvideIntegrations(props: { children: any }) { |
|
const integrations = useProvideIntegrations(); |
|
return <integrationsContext.Provider value={integrations}>{props.children}</integrationsContext.Provider>; |
|
} |
|
|
|
export const useIntegrations = () => { |
|
return useContext(integrationsContext); |
|
}; |
|
|
|
function useProvideIntegrations(): Integrations { |
|
let auth = useAuth(); |
|
enum IntegrationsActions { |
|
SetItem = "SetItem", |
|
Set = "Set", |
|
DeleteItem = "DeleteItem", |
|
AddItem = "AddItem", |
|
} |
|
let IntegrationsReducer = (state: IntegrationsState, action: any): IntegrationsState => { |
|
switch (action.type) { |
|
case IntegrationsActions.SetItem: { |
|
if (state !== "Loading") { |
|
return state.map((item: any) => { |
|
return (item.id === action.id) ? action.value : item; |
|
}) |
|
} |
|
return state; |
|
} |
|
case IntegrationsActions.Set: { |
|
return action.value; |
|
} |
|
case IntegrationsActions.DeleteItem: { |
|
if (state !== "Loading") { |
|
const newState = [...state]; |
|
return newState.filter((item: any) => item.id !== action.id); |
|
} |
|
return state; |
|
} |
|
case IntegrationsActions.AddItem: { |
|
return [...state, action.value]; |
|
} |
|
default: |
|
throw new Error("Unimplemented Integrations state update.") |
|
} |
|
} |
|
|
|
const [state, dispatch] = useReducer(IntegrationsReducer, []) |
|
|
|
let updateFromUpstream = async () => { |
|
try { |
|
return await backend.getIntegrations() |
|
.then((integrations: serverApi.ListIntegrationsResponse) => { |
|
dispatch({ |
|
type: IntegrationsActions.Set, |
|
value: integrations.map((i: any) => { |
|
return { |
|
integration: new (IntegrationClasses[i.type])(i.id), |
|
properties: { ...i }, |
|
id: i.id, |
|
} |
|
}) |
|
}); |
|
}) |
|
.catch((e) => handleNotLoggedIn(auth, e)); |
|
} catch(e) {} |
|
} |
|
|
|
let addIntegration = async (v: serverApi.PostIntegrationRequest) => { |
|
const id = await backend.createIntegration(v).catch((e: any) => { handleNotLoggedIn(auth, e) }); |
|
await updateFromUpstream(); |
|
return (id as serverApi.PostIntegrationResponse).id; |
|
} |
|
|
|
let deleteIntegration = async (id: number) => { |
|
await backend.deleteIntegration(id).catch((e: any) => { handleNotLoggedIn(auth, e) }); |
|
await updateFromUpstream(); |
|
} |
|
|
|
let modifyIntegration = async (id: number, v: serverApi.PostIntegrationRequest) => { |
|
await backend.modifyIntegration(id, v).catch((e: any) => { handleNotLoggedIn(auth, e) }); |
|
await updateFromUpstream(); |
|
} |
|
|
|
useEffect(() => { |
|
if (auth.user) { |
|
updateFromUpstream() |
|
} |
|
}, [auth]); |
|
|
|
return { |
|
state: state, |
|
addIntegration: addIntegration, |
|
modifyIntegration: modifyIntegration, |
|
deleteIntegration: deleteIntegration, |
|
updateFromUpstream: updateFromUpstream, |
|
} |
|
} |