diff --git a/src/App.tsx b/src/App.tsx index 1d4cc98de..2976f9c10 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import { BrowserRouter as Router } from 'react-router-dom'; import Root from './components/Root/Root'; -import ConfigProvider from './providers/configProvider'; +import ConfigProvider from './providers/ConfigProvider'; import QueryProvider from './providers/QueryProvider'; import UIStateProvider from './providers/uiStateProvider'; import './styles/main.scss'; diff --git a/src/components/Layout/Layout.tsx b/src/components/Layout/Layout.tsx index 71e99c2f1..48178d362 100644 --- a/src/components/Layout/Layout.tsx +++ b/src/components/Layout/Layout.tsx @@ -4,7 +4,7 @@ import ButtonLink from '../ButtonLink/ButtonLink'; import Header from '../Header/Header'; import SideBar from '../SideBar/SideBar'; import DynamicBlur from '../DynamicBlur/DynamicBlur'; -import { ConfigContext } from '../../providers/configProvider'; +import { ConfigContext } from '../../providers/ConfigProvider'; import { UIStateContext } from '../../providers/uiStateProvider'; import styles from './Layout.module.scss'; diff --git a/src/components/LoadingOverlay/LoadingOverlay.module.scss b/src/components/LoadingOverlay/LoadingOverlay.module.scss new file mode 100644 index 000000000..185389eb8 --- /dev/null +++ b/src/components/LoadingOverlay/LoadingOverlay.module.scss @@ -0,0 +1,56 @@ +@use '../../styles/variables'; +@use '../../styles/theme'; + +.loadingOverlay { + position: fixed; + top: 0; + left: 0; + z-index: variables.$loading-z-index; + display: flex; + justify-content: center; + align-items: center; + width: 100vw; + height: 100vh; + + background-color: var(--body-background-color); +} + +.buffer { + position: relative; + display: inline-block; + width: 80px; + height: 80px; +} + +.buffer div { + position: absolute; + display: block; + width: 64px; + height: 64px; + margin: 8px; + border: 4px solid #fff; + border-color: #fff transparent transparent transparent; + border-radius: 50%; + animation: buffer 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; +} + +.buffer div:nth-child(1) { + animation-delay: -0.45s; +} + +.buffer div:nth-child(2) { + animation-delay: -0.3s; +} + +.buffer div:nth-child(3) { + animation-delay: -0.15s; +} + +@keyframes buffer { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} diff --git a/src/components/LoadingOverlay/LoadingOverlay.tsx b/src/components/LoadingOverlay/LoadingOverlay.tsx new file mode 100644 index 000000000..88946f393 --- /dev/null +++ b/src/components/LoadingOverlay/LoadingOverlay.tsx @@ -0,0 +1,18 @@ +import React from 'react'; + +import styles from './LoadingOverlay.module.scss'; + +const LoadingOverlay: React.FC = () => { + return ( +
+
+
+
+
+
+
+
+ ); +}; + +export default LoadingOverlay; diff --git a/src/components/Root/Root.tsx b/src/components/Root/Root.tsx index 6f54a3c81..5440f7d3e 100644 --- a/src/components/Root/Root.tsx +++ b/src/components/Root/Root.tsx @@ -2,8 +2,11 @@ import React, { FC } from 'react'; import { Route, Switch } from 'react-router-dom'; import Layout from '../Layout/Layout'; -import Playlist from '../../screens/Playlist/Playlist'; import Home from '../../screens/Home/Home'; +import Playlist from '../../screens/Playlist/Playlist'; +import Settings from '../../screens/Settings/Settings'; +import Video from '../../screens/Video/Video'; +import Series from '../../screens/Series/Series'; type Props = { error?: Error | null; @@ -19,7 +22,9 @@ const Root: FC = ({ error }: Props) => { - Settings} exact /> + + + ); diff --git a/src/providers/configProvider.tsx b/src/providers/configProvider.tsx index 91c3c1884..85432a79c 100644 --- a/src/providers/configProvider.tsx +++ b/src/providers/configProvider.tsx @@ -3,6 +3,7 @@ import merge from 'lodash.merge'; import loadConfig, { validateConfig } from '../services/config.service'; import type { Config, Options } from '../../types/Config'; +import LoadingOverlay from '../components/LoadingOverlay/LoadingOverlay'; const defaultConfig: Config = { id: '', @@ -34,20 +35,24 @@ const ConfigProvider: FunctionComponent = ({ onValidationError, }) => { const [config, setConfig] = useState(defaultConfig); + const [loading, setLoading] = useState(true); useEffect(() => { const loadAndValidateConfig = async (configLocation: string) => { onLoading(true); + setLoading(true); const config = await loadConfig(configLocation); validateConfig(config) .then((configValidated) => { setConfig(() => merge({}, defaultConfig, configValidated)); setCssVariables(configValidated.options); onLoading(false); + setLoading(false); }) .catch((error: Error) => { onValidationError(error); onLoading(false); + setLoading(false); }); }; loadAndValidateConfig(configLocation); @@ -65,7 +70,12 @@ const ConfigProvider: FunctionComponent = ({ } }; - return {children}; + return ( + + {loading ? : null} + {children} + + ); }; export default ConfigProvider; diff --git a/src/screens/Home/Home.tsx b/src/screens/Home/Home.tsx index 87ada4ae1..1a6dfe778 100644 --- a/src/screens/Home/Home.tsx +++ b/src/screens/Home/Home.tsx @@ -9,7 +9,7 @@ import type { PlaylistItem } from 'types/playlist'; import { featuredTileBreakpoints, tileBreakpoints } from '../../components/Shelf/Shelf'; import { UIStateContext, UpdateBlurImage } from '../../providers/uiStateProvider'; import Shelf from '../../container/Shelf/Shelf'; -import { ConfigContext } from '../../providers/configProvider'; +import { ConfigContext } from '../../providers/ConfigProvider'; import type { UsePlaylistResult } from '../../hooks/usePlaylist'; import usePlaylist from '../../hooks/usePlaylist'; import useBreakpoint from '../../hooks/useBreakpoint'; diff --git a/src/screens/Series/Series.module.scss b/src/screens/Series/Series.module.scss new file mode 100644 index 000000000..959a1878b --- /dev/null +++ b/src/screens/Series/Series.module.scss @@ -0,0 +1,2 @@ +@use '../../styles/variables'; +@use '../../styles/theme'; diff --git a/src/screens/Series/Series.test.tsx b/src/screens/Series/Series.test.tsx new file mode 100644 index 000000000..0ed5d331d --- /dev/null +++ b/src/screens/Series/Series.test.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { render } from '@testing-library/react'; + +import Series from './Series'; + +describe('', () => { + test('renders and matches snapshot', () => { + const { container } = render(); + + expect(container).toMatchSnapshot(); + }); +}); diff --git a/src/screens/Series/Series.tsx b/src/screens/Series/Series.tsx new file mode 100644 index 000000000..082704ad4 --- /dev/null +++ b/src/screens/Series/Series.tsx @@ -0,0 +1,17 @@ +import React from 'react'; + +import styles from './Series.module.scss'; + +type Props = { + prop?: string; +}; + +const Series: React.FC = ({ prop }: Props) => { + return ( +
+

{prop}

+
+ ); +}; + +export default Series; diff --git a/src/screens/Series/__snapshots__/Series.test.tsx.snap b/src/screens/Series/__snapshots__/Series.test.tsx.snap new file mode 100644 index 000000000..95cbc9226 --- /dev/null +++ b/src/screens/Series/__snapshots__/Series.test.tsx.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders and matches snapshot 1`] = ` +
+
+

+

+
+`; diff --git a/src/screens/Settings/Settings.module.scss b/src/screens/Settings/Settings.module.scss new file mode 100644 index 000000000..959a1878b --- /dev/null +++ b/src/screens/Settings/Settings.module.scss @@ -0,0 +1,2 @@ +@use '../../styles/variables'; +@use '../../styles/theme'; diff --git a/src/screens/Settings/Settings.test.tsx b/src/screens/Settings/Settings.test.tsx new file mode 100644 index 000000000..90bf8e106 --- /dev/null +++ b/src/screens/Settings/Settings.test.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { render } from '@testing-library/react'; + +import Settings from './Settings'; + +describe('', () => { + test('renders and matches snapshot', () => { + const { container } = render(); + + expect(container).toMatchSnapshot(); + }); +}); diff --git a/src/screens/Settings/Settings.tsx b/src/screens/Settings/Settings.tsx new file mode 100644 index 000000000..76669c8d3 --- /dev/null +++ b/src/screens/Settings/Settings.tsx @@ -0,0 +1,17 @@ +import React from 'react'; + +import styles from './Settings.module.scss'; + +type Props = { + prop?: string; +}; + +const Settings: React.FC = ({ prop }: Props) => { + return ( +
+

{prop}

+
+ ); +}; + +export default Settings; diff --git a/src/screens/Settings/__snapshots__/Settings.test.tsx.snap b/src/screens/Settings/__snapshots__/Settings.test.tsx.snap new file mode 100644 index 000000000..6157412e9 --- /dev/null +++ b/src/screens/Settings/__snapshots__/Settings.test.tsx.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders and matches snapshot 1`] = ` +
+
+

+

+
+`; diff --git a/src/screens/Video/Video.module.scss b/src/screens/Video/Video.module.scss new file mode 100644 index 000000000..959a1878b --- /dev/null +++ b/src/screens/Video/Video.module.scss @@ -0,0 +1,2 @@ +@use '../../styles/variables'; +@use '../../styles/theme'; diff --git a/src/screens/Video/Video.test.tsx b/src/screens/Video/Video.test.tsx new file mode 100644 index 000000000..fcc710bba --- /dev/null +++ b/src/screens/Video/Video.test.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { render } from '@testing-library/react'; + +import Video from './Video'; + +describe('