diff --git a/src/App.tsx b/src/App.tsx index 0bba9e4..a9c8b98 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,6 +7,7 @@ import {CssBaseline, ThemeProvider} from "@mui/material" import {queryClient} from "~/constants/react-query" import {lightTheme} from "~/constants/themes" import {getServerSettings} from "~/apis" +import AliasesRoute from "~/routes/AliasesRoute" import AuthContextProvider from "~/AuthContext/AuthContextProvider" import AuthenticateRoute from "~/routes/AuthenticateRoute" import AuthenticatedRoute from "~/routes/AuthenticatedRoute" @@ -39,6 +40,12 @@ const router = createBrowserRouter([ { path: "/", element: , + children: [ + { + path: "/aliases", + element: , + }, + ], }, ], }, diff --git a/src/apis/create-alias.ts b/src/apis/create-alias.ts new file mode 100644 index 0000000..ce18e57 --- /dev/null +++ b/src/apis/create-alias.ts @@ -0,0 +1,49 @@ +import axios from "axios" + +import { + Alias, + AliasType, + ImageProxyFormatType, + ProxyUserAgentType, +} from "~/server-types" +import parseAlias from "~/apis/helpers/parse-alias" + +interface CreateAliasDataOther { + isActive?: boolean + encryptedNotes?: string + removeTrackers?: boolean + createMailReport?: boolean + proxyImages?: boolean + imageProxyFormat?: ImageProxyFormatType + imageProxyUserAgent?: ProxyUserAgentType +} + +interface CreateAliasDataBase extends CreateAliasDataOther { + type: AliasType + local?: string +} + +interface CreateAliasDataRandomType extends CreateAliasDataBase { + type: AliasType.Random + local?: undefined +} + +interface CreateAliasDataCustomType extends CreateAliasDataBase { + type: AliasType.Custom + local: string +} + +export type CreateAliasData = + | CreateAliasDataRandomType + | CreateAliasDataCustomType + +export default async function createAlias( + aliasData: CreateAliasData, +): Promise { + const {data} = await axios.post( + `${import.meta.env.VITE_SERVER_BASE_URL}/alias`, + aliasData, + ) + + return parseAlias(data) +} diff --git a/src/apis/get-aliases.ts b/src/apis/get-aliases.ts new file mode 100644 index 0000000..2e671b0 --- /dev/null +++ b/src/apis/get-aliases.ts @@ -0,0 +1,12 @@ +import axios from "axios" + +import {Alias} from "~/server-types" +import parseAlias from "~/apis/helpers/parse-alias" + +export default async function getAliases(): Promise> { + const {data} = await axios.get( + `${import.meta.env.VITE_SERVER_BASE_URL}/alias`, + ) + + return data.map(parseAlias) +} diff --git a/src/apis/helpers/parse-alias.ts b/src/apis/helpers/parse-alias.ts new file mode 100644 index 0000000..13a06e4 --- /dev/null +++ b/src/apis/helpers/parse-alias.ts @@ -0,0 +1,16 @@ +import {Alias} from "~/server-types" + +export default function parseAlias(alias: any): Alias { + return { + id: alias.id, + domain: alias.domain, + local: alias.local, + isActive: alias.is_active, + encryptedNotes: alias.encrypted_notes, + removeTrackers: alias.remove_trackers, + createMailReport: alias.create_mail_report, + proxyImages: alias.proxy_images, + imageProxyFormat: alias.image_proxy_format, + imageProxyUserAgent: alias.image_proxy_user_agent, + } +} diff --git a/src/apis/index.ts b/src/apis/index.ts index f588252..dca566b 100644 --- a/src/apis/index.ts +++ b/src/apis/index.ts @@ -12,3 +12,7 @@ export * from "./refresh-token" export {default as refreshToken} from "./refresh-token" export * from "./logout" export {default as logout} from "./logout" +export * from "./get-aliases" +export {default as getAliases} from "./get-aliases" +export * from "./create-alias" +export {default as createAlias} from "./create-alias" diff --git a/src/components/LoadingData.tsx b/src/components/LoadingData.tsx new file mode 100644 index 0000000..d572734 --- /dev/null +++ b/src/components/LoadingData.tsx @@ -0,0 +1,38 @@ +import {ReactElement, useEffect, useState} from "react" + +import {CircularProgress, Grid, Typography} from "@mui/material" + +export interface LoadingDataProps { + message?: string +} + +export default function LoadingData({ + message = "Loading", +}: LoadingDataProps): ReactElement { + const [ellipsis, setEllipsis] = useState("") + + useEffect(() => { + const interval = setInterval(() => { + setEllipsis((value: string) => { + if (value.length === 3) { + return "" + } + + return value + "." + }) + }, 300) + + return () => clearInterval(interval) + }, []) + + return ( + + + + + + Loading{ellipsis} + + + ) +} diff --git a/src/hooks/use-user.ts b/src/hooks/use-user.ts index fe3dfb6..f0449e2 100644 --- a/src/hooks/use-user.ts +++ b/src/hooks/use-user.ts @@ -12,7 +12,7 @@ export default function useUser(): User { useLayoutEffect(() => { if (!isAuthenticated) { - navigate("/login") + navigate("/auth/login") } }, [isAuthenticated, navigate]) diff --git a/src/route-widgets/AliasRoute/AliasListItem.tsx b/src/route-widgets/AliasRoute/AliasListItem.tsx new file mode 100644 index 0000000..5ef9350 --- /dev/null +++ b/src/route-widgets/AliasRoute/AliasListItem.tsx @@ -0,0 +1,30 @@ +import {ReactElement} from "react" +import {MdOutlineMoreVert} from "react-icons/md" + +import { + IconButton, + ListItem, + ListItemSecondaryAction, + ListItemText, +} from "@mui/material" + +import {Alias} from "~/server-types" + +export interface AliasListItemProps { + alias: Alias +} + +export default function AliasListItem({ + alias, +}: AliasListItemProps): ReactElement { + return ( + + + + + + + + + ) +} diff --git a/src/route-widgets/AliasRoute/CreateRandomAliasButton.tsx b/src/route-widgets/AliasRoute/CreateRandomAliasButton.tsx new file mode 100644 index 0000000..6a77747 --- /dev/null +++ b/src/route-widgets/AliasRoute/CreateRandomAliasButton.tsx @@ -0,0 +1,37 @@ +import {ReactElement} from "react" +import {BsArrowClockwise} from "react-icons/bs" +import {AxiosError} from "axios" + +import {Button} from "@mui/material" +import {useMutation} from "@tanstack/react-query" + +import {CreateAliasData, createAlias} from "~/apis" +import {Alias} from "~/server-types" + +export interface CreateRandomAliasButtonProps { + onCreated: (alias: Alias) => void +} + +export default function CreateRandomAliasButton({ + onCreated, +}: CreateRandomAliasButtonProps): ReactElement { + const {mutate} = useMutation( + createAlias, + { + onSuccess: onCreated, + }, + ) + + return ( + + ) +} diff --git a/src/routes/AliasesRoute.tsx b/src/routes/AliasesRoute.tsx new file mode 100644 index 0000000..44ce1de --- /dev/null +++ b/src/routes/AliasesRoute.tsx @@ -0,0 +1,32 @@ +import {ReactElement} from "react" +import {AxiosError} from "axios" + +import {List} from "@mui/material" +import {useQuery} from "@tanstack/react-query" + +import {Alias} from "~/server-types" +import AliasListItem from "~/route-widgets/AliasRoute/AliasListItem" +import CreateRandomAliasButton from "~/route-widgets/AliasRoute/CreateRandomAliasButton" +import LoadingData from "~/components/LoadingData" +import getAliases from "~/apis/get-aliases" + +export default function AliasesRoute(): ReactElement { + const { + data: aliases, + isLoading, + refetch, + } = useQuery, AxiosError>(["get_aliases"], getAliases) + + return ( + + {isLoading ? ( + + ) : ( + aliases?.map?.(alias => ( + + )) + )} + refetch()} /> + + ) +} diff --git a/src/server-types.d.ts b/src/server-types.d.ts index 77b51ac..1184449 100644 --- a/src/server-types.d.ts +++ b/src/server-types.d.ts @@ -13,6 +13,11 @@ export enum ProxyUserAgentType { CHROME = "chrome", } +export enum AliasType { + Random = "random", + Custom = "custom", +} + export interface User { id: string createdAt: Date @@ -50,3 +55,16 @@ export interface ServerSettings { export interface MinimumServerResponse { detail?: string } + +export interface Alias { + id: string + domain: string + local: string + isActive: boolean + encryptedNotes: string + removeTrackers: boolean + createMailReport: boolean + proxyImages: boolean + imageProxyFormat: ImageProxyFormatType + imageProxyUserAgent: ProxyUserAgentType +}