From a60a053b6b08d8ed2c45d85aaf8d9dcad451827a Mon Sep 17 00:00:00 2001 From: jeffvli Date: Sun, 30 Oct 2022 01:43:32 -0700 Subject: [PATCH] Add action-required route --- .../components/action-required-container.tsx | 26 ++++++ .../components/mpv-required.tsx | 32 +++++++ .../components/server-credential-required.tsx | 19 ++++ .../components/server-required.tsx | 10 +++ .../features/action-required/index.ts | 1 + .../routes/action-required-route.tsx | 87 +++++++++++++++++++ src/renderer/router/app-outlet.tsx | 36 +++++++- src/renderer/router/app-router.tsx | 7 ++ src/renderer/router/routes.ts | 2 + 9 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 src/renderer/features/action-required/components/action-required-container.tsx create mode 100644 src/renderer/features/action-required/components/mpv-required.tsx create mode 100644 src/renderer/features/action-required/components/server-credential-required.tsx create mode 100644 src/renderer/features/action-required/components/server-required.tsx create mode 100644 src/renderer/features/action-required/index.ts create mode 100644 src/renderer/features/action-required/routes/action-required-route.tsx diff --git a/src/renderer/features/action-required/components/action-required-container.tsx b/src/renderer/features/action-required/components/action-required-container.tsx new file mode 100644 index 000000000..52656bfe4 --- /dev/null +++ b/src/renderer/features/action-required/components/action-required-container.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { Stack, Group } from '@mantine/core'; +import { RiAlertFill } from 'react-icons/ri'; +import { Text } from '@/renderer/components'; + +interface ActionRequiredContainerProps { + children: React.ReactNode; + title: string; +} + +export const ActionRequiredContainer = ({ + title, + children, +}: ActionRequiredContainerProps) => { + return ( + + + + + {title} + + + {children} + + ); +}; diff --git a/src/renderer/features/action-required/components/mpv-required.tsx b/src/renderer/features/action-required/components/mpv-required.tsx new file mode 100644 index 000000000..59b7d8b9b --- /dev/null +++ b/src/renderer/features/action-required/components/mpv-required.tsx @@ -0,0 +1,32 @@ +import { useEffect, useState } from 'react'; +import isElectron from 'is-electron'; +import { FileInput, Text, Button } from '@/renderer/components'; +import { settings } from '@/renderer/features/settings'; + +export const MpvRequired = () => { + const [mpvPath, setMpvPath] = useState(''); + const handleSetMpvPath = (e: File) => { + settings.set('mpv_path', e.path); + }; + + useEffect(() => { + const getMpvPath = async () => { + if (!isElectron()) return setMpvPath(''); + const mpvPath = await settings.get('mpv_path'); + return setMpvPath(mpvPath); + }; + + getMpvPath(); + }, []); + + return ( + <> + + MPV is required for local playback. Set your MPV executable location + below. + + + + + ); +}; diff --git a/src/renderer/features/action-required/components/server-credential-required.tsx b/src/renderer/features/action-required/components/server-credential-required.tsx new file mode 100644 index 000000000..f3a4280ca --- /dev/null +++ b/src/renderer/features/action-required/components/server-credential-required.tsx @@ -0,0 +1,19 @@ +import { Text } from '@/renderer/components'; +import { useAuthStore } from '@/renderer/store'; + +export const ServerCredentialRequired = () => { + const currentServer = useAuthStore((state) => state.currentServer); + + return ( + <> + + The selected server '{currentServer?.name}' requires an + additional login to access. + + + Add your credentials in the 'manage servers' menu or switch to + a different server. + + + ); +}; diff --git a/src/renderer/features/action-required/components/server-required.tsx b/src/renderer/features/action-required/components/server-required.tsx new file mode 100644 index 000000000..e235cd353 --- /dev/null +++ b/src/renderer/features/action-required/components/server-required.tsx @@ -0,0 +1,10 @@ +import { Text } from '@/renderer/components'; + +export const ServerRequired = () => { + return ( + <> + No server selected. + Add or select a server in the file menu. + + ); +}; diff --git a/src/renderer/features/action-required/index.ts b/src/renderer/features/action-required/index.ts new file mode 100644 index 000000000..99c1a7eaf --- /dev/null +++ b/src/renderer/features/action-required/index.ts @@ -0,0 +1 @@ +export * from './routes/action-required-route'; diff --git a/src/renderer/features/action-required/routes/action-required-route.tsx b/src/renderer/features/action-required/routes/action-required-route.tsx new file mode 100644 index 000000000..9e66bca26 --- /dev/null +++ b/src/renderer/features/action-required/routes/action-required-route.tsx @@ -0,0 +1,87 @@ +import { useState, useEffect } from 'react'; +import { Center, Group, Stack } from '@mantine/core'; +import isElectron from 'is-electron'; +import { RiCheckFill } from 'react-icons/ri'; +import { Link } from 'react-router-dom'; +import { Button, Text } from '@/renderer/components'; +import { ActionRequiredContainer } from '@/renderer/features/action-required/components/action-required-container'; +import { MpvRequired } from '@/renderer/features/action-required/components/mpv-required'; +import { ServerCredentialRequired } from '@/renderer/features/action-required/components/server-credential-required'; +import { ServerRequired } from '@/renderer/features/action-required/components/server-required'; +import { settings } from '@/renderer/features/settings'; +import { useServerCredential } from '@/renderer/features/shared'; +import { AppRoute } from '@/renderer/router/routes'; +import { useAuthStore } from '@/renderer/store'; + +export const ActionRequiredRoute = () => { + const { serverToken } = useServerCredential(); + const currentServer = useAuthStore((state) => state.currentServer); + const [isMpvRequired, setIsMpvRequired] = useState(false); + const isServerRequired = !currentServer; + const isCredentialRequired = currentServer?.noCredential && !serverToken; + + useEffect(() => { + const getMpvPath = async () => { + if (!isElectron()) return setIsMpvRequired(false); + const mpvPath = await settings.get('mpv_path'); + return setIsMpvRequired(!mpvPath); + }; + + getMpvPath(); + }, []); + + const checks = [ + { + component: , + title: 'MPV required', + valid: !isMpvRequired, + }, + { + component: , + title: 'Credentials required', + valid: !isCredentialRequired, + }, + { + component: , + title: 'Server required', + valid: !isServerRequired, + }, + ]; + + const canReturnHome = checks.every((c) => c.valid); + const displayedCheck = checks.find((c) => !c.valid); + + return ( + <> +
+ + + {displayedCheck && ( + + {displayedCheck?.component} + + )} + + + {canReturnHome && ( + <> + + + No issues found. + + + + )} + + +
+ + ); +}; diff --git a/src/renderer/router/app-outlet.tsx b/src/renderer/router/app-outlet.tsx index 1f438f6f2..621f32522 100644 --- a/src/renderer/router/app-outlet.tsx +++ b/src/renderer/router/app-outlet.tsx @@ -1,4 +1,9 @@ +import { useState, useEffect } from 'react'; +import isElectron from 'is-electron'; import { Navigate, Outlet, useLocation } from 'react-router-dom'; +import { settings } from '@/renderer/features/settings'; +import { useServerCredential } from '@/renderer/features/shared'; +import { AppRoute } from '@/renderer/router/routes'; import { useAuthStore } from '@/renderer/store'; interface PrivateOutletProps { @@ -7,8 +12,37 @@ interface PrivateOutletProps { export const AppOutlet = ({ redirectTo }: PrivateOutletProps) => { const location = useLocation(); - const isAuthenticated = useAuthStore((state) => !!state.accessToken); const logout = useAuthStore((state) => state.logout); + const isAuthenticated = useAuthStore((state) => !!state.accessToken); + const { serverToken } = useServerCredential(); + const currentServer = useAuthStore((state) => state.currentServer); + + const [isMpvRequired, setIsMpvRequired] = useState(false); + const isServerRequired = !currentServer; + const isCredentialRequired = currentServer?.noCredential && !serverToken; + + useEffect(() => { + const getMpvPath = async () => { + if (!isElectron()) return setIsMpvRequired(false); + const mpvPath = await settings.get('mpv_path'); + return setIsMpvRequired(!mpvPath); + }; + + getMpvPath(); + }, []); + + const actions = [isServerRequired, isCredentialRequired, isMpvRequired]; + const actionRequired = actions.some((c) => c); + + if (actionRequired) { + return ( + + ); + } if (isAuthenticated) { return ; diff --git a/src/renderer/router/app-router.tsx b/src/renderer/router/app-router.tsx index 0f16118b7..b0352b862 100644 --- a/src/renderer/router/app-router.tsx +++ b/src/renderer/router/app-router.tsx @@ -1,5 +1,6 @@ /* eslint-disable sort-keys-fix/sort-keys-fix */ import { Routes, Route, Link } from 'react-router-dom'; +import { ActionRequiredRoute } from '@/renderer/features/action-required'; import { AlbumListRoute } from '@/renderer/features/albums'; import { LoginRoute } from '@/renderer/features/auth'; import { DashboardRoute } from '@/renderer/features/dashboard'; @@ -28,6 +29,12 @@ export const AppRouter = () => { } path={AppRoute.PLAYING} /> + }> + } + path={AppRoute.ACTION_REQUIRED} + /> + ); }; diff --git a/src/renderer/router/routes.ts b/src/renderer/router/routes.ts index 82ef096e3..7a9f377b2 100644 --- a/src/renderer/router/routes.ts +++ b/src/renderer/router/routes.ts @@ -1,6 +1,7 @@ // Referenced from: https://betterprogramming.pub/the-best-way-to-manage-routes-in-a-react-project-with-typescript-c4e8d4422d64 export enum AppRoute { + ACTION_REQUIRED = '/action-required', EXPLORE = '/explore', HOME = '/', LIBRARY_ALBUMARTISTS = '/library/album-artists', @@ -22,6 +23,7 @@ export enum AppRoute { type TArgs = | { path: AppRoute.HOME } + | { path: AppRoute.ACTION_REQUIRED } | { path: AppRoute.NOW_PLAYING } | { path: AppRoute.EXPLORE } | { path: AppRoute.LOGIN }