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 (
+
+ );
+};
+
+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 (
+
+ );
+};
+
+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('