diff --git a/src/api/axiosInstance.ts b/src/api/axiosInstance.ts
index 0c8d70f..d505558 100644
--- a/src/api/axiosInstance.ts
+++ b/src/api/axiosInstance.ts
@@ -4,7 +4,6 @@ import { backendDomain } from "./paths";
export const axiosInstance = axios.create({
baseURL: backendDomain,
withCredentials: true,
- timeout: 20000,
headers: {
"Content-Type": "application/json",
},
diff --git a/src/components/SimpleLoader/SimpleLoader.module.css b/src/components/SimpleLoader/SimpleLoader.module.css
new file mode 100644
index 0000000..cce0d84
--- /dev/null
+++ b/src/components/SimpleLoader/SimpleLoader.module.css
@@ -0,0 +1,50 @@
+.container {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+}
+
+.text {
+ margin-top: var(--mb-2);
+ font-family: var(--primaryFont);
+ font-size: larger;
+ letter-spacing: 1px;
+}
+
+.loader {
+ width: 80px;
+ height: 80px;
+}
+.loader div {
+ box-sizing: border-box;
+ display: block;
+ position: absolute;
+ width: 64px;
+ height: 64px;
+ margin: 8px;
+ /* border: 7px solid var(--primary); */
+ border: 7px solid white;
+ border-radius: 50%;
+ animation: loader 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
+ border-color: white transparent transparent transparent;
+}
+.loader div:nth-child(1) {
+ animation-delay: -0.45s;
+}
+.loader div:nth-child(2) {
+ animation-delay: -0.3s;
+}
+.loader div:nth-child(3) {
+ animation-delay: -0.15s;
+}
+@keyframes loader {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
diff --git a/src/components/SimpleLoader/index.tsx b/src/components/SimpleLoader/index.tsx
new file mode 100644
index 0000000..25f2ee2
--- /dev/null
+++ b/src/components/SimpleLoader/index.tsx
@@ -0,0 +1,17 @@
+import styles from "./SimpleLoader.module.css";
+
+const SimpleLoader = ({ text }: { text?: string }) => {
+ return (
+
+ );
+};
+
+export default SimpleLoader;
diff --git a/src/pages/Graph/Graph.module.css b/src/pages/Graph/Graph.module.css
index 2d03191..76071fe 100644
--- a/src/pages/Graph/Graph.module.css
+++ b/src/pages/Graph/Graph.module.css
@@ -5,6 +5,10 @@
color: black;
}
+.loading {
+ filter: brightness(70%);
+}
+
.operations_wrapper {
display: flex;
background-color: var(--bgNav);
diff --git a/src/pages/Graph/index.tsx b/src/pages/Graph/index.tsx
index f3480dc..762acd6 100644
--- a/src/pages/Graph/index.tsx
+++ b/src/pages/Graph/index.tsx
@@ -4,6 +4,7 @@ import {
Controls,
MiniMap,
Background,
+ Panel,
addEdge,
applyNodeChanges,
applyEdgeChanges,
@@ -53,6 +54,7 @@ import {
import { RefreshAuthContext } from "../../App";
import Button from "../../components/Button";
import APIWrapper from "../../components/APIWrapper";
+import SimpleLoader from "../../components/SimpleLoader";
const initialNodes: Node[] = [];
const initialEdges: Edge[] = [];
@@ -134,6 +136,7 @@ const Graph = () => {
const [selectedEdgeID, setSelectedEdgeID] = useState("");
const [interactive, setInteractive] =
useState(initialInteractive);
+ const [loading, setLoading] = useState(false);
const onFlowInit: OnInit = (_instance) => {
console.debug("flow loaded");
@@ -175,6 +178,7 @@ const Graph = () => {
);
// call API to create link
const spotifyPlaylistLinkPrefix = "https://open.spotify.com/playlist/";
+ setLoading(true);
const resp = await APIWrapper({
apiFn: apiCreateLink,
data: {
@@ -183,6 +187,7 @@ const Graph = () => {
},
refreshAuth,
});
+ setLoading(false);
if (resp?.status === 201) {
showSuccessToastNotification(resp?.data.message);
setLinkEdges((eds) => addEdge(connection, eds));
@@ -203,6 +208,7 @@ const Graph = () => {
`deleted connection: ${edges[0].source} -> ${edges[0].target}`
);
// call API to delete link
+ setLoading(true);
const resp = await APIWrapper({
apiFn: apiDeleteLink,
data: {
@@ -211,6 +217,7 @@ const Graph = () => {
},
refreshAuth,
});
+ setLoading(false);
if (resp?.status === 200) {
showSuccessToastNotification(resp?.data.message);
return { nodes, edges };
@@ -228,6 +235,7 @@ const Graph = () => {
}
const selectedEdge = linkEdges.filter((ed) => ed.id === selectedEdgeID)[0];
+ setLoading(true);
const resp = await APIWrapper({
apiFn: apiBackfillLink,
data: {
@@ -236,6 +244,7 @@ const Graph = () => {
},
refreshAuth,
});
+ setLoading(false);
if (resp?.status === 200) {
showSuccessToastNotification(resp?.data.message);
@@ -252,6 +261,7 @@ const Graph = () => {
}
const selectedEdge = linkEdges.filter((ed) => ed.id === selectedEdgeID)[0];
+ setLoading(true);
const resp = await APIWrapper({
apiFn: apiPruneLink,
data: {
@@ -260,6 +270,7 @@ const Graph = () => {
},
refreshAuth,
});
+ setLoading(false);
if (resp?.status === 200) {
showSuccessToastNotification(resp?.data.message);
@@ -359,7 +370,9 @@ const Graph = () => {
);
const fetchGraph = useCallback(async () => {
+ setLoading(true);
const resp = await APIWrapper({ apiFn: apiFetchGraph, refreshAuth });
+ setLoading(false);
console.debug(
`graph fetched with ${resp?.data.playlists?.length} nodes and ${resp?.data.links?.length} edges`
);
@@ -399,10 +412,12 @@ const Graph = () => {
}, [refreshAuth]);
const updateUserData = async () => {
+ setLoading(true);
const resp = await APIWrapper({
apiFn: apiUpdateUserData,
refreshAuth,
});
+ setLoading(false);
showInfoToastNotification(resp?.data.message);
if (resp?.data.removedLinks)
showWarnToastNotification(
@@ -442,7 +457,7 @@ const Graph = () => {
};
return (
-
+
{
proOptions={proOptions}
colorMode={"light"}
fitView
- minZoom={0.1}
- maxZoom={10}
+ minZoom={0.05}
+ maxZoom={20}
elevateEdgesOnSelect
defaultEdgeOptions={edgeOptions}
edgesReconnectable={false}
@@ -475,6 +490,7 @@ const Graph = () => {
bgColor={"purple"}
/>
+ {loading && }