mirror of
https://github.com/20kaushik02/spotify-manager-web.git
synced 2026-01-25 08:04:05 +00:00
stricter tsconfig
This commit is contained in:
21
src/App.tsx
21
src/App.tsx
@@ -1,5 +1,5 @@
|
||||
// Libraries
|
||||
import { createContext, useEffect, useState } from "react";
|
||||
import { createContext, useEffect, useState, type Context } from "react";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import { ToastContainer } from "react-toastify";
|
||||
|
||||
@@ -9,27 +9,28 @@ import styles from "./App.module.css";
|
||||
// Assets
|
||||
|
||||
// Utils
|
||||
import ScrollToTop from "./utils/ScrollToTop";
|
||||
import ScrollToTop from "./utils/ScrollToTop.tsx";
|
||||
|
||||
// Components
|
||||
import Navbar from "./components/Navbar/index";
|
||||
import Navbar from "./components/Navbar/index.tsx";
|
||||
|
||||
// Routes
|
||||
import AllRoutes from "./routes/AllRoutes";
|
||||
import AllRoutes from "./routes/AllRoutes.tsx";
|
||||
import {
|
||||
showErrorToastNotification,
|
||||
showInfoToastNotification,
|
||||
showWarnToastNotification,
|
||||
} from "./components/ToastNotification";
|
||||
import { apiAuthCheck, apiAuthRefresh } from "./api/auth";
|
||||
} from "./components/ToastNotification/index.tsx";
|
||||
import { apiAuthCheck, apiAuthRefresh } from "./api/auth.ts";
|
||||
import { ReactFlowProvider } from "@xyflow/react";
|
||||
|
||||
// Contexts
|
||||
export const WidthContext = createContext(0);
|
||||
export const AuthContext = createContext(false);
|
||||
export const RefreshAuthContext = createContext(async () => false);
|
||||
export const WidthContext: Context<number> = createContext(0);
|
||||
export const AuthContext: Context<boolean> = createContext(false);
|
||||
export const RefreshAuthContext: Context<() => Promise<boolean>> =
|
||||
createContext(async () => false as boolean); // why is this needed...
|
||||
|
||||
function App() {
|
||||
function App(): React.ReactNode {
|
||||
// States
|
||||
const [width, setWidth] = useState(0);
|
||||
const [auth, setAuth] = useState(false);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { AxiosResponse } from "axios";
|
||||
import { apiRespBaseType, axiosInstance } from "./axiosInstance";
|
||||
import { authHealthCheckURL, authRefreshURL } from "./paths";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import { type apiRespBaseType, axiosInstance } from "./axiosInstance.ts";
|
||||
import { authHealthCheckURL, authRefreshURL } from "./paths.ts";
|
||||
|
||||
export const apiAuthCheck = async (): Promise<
|
||||
AxiosResponse<apiRespBaseType, any>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import axios from "axios";
|
||||
import { backendDomain } from "./paths";
|
||||
import axios, { type AxiosInstance } from "axios";
|
||||
import { backendDomain } from "./paths.ts";
|
||||
|
||||
export const axiosInstance = axios.create({
|
||||
export const axiosInstance: AxiosInstance = axios.create({
|
||||
baseURL: backendDomain,
|
||||
withCredentials: true,
|
||||
headers: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AxiosResponse } from "axios";
|
||||
import { apiRespBaseType, axiosInstance } from "./axiosInstance";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import { type apiRespBaseType, axiosInstance } from "./axiosInstance.ts";
|
||||
import {
|
||||
opBackfillLinkURL,
|
||||
opCreateLinkURL,
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
opFetchGraphURL,
|
||||
opPruneLinkURL,
|
||||
opUpdateUserDataURL,
|
||||
} from "./paths";
|
||||
} from "./paths.ts";
|
||||
|
||||
interface fetchGraphDataType extends apiRespBaseType {
|
||||
playlists?: {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
export const backendDomain = process.env.REACT_APP_API_BASE_URL + "/";
|
||||
export const backendDomain: string =
|
||||
process.env["REACT_APP_API_BASE_URL"] + "/";
|
||||
export const spotifyPlaylistLinkPrefix = "https://open.spotify.com/playlist/";
|
||||
|
||||
export const authLoginURL = "api/auth/login";
|
||||
export const authLoginFullURL = backendDomain + authLoginURL;
|
||||
export const authLoginFullURL: string = backendDomain + authLoginURL;
|
||||
export const authLogoutURL = "api/auth/logout";
|
||||
export const authLogoutFullURL = backendDomain + authLogoutURL;
|
||||
export const authLogoutFullURL: string = backendDomain + authLogoutURL;
|
||||
|
||||
export const authHealthCheckURL = "auth-health";
|
||||
export const authRefreshURL = "api/auth/refresh";
|
||||
@@ -12,6 +13,6 @@ export const authRefreshURL = "api/auth/refresh";
|
||||
export const opFetchGraphURL = "api/operations/fetch";
|
||||
export const opUpdateUserDataURL = "api/operations/update";
|
||||
export const opCreateLinkURL = "api/operations/link";
|
||||
export const opDeleteLinkURL = opCreateLinkURL;
|
||||
export const opDeleteLinkURL: "api/operations/link" = opCreateLinkURL;
|
||||
export const opBackfillLinkURL = "api/operations/populate/link";
|
||||
export const opPruneLinkURL = "api/operations/prune/link";
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { AxiosRequestConfig, AxiosResponse } from "axios";
|
||||
import type { AxiosRequestConfig, AxiosResponse } from "axios";
|
||||
|
||||
import { apiRespBaseType } from "../../api/axiosInstance";
|
||||
import type { apiRespBaseType } from "../../api/axiosInstance.ts";
|
||||
import {
|
||||
showErrorToastNotification,
|
||||
showWarnToastNotification,
|
||||
} from "../ToastNotification";
|
||||
} from "../ToastNotification/index.tsx";
|
||||
|
||||
const maxRetries = 3;
|
||||
|
||||
@@ -29,7 +29,7 @@ const APIWrapper = async <T extends apiRespBaseType>({
|
||||
refreshAuth,
|
||||
data,
|
||||
config,
|
||||
}: APIWrapperProps<T>) => {
|
||||
}: APIWrapperProps<T>): Promise<AxiosResponse<T, any> | null> => {
|
||||
let apiResp;
|
||||
for (let i = 1; i <= maxRetries + 1; i++) {
|
||||
apiResp = await apiFn(data, config);
|
||||
@@ -52,7 +52,7 @@ const APIWrapper = async <T extends apiRespBaseType>({
|
||||
}
|
||||
await sleep(i * i * 1000);
|
||||
}
|
||||
return apiResp;
|
||||
return apiResp ?? null;
|
||||
};
|
||||
|
||||
export default APIWrapper;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import styles from "./AnimatedSVG.module.css";
|
||||
|
||||
const AnimatedSVG = () => {
|
||||
const AnimatedSVG = (): React.ReactNode => {
|
||||
const stroke = "#fff";
|
||||
return (
|
||||
<div className={styles.svg_wrapper}>
|
||||
|
||||
@@ -6,7 +6,10 @@ type ButtonProps = {
|
||||
onClickMethod?: () => void;
|
||||
};
|
||||
|
||||
const Button = ({ children, onClickMethod = () => {} }: ButtonProps) => {
|
||||
const Button = ({
|
||||
children,
|
||||
onClickMethod = () => {},
|
||||
}: ButtonProps): React.ReactNode => {
|
||||
const clickHandler = (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
onClickMethod();
|
||||
|
||||
@@ -2,10 +2,10 @@ import React, { useContext } from "react";
|
||||
|
||||
import styles from "./Navbar.module.css";
|
||||
|
||||
import { AuthContext } from "../../App";
|
||||
import StyledNavLink from "../StyledNavLink/index";
|
||||
import { AuthContext } from "../../App.tsx";
|
||||
import StyledNavLink from "../StyledNavLink/index.tsx";
|
||||
|
||||
const Navbar = () => {
|
||||
const Navbar = (): React.ReactNode => {
|
||||
const auth = useContext(AuthContext);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import styles from "./SimpleLoader.module.css";
|
||||
|
||||
const SimpleLoader = ({ text }: { text?: string }) => {
|
||||
const SimpleLoader = ({ text }: { text?: string }): React.ReactNode => {
|
||||
return (
|
||||
<div className={`${styles.container}`}>
|
||||
<div className={`${styles.loader}`}>
|
||||
|
||||
@@ -8,7 +8,7 @@ type StyledNavLinkProps = {
|
||||
text: string;
|
||||
activeClass?: string;
|
||||
inactiveClass?: string;
|
||||
}
|
||||
};
|
||||
const StyledNavLink = ({
|
||||
path = "/",
|
||||
text = "Go To",
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { toast, type ToastContent } from "react-toastify";
|
||||
|
||||
export const showErrorToastNotification = (message: ToastContent) => {
|
||||
export const showErrorToastNotification = (message: ToastContent): void => {
|
||||
toast.error(message || "Server Error");
|
||||
};
|
||||
|
||||
export const showSuccessToastNotification = (message: ToastContent) => {
|
||||
export const showSuccessToastNotification = (message: ToastContent): void => {
|
||||
toast.success(message || "Success");
|
||||
};
|
||||
|
||||
export const showWarnToastNotification = (message: ToastContent) => {
|
||||
export const showWarnToastNotification = (message: ToastContent): void => {
|
||||
toast.warn(message || "Warning");
|
||||
};
|
||||
|
||||
export const showInfoToastNotification = (message: ToastContent) => {
|
||||
export const showInfoToastNotification = (message: ToastContent): void => {
|
||||
toast.info(message || "Info");
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
import reportWebVitals from "./reportWebVitals";
|
||||
import App from "./App.tsx";
|
||||
import reportWebVitals from "./reportWebVitals.ts";
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById("root") as HTMLElement
|
||||
|
||||
@@ -39,9 +39,9 @@ import {
|
||||
showInfoToastNotification,
|
||||
showSuccessToastNotification,
|
||||
showWarnToastNotification,
|
||||
} from "../../components/ToastNotification";
|
||||
} from "../../components/ToastNotification/index.tsx";
|
||||
|
||||
import { spotifyPlaylistLinkPrefix } from "../../api/paths";
|
||||
import { spotifyPlaylistLinkPrefix } from "../../api/paths.ts";
|
||||
import {
|
||||
apiBackfillLink,
|
||||
apiCreateLink,
|
||||
@@ -49,12 +49,12 @@ import {
|
||||
apiFetchGraph,
|
||||
apiPruneLink,
|
||||
apiUpdateUserData,
|
||||
} from "../../api/operations";
|
||||
} from "../../api/operations.ts";
|
||||
|
||||
import { RefreshAuthContext } from "../../App";
|
||||
import Button from "../../components/Button";
|
||||
import APIWrapper from "../../components/APIWrapper";
|
||||
import SimpleLoader from "../../components/SimpleLoader";
|
||||
import { RefreshAuthContext } from "../../App.tsx";
|
||||
import Button from "../../components/Button/index.tsx";
|
||||
import APIWrapper from "../../components/APIWrapper/index.tsx";
|
||||
import SimpleLoader from "../../components/SimpleLoader/index.tsx";
|
||||
|
||||
const initialNodes: Node[] = [];
|
||||
const initialEdges: Edge[] = [];
|
||||
@@ -128,7 +128,7 @@ const selectedEdgeOptions: DefaultEdgeOptions = {
|
||||
|
||||
const proOptions: ProOptions = { hideAttribution: true };
|
||||
|
||||
const Graph = () => {
|
||||
const Graph = (): React.ReactNode => {
|
||||
const refreshAuth = useContext(RefreshAuthContext);
|
||||
const flowInstance = useReactFlow();
|
||||
const [playlistNodes, setPlaylistNodes] = useState<Node[]>(initialNodes);
|
||||
@@ -153,7 +153,7 @@ const Graph = () => {
|
||||
);
|
||||
|
||||
const onFlowSelectionChange: OnSelectionChangeFunc = useCallback(
|
||||
({ nodes, edges }) => {
|
||||
({ edges }) => {
|
||||
const selectedID = edges[0]?.id ?? "";
|
||||
setSelectedEdgeID(selectedID);
|
||||
setLinkEdges((eds) =>
|
||||
@@ -204,6 +204,7 @@ const Graph = () => {
|
||||
showErrorToastNotification("Can't delete playlists!");
|
||||
return false;
|
||||
}
|
||||
if (!edges[0]) throw new ReferenceError("no edge selected");
|
||||
console.debug(
|
||||
`deleted connection: ${edges[0].source} -> ${edges[0].target}`
|
||||
);
|
||||
@@ -234,7 +235,7 @@ const Graph = () => {
|
||||
return;
|
||||
}
|
||||
const selectedEdge = linkEdges.filter((ed) => ed.id === selectedEdgeID)[0];
|
||||
|
||||
if (!selectedEdge) throw new ReferenceError("no edge selected");
|
||||
setLoading(true);
|
||||
const resp = await APIWrapper({
|
||||
apiFn: apiBackfillLink,
|
||||
@@ -260,6 +261,7 @@ const Graph = () => {
|
||||
return;
|
||||
}
|
||||
const selectedEdge = linkEdges.filter((ed) => ed.id === selectedEdgeID)[0];
|
||||
if (!selectedEdge) throw new ReferenceError("no edge selected");
|
||||
|
||||
setLoading(true);
|
||||
const resp = await APIWrapper({
|
||||
@@ -400,7 +402,7 @@ const Graph = () => {
|
||||
setPlaylistNodes(newNodes);
|
||||
// connect links
|
||||
const newEdges =
|
||||
resp?.data.links?.map((link, idx) => {
|
||||
resp?.data.links?.map((link, _idx) => {
|
||||
return {
|
||||
id: `${link.from}->${link.to}`,
|
||||
source: link.from,
|
||||
@@ -501,6 +503,7 @@ const Graph = () => {
|
||||
<PiSubsetOf size={36} />
|
||||
Prune Link
|
||||
</Button>
|
||||
<hr className={styles.divider} />
|
||||
<Button onClickMethod={() => arrangeLayout("TB")}>
|
||||
<IoIosGitNetwork size={36} />
|
||||
Arrange
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
|
||||
const HowToUse = () => {
|
||||
const HowToUse = (): React.ReactNode => {
|
||||
return (
|
||||
<div>
|
||||
<h1>How To Use?</h1>
|
||||
|
||||
@@ -4,10 +4,10 @@ import styles from "./Landing.module.css";
|
||||
import {
|
||||
showInfoToastNotification,
|
||||
showSuccessToastNotification,
|
||||
} from "../../components/ToastNotification";
|
||||
import AnimatedSVG from "../../components/AnimatedSVG";
|
||||
} from "../../components/ToastNotification/index.tsx";
|
||||
import AnimatedSVG from "../../components/AnimatedSVG/index.tsx";
|
||||
|
||||
const Landing = () => {
|
||||
const Landing = (): React.ReactNode => {
|
||||
const [searchParams] = useSearchParams();
|
||||
useEffect(() => {
|
||||
if (searchParams.get("login") === "success") {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useEffect } from "react";
|
||||
import styles from "./Login.module.css";
|
||||
import { authLoginFullURL } from "../../api/paths";
|
||||
import { authLoginFullURL } from "../../api/paths.ts";
|
||||
|
||||
// auth through backend
|
||||
const Login = () => {
|
||||
const Login = ():React.ReactNode => {
|
||||
useEffect(() => {
|
||||
const timeoutID = setTimeout(() => {
|
||||
window.open(authLoginFullURL, "_self");
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { useEffect } from "react";
|
||||
import styles from "./Logout.module.css";
|
||||
import { authLogoutFullURL } from "../../api/paths";
|
||||
import { authLogoutFullURL } from "../../api/paths.ts";
|
||||
|
||||
const Logout = () => {
|
||||
const Logout = ():React.ReactNode => {
|
||||
useEffect(() => {
|
||||
const timeoutID = setTimeout(() => {
|
||||
window.open(authLogoutFullURL, "_self");
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { useEffect } from "react";
|
||||
import styles from "./PageNotFound.module.css";
|
||||
import { showWarnToastNotification } from "../../components/ToastNotification";
|
||||
import { showWarnToastNotification } from "../../components/ToastNotification/index.tsx";
|
||||
|
||||
const PageNotFound = () => {
|
||||
const PageNotFound = ():React.ReactNode => {
|
||||
useEffect(() => {
|
||||
showWarnToastNotification("Oops!");
|
||||
}, []);
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { Route, Routes } from "react-router-dom";
|
||||
|
||||
import AuthOnlyRoutes from "./AuthOnlyRoutes";
|
||||
import UnAuthOnlyRoutes from "./UnAuthOnlyRoutes";
|
||||
import AuthOnlyRoutes from "./AuthOnlyRoutes.tsx";
|
||||
import UnAuthOnlyRoutes from "./UnAuthOnlyRoutes.tsx";
|
||||
|
||||
import Landing from "../pages/Landing";
|
||||
import PageNotFound from "../pages/PageNotFound";
|
||||
import Graph from "../pages/Graph";
|
||||
import Login from "../pages/Login";
|
||||
import Logout from "../pages/Logout";
|
||||
import HowToUse from "../pages/HowToUse";
|
||||
import Landing from "../pages/Landing/index.tsx";
|
||||
import PageNotFound from "../pages/PageNotFound/index.tsx";
|
||||
import Graph from "../pages/Graph/index.tsx";
|
||||
import Login from "../pages/Login/index.tsx";
|
||||
import Logout from "../pages/Logout/index.tsx";
|
||||
import HowToUse from "../pages/HowToUse/index.tsx";
|
||||
|
||||
const AllRoutes = () => {
|
||||
const AllRoutes = ():React.ReactNode => {
|
||||
return (
|
||||
<Routes>
|
||||
{/* Routes that require user to be logged in */}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React, { useContext } from "react";
|
||||
import { Navigate, Outlet, useLocation } from "react-router-dom";
|
||||
|
||||
import { AuthContext } from "../App";
|
||||
import { showWarnToastNotification } from "../components/ToastNotification";
|
||||
import { AuthContext } from "../App.tsx";
|
||||
import { showWarnToastNotification } from "../components/ToastNotification/index.tsx";
|
||||
|
||||
function AuthOnlyRoutes() {
|
||||
function AuthOnlyRoutes():React.ReactNode {
|
||||
let location = useLocation();
|
||||
const auth = useContext(AuthContext);
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React, { useContext } from "react";
|
||||
import { Navigate, Outlet, useLocation } from "react-router-dom";
|
||||
|
||||
import { AuthContext } from "../App";
|
||||
import { showWarnToastNotification } from "../components/ToastNotification";
|
||||
import { AuthContext } from "../App.tsx";
|
||||
import { showWarnToastNotification } from "../components/ToastNotification/index.tsx";
|
||||
|
||||
function UnAuthOnlyRoutes() {
|
||||
function UnAuthOnlyRoutes():React.ReactNode {
|
||||
let location = useLocation();
|
||||
const auth = useContext(AuthContext);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user