link creation/removal

This commit is contained in:
Kaushik Narayan R 2025-01-06 23:56:22 -07:00
parent 8898fa0b9f
commit d7addccbc7
4 changed files with 81 additions and 9 deletions

View File

@ -1,6 +1,11 @@
import { AxiosResponse } from "axios"; import { AxiosResponse } from "axios";
import { apiRespBaseType, axiosInstance } from "./axiosInstance"; import { apiRespBaseType, axiosInstance } from "./axiosInstance";
import { opFetchGraphURL, opUpdateUserDataURL } from "./paths"; import {
opCreateLinkURL,
opDeleteLinkURL,
opFetchGraphURL,
opUpdateUserDataURL,
} from "./paths";
interface fetchGraphDataType extends apiRespBaseType { interface fetchGraphDataType extends apiRespBaseType {
playlists?: { playlists?: {
@ -17,6 +22,13 @@ interface updateUserDataType extends apiRespBaseType {
removedLinks: boolean; removedLinks: boolean;
} }
type createLinkBodyType = {
from: string; // playlistID
to: string; // playlistID
};
type deleteLinkBodyType = createLinkBodyType;
export const apiFetchGraph = async (): Promise< export const apiFetchGraph = async (): Promise<
AxiosResponse<fetchGraphDataType, any> AxiosResponse<fetchGraphDataType, any>
> => { > => {
@ -38,3 +50,25 @@ export const apiUpdateUserData = async (): Promise<
return error.response; return error.response;
} }
}; };
export const apiCreateLink = async (
data: createLinkBodyType
): Promise<AxiosResponse<apiRespBaseType, any>> => {
try {
const response = await axiosInstance.post(opCreateLinkURL, data);
return response;
} catch (error: any) {
return error.response;
}
};
export const apiDeleteLink = async (
data: deleteLinkBodyType
): Promise<AxiosResponse<apiRespBaseType, any>> => {
try {
const response = await axiosInstance.delete(opDeleteLinkURL, { data });
return response;
} catch (error: any) {
return error.response;
}
};

View File

@ -8,3 +8,5 @@ export const authRefreshURL = "api/auth/refresh";
export const opFetchGraphURL = "api/operations/fetch"; export const opFetchGraphURL = "api/operations/fetch";
export const opUpdateUserDataURL = "api/operations/update"; export const opUpdateUserDataURL = "api/operations/update";
export const opCreateLinkURL = "api/operations/link";
export const opDeleteLinkURL = opCreateLinkURL;

View File

@ -35,7 +35,7 @@ const APIWrapper = async <T extends apiRespBaseType>({
if (apiResp === undefined) { if (apiResp === undefined) {
showErrorToastNotification("Please try again after sometime"); showErrorToastNotification("Please try again after sometime");
} else if (apiResp.status === 200) { } else if (apiResp.status >= 200 && apiResp.status < 300) {
return apiResp; return apiResp;
} else if (apiResp.status === 401) { } else if (apiResp.status === 401) {
showWarnToastNotification("Session expired, refreshing..."); showWarnToastNotification("Session expired, refreshing...");
@ -43,6 +43,9 @@ const APIWrapper = async <T extends apiRespBaseType>({
showErrorToastNotification("Session invalid."); showErrorToastNotification("Session invalid.");
return; return;
} }
} else if (apiResp.status >= 400 && apiResp.status < 500) {
showErrorToastNotification(apiResp.data.message);
return; // no retry on 4XX
} else { } else {
showErrorToastNotification(apiResp.data.message); showErrorToastNotification(apiResp.data.message);
} }

View File

@ -35,10 +35,16 @@ import { AiFillSpotify } from "react-icons/ai";
import { import {
showErrorToastNotification, showErrorToastNotification,
showInfoToastNotification, showInfoToastNotification,
showSuccessToastNotification,
showWarnToastNotification, showWarnToastNotification,
} from "../../components/ToastNotification"; } from "../../components/ToastNotification";
import { apiFetchGraph, apiUpdateUserData } from "../../api/operations"; import {
apiCreateLink,
apiDeleteLink,
apiFetchGraph,
apiUpdateUserData,
} from "../../api/operations";
import { RefreshAuthContext } from "../../App"; import { RefreshAuthContext } from "../../App";
import Button from "../../components/Button"; import Button from "../../components/Button";
@ -125,10 +131,26 @@ const Graph = () => {
[] []
); );
const onFlowAfterDelete: OnDelete = useCallback(({ nodes, edges }) => { const onFlowAfterDelete: OnDelete = useCallback(
console.debug("deleted edges"); async ({ nodes, edges }) => {
console.debug(edges); console.debug(
}, []); `deleted connection: ${edges[0].source} -> ${edges[0].target}`
);
// call API to delete link
const spotifyPlaylistLinkPrefix = "https://open.spotify.com/playlist/";
const resp = await APIWrapper({
apiFn: apiDeleteLink,
data: {
from: spotifyPlaylistLinkPrefix + edges[0].source,
to: spotifyPlaylistLinkPrefix + edges[0].target,
},
refreshAuth,
});
if (resp?.status === 200)
showSuccessToastNotification(resp?.data.message);
},
[refreshAuth]
);
// base event handling // base event handling
const onNodesChange: OnNodesChange = useCallback( const onNodesChange: OnNodesChange = useCallback(
@ -141,14 +163,25 @@ const Graph = () => {
); );
const onConnect: OnConnect = useCallback( const onConnect: OnConnect = useCallback(
(connection) => { async (connection) => {
setLinkEdges((eds) => addEdge(connection, eds)); setLinkEdges((eds) => addEdge(connection, eds));
console.debug( console.debug(
`new connection: ${connection.source} -> ${connection.target}` `new connection: ${connection.source} -> ${connection.target}`
); );
// call API to create link // call API to create link
const spotifyPlaylistLinkPrefix = "https://open.spotify.com/playlist/";
const resp = await APIWrapper({
apiFn: apiCreateLink,
data: {
from: spotifyPlaylistLinkPrefix + connection.source,
to: spotifyPlaylistLinkPrefix + connection.target,
},
refreshAuth,
});
if (resp?.status === 201)
showSuccessToastNotification(resp?.data.message);
}, },
[setLinkEdges] [setLinkEdges, refreshAuth]
); );
type getLayoutedElementsOpts = { type getLayoutedElementsOpts = {