diff --git a/.eslintrc b/.eslintrc
index ba178545c..e5e605cd2 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -3,7 +3,7 @@
"plugins": ["prettier"],
"rules": {
"prettier/prettier": "error",
- "react/jsx-filename-extension": "error",
+ "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
"react-hooks/exhaustive-deps": "warn",
"import/no-unresolved": ["off", { "ignore": [".css$"] }],
"import/prefer-default-export": "off",
diff --git a/.gitignore b/.gitignore
index 4d29575de..179eb2ebe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,7 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
+
+#netlify
+
+.netlify
diff --git a/package.json b/package.json
index 5bc0e0d0d..3c542715e 100644
--- a/package.json
+++ b/package.json
@@ -8,9 +8,11 @@
"@testing-library/user-event": "^12.1.3",
"react": "^16.13.1",
"react-dom": "^16.13.1",
+ "react-icons": "^4.2.0",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
- "react-scripts": "3.4.3"
+ "react-scripts": "3.4.3",
+ "styled-components": "^5.3.0"
},
"scripts": {
"start": "react-scripts start",
@@ -34,6 +36,9 @@
"prettier": "^2.1.1",
"pretty-quick": "^3.0.0"
},
+ "resolutions": {
+ "styled-components": "^5"
+ },
"browserslist": {
"production": [
">0.2%",
diff --git a/src/components/App/App.component.jsx b/src/components/App/App.component.jsx
index e372d6849..66144e8f9 100644
--- a/src/components/App/App.component.jsx
+++ b/src/components/App/App.component.jsx
@@ -1,57 +1,73 @@
-import React, { useLayoutEffect } from 'react';
+import React, { useState } from 'react';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
+import { ThemeProvider } from 'styled-components';
import AuthProvider from '../../providers/Auth';
import HomePage from '../../pages/Home';
+import FavoritesPage from '../../pages/Favorites';
import LoginPage from '../../pages/Login';
import NotFound from '../../pages/NotFound';
import SecretPage from '../../pages/Secret';
import Private from '../Private';
-import Fortune from '../Fortune';
import Layout from '../Layout';
-import { random } from '../../utils/fns';
-function App() {
- useLayoutEffect(() => {
- const { body } = document;
-
- function rotateBackground() {
- const xPercent = random(100);
- const yPercent = random(100);
- body.style.setProperty('--bg-position', `${xPercent}% ${yPercent}%`);
- }
-
- const intervalId = setInterval(rotateBackground, 3000);
- body.addEventListener('click', rotateBackground);
-
- return () => {
- clearInterval(intervalId);
- body.removeEventListener('click', rotateBackground);
- };
- }, []);
+import { GlobalStyle, themes } from '../../globalStyles';
+import VideoPlayer from '../../pages/VideoPlayer';
+import ThemeContext from '../Context/ThemeContext';
+import VideoContext from '../Context/VideoContext';
+import { FavoritesProvider } from '../../providers/Favorites/Favorites.provider';
+import FavoriteDetailsPage from '../../pages/FavoriteDetails/FavoriteDetails.page';
+import VideoFavoriteContext from '../Context/VideoFavorite';
+function App() {
+ const [theme, setTheme] = useState('light');
+ const [video, setVideo] = useState({});
+ const [videoFavorite, setVideoFavorite] = useState({});
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
);
}
diff --git a/src/components/CardVideo/CardVideo.component.jsx b/src/components/CardVideo/CardVideo.component.jsx
new file mode 100644
index 000000000..9a3f2a14c
--- /dev/null
+++ b/src/components/CardVideo/CardVideo.component.jsx
@@ -0,0 +1,92 @@
+import React, { useContext, useEffect, useState } from 'react';
+import VideoContext from '../Context/VideoContext';
+import VideoFavoriteContext from '../Context/VideoFavorite';
+import LinkVideo from '../Link.element';
+import {
+ CardVideoDisplayerContainer,
+ CardVideoContainer,
+ CardVideoImage,
+ CardVideoBottom,
+} from './CardVideo.elements';
+
+export function CardVideo({ video, id }) {
+ const urlVideo = `/watch?v=${id}`;
+
+ const { setVideo } = useContext(VideoContext);
+
+ const handleClick = () => {
+ setVideo(video);
+ };
+
+ return (
+ <>
+
+
+
+
+
+
+ >
+ );
+}
+
+export function CardVideoFavorite({ video, id }) {
+ const urlVideo = `favorites/${id}`;
+
+ const { setVideoFavorite } = useContext(VideoFavoriteContext);
+
+ const handleClick = () => {
+ setVideoFavorite(id);
+ };
+
+ return (
+ <>
+
+
+
+
+
+
+ >
+ );
+}
+
+function CardVideoDisplayer({ videos }) {
+ const [listVideos, setListVideo] = useState();
+
+ useEffect(() => {
+ if (videos) {
+ setListVideo(
+ videos.map((video) => (
+
+ ))
+ );
+ }
+ }, [videos]);
+
+ return (
+ <>
+ {listVideos}
+ >
+ );
+}
+
+export default CardVideoDisplayer;
diff --git a/src/components/CardVideo/CardVideo.elements.jsx b/src/components/CardVideo/CardVideo.elements.jsx
new file mode 100644
index 000000000..e0cb586cd
--- /dev/null
+++ b/src/components/CardVideo/CardVideo.elements.jsx
@@ -0,0 +1,81 @@
+import React from 'react';
+import styled from 'styled-components';
+
+export const CardVideoDisplayerContainer = styled.div`
+ display: flex;
+ flex-wrap: wrap;
+ position: absolute;
+ justify-content: center;
+ padding-bottom: 2rem;
+
+ top: ${(props) => props.theme.navbar_height};
+ left: ${(props) => props.theme.sidemenu_width};
+
+ width: calc(100vw - ${(props) => props.theme.sidemenu_width});
+`;
+
+export const CardVideoContainer = styled.div`
+ width: calc((100vw - ${({ theme }) => theme.sidemenu_width}) / 1 - 4rem - 0.01px);
+ aspect-ratio: 16/14;
+ margin-top: 2rem;
+ margin-left: 1rem;
+ margin-right: 1rem;
+
+ @media (min-width: 750px) {
+ width: calc((100vw - ${({ theme }) => theme.sidemenu_width}) / 2 - 4rem - 0.01px);
+ }
+
+ @media (min-width: 1000px) {
+ width: calc((100vw - ${({ theme }) => theme.sidemenu_width}) / 3 - 4rem - 0.01px);
+ }
+
+ &:hover {
+ cursor: pointer;
+ }
+`;
+
+export const CardVideoImage = styled.img`
+ width: 100%;
+ aspect-ratio: 16/9;
+ height: auto;
+`;
+
+const CardVideoBottomContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+ aspect-ratio: 16/5;
+ align-items: center;
+ justify-content: center;
+`;
+
+const CardVideoTitle = styled.h2`
+ color: ${(props) => props.theme.text_color};
+ font-size: 1.2rem;
+ padding: 0.5rem 0.3rem;
+ text-align: center;
+`;
+
+const CardVideoDescription = styled.p`
+ color: ${(props) => props.theme.text_color};
+
+ padding-left: 0.5rem;
+ padding-right: 0.5rem;
+ padding-bottom: 0.5rem;
+ font-size: 1rem;
+`;
+
+export function CardVideoBottom({ title, description }) {
+ function trucateText(text) {
+ const maxLength = 50;
+ return text.length <= maxLength ? text : `${text.substring(0, maxLength)}...`;
+ }
+
+ return (
+ <>
+
+ {title}
+ {trucateText(description)}
+
+ >
+ );
+}
diff --git a/src/components/CardVideo/CardVideo.test.js b/src/components/CardVideo/CardVideo.test.js
new file mode 100644
index 000000000..f59feb363
--- /dev/null
+++ b/src/components/CardVideo/CardVideo.test.js
@@ -0,0 +1,37 @@
+import { render, screen } from '@testing-library/react';
+import React from 'react';
+import CardVideoDisplayer from './CardVideo.component';
+
+describe('navbar', () => {
+ beforeEach(() => {
+ const videos = [
+ {
+ etag: 'sadlkjsakdljsa',
+ snippet: {
+ title: 'Wizeline',
+ description: 'hlksjdakjskldja asjdaks',
+ thumbnails: {
+ high: {
+ url:
+ 'https://yt3.ggpht.com/ytc/AAUvwnighSReQlmHl_S_vSfvnWBAG5Cw4A0YxtE0tm5OpQ=s800-c-k-c0xffffffff-no-rj-mo',
+ },
+ },
+ },
+ },
+ ];
+
+ render();
+ });
+
+ test('should contains a title', () => {
+ const title = screen.queryByText('Wizeline');
+
+ expect(title).toBeInTheDocument();
+ });
+
+ test('should contains a img', () => {
+ const img = screen.queryByText('hlksjdakjskldja asjdaks');
+
+ expect(img).toBeInTheDocument();
+ });
+});
diff --git a/src/components/CardVideo/index.js b/src/components/CardVideo/index.js
new file mode 100644
index 000000000..d38c3435c
--- /dev/null
+++ b/src/components/CardVideo/index.js
@@ -0,0 +1 @@
+export { default, CardVideo } from './CardVideo.component';
diff --git a/src/components/Context/ThemeContext.js b/src/components/Context/ThemeContext.js
new file mode 100644
index 000000000..54556a3fd
--- /dev/null
+++ b/src/components/Context/ThemeContext.js
@@ -0,0 +1,5 @@
+import { createContext } from 'react';
+
+const ThemeContext = createContext({});
+
+export default ThemeContext;
diff --git a/src/components/Context/VideoContext.js b/src/components/Context/VideoContext.js
new file mode 100644
index 000000000..737fb1583
--- /dev/null
+++ b/src/components/Context/VideoContext.js
@@ -0,0 +1,5 @@
+import { createContext } from 'react';
+
+const VideoContext = createContext({});
+
+export default VideoContext;
diff --git a/src/components/Context/VideoFavorite.js b/src/components/Context/VideoFavorite.js
new file mode 100644
index 000000000..cb0ca5c31
--- /dev/null
+++ b/src/components/Context/VideoFavorite.js
@@ -0,0 +1,5 @@
+import { createContext } from 'react';
+
+const VideoFavoriteContext = createContext({});
+
+export default VideoFavoriteContext;
diff --git a/src/components/Link.element.jsx b/src/components/Link.element.jsx
new file mode 100644
index 000000000..f41a3f9b0
--- /dev/null
+++ b/src/components/Link.element.jsx
@@ -0,0 +1,8 @@
+import styled from 'styled-components';
+import { Link } from 'react-router-dom';
+
+const LinkVideo = styled(Link)`
+ text-decoration: none;
+`;
+
+export default LinkVideo;
diff --git a/src/components/Navbar/Navbar.component.jsx b/src/components/Navbar/Navbar.component.jsx
new file mode 100644
index 000000000..7bc1511da
--- /dev/null
+++ b/src/components/Navbar/Navbar.component.jsx
@@ -0,0 +1,26 @@
+import React, { createRef } from 'react';
+import { useHistory } from 'react-router';
+import { Nav, IconLogo, SearchBar, ProfileImg } from './Navbar.elements';
+
+function Navbar() {
+ const history = useHistory();
+
+ const inputRef = createRef();
+
+ const handleSubmit = (event) => {
+ event.preventDefault();
+ history.push(`/?q=${inputRef.current.value}`);
+ };
+
+ return (
+ <>
+
+ >
+ );
+}
+
+export default Navbar;
diff --git a/src/components/Navbar/Navbar.elements.jsx b/src/components/Navbar/Navbar.elements.jsx
new file mode 100644
index 000000000..3ce77cc73
--- /dev/null
+++ b/src/components/Navbar/Navbar.elements.jsx
@@ -0,0 +1,88 @@
+import React from 'react';
+import styled from 'styled-components';
+import { AiFillYoutube } from 'react-icons/ai';
+import { FaSearch } from 'react-icons/fa';
+
+export const Nav = styled.nav`
+ background: ${(props) => props.theme.background_color};
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ width: 100vw;
+ height: ${(props) => props.theme.navbar_height};
+ position: fixed;
+ top: 0;
+ z-index: 99;
+ padding: 0rem 1.5rem;
+`;
+
+const LogoContainer = styled.div`
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: flex-start;
+ align-items: center;
+ user-select: none;
+`;
+
+const LogoText = styled.h1`
+ color: ${(props) => props.theme.text_color};
+ margin-left: 0.5rem;
+ font-size: 2rem;
+ font-weight: 900;
+ @media (max-width: 600px) {
+ display: none;
+ }
+`;
+
+export function IconLogo() {
+ return (
+
+
+ WizeTube
+
+ );
+}
+
+const FormSearch = styled.form`
+ display: flex;
+ display: row;
+`;
+
+const InputSearch = styled.input`
+ padding: 0rem 0.5rem;
+ border: 1px solid ${(props) => props.theme.icon_color};
+ margin-left: 1rem;
+ width: 350px;
+
+ @media (max-width: 737px) {
+ width: 200px;
+ }
+`;
+
+const SearchButton = styled.button`
+ color: ${(props) => props.theme.icon_color};
+ display: flex;
+ justify-content: center;
+ align-content: center;
+ padding: 0.5rem 0.75rem;
+ margin-right: 1rem;
+`;
+
+export function SearchBar({ inputRef, handleSubmit }) {
+ return (
+
+
+
+
+
+
+ );
+}
+
+export const ProfileImg = styled.img`
+ border-radius: 50%;
+ width: 3.5rem;
+ height: auto;
+`;
diff --git a/src/components/Navbar/Navbar.test.js b/src/components/Navbar/Navbar.test.js
new file mode 100644
index 000000000..f29f4e11b
--- /dev/null
+++ b/src/components/Navbar/Navbar.test.js
@@ -0,0 +1,33 @@
+import { render, screen } from '@testing-library/react';
+import React from 'react';
+import Navbar from './Navbar.component';
+
+describe('navbar', () => {
+ beforeEach(() => {
+ render();
+ });
+
+ test('should contains a title', () => {
+ const title = screen.queryByText(/wizetube/i);
+
+ expect(title).toBeInTheDocument();
+ });
+
+ test('should contains a profile picture', () => {
+ const picture = screen.queryByRole('img');
+
+ expect(picture).toBeInTheDocument();
+ });
+
+ test('should contains a search input', () => {
+ const input = screen.queryByPlaceholderText(/search/i);
+
+ expect(input).toBeInTheDocument();
+ });
+
+ test('should contains a search button', () => {
+ const button = screen.queryByRole('button');
+
+ expect(button).toBeInTheDocument();
+ });
+});
diff --git a/src/components/Navbar/index.js b/src/components/Navbar/index.js
new file mode 100644
index 000000000..3b39ac26f
--- /dev/null
+++ b/src/components/Navbar/index.js
@@ -0,0 +1 @@
+export { default } from './Navbar.component';
diff --git a/src/components/SideMenu/SideMenu.component.jsx b/src/components/SideMenu/SideMenu.component.jsx
new file mode 100644
index 000000000..3c528c3a5
--- /dev/null
+++ b/src/components/SideMenu/SideMenu.component.jsx
@@ -0,0 +1,46 @@
+import React, { useContext } from 'react';
+
+import { AiFillHome, AiFillStar } from 'react-icons/ai';
+import { FaSun, FaMoon } from 'react-icons/fa';
+import ThemeContext from '../Context/ThemeContext';
+import LinkVideo from '../Link.element';
+import { SideMenuContainer, SideMenuItem } from './SideMenu.elements';
+
+import { useAuth } from '../../providers/Auth';
+
+const PrivateLink = () => {
+ const { authenticated } = useAuth();
+
+ return authenticated ? (
+
+
+
+ ) : (
+
+
+
+ );
+};
+
+function SideMenu() {
+ const { theme, setTheme } = useContext(ThemeContext);
+
+ const icon = theme === 'light' ? FaMoon : FaSun;
+ const text = theme === 'light' ? 'Dark Mode' : 'Light Mode';
+
+ const handleTheme = () => setTheme(theme === 'light' ? 'dark' : 'light');
+
+ return (
+ <>
+
+
+
+
+
+
+
+ >
+ );
+}
+
+export default SideMenu;
diff --git a/src/components/SideMenu/SideMenu.elements.jsx b/src/components/SideMenu/SideMenu.elements.jsx
new file mode 100644
index 000000000..3c4dfe2d3
--- /dev/null
+++ b/src/components/SideMenu/SideMenu.elements.jsx
@@ -0,0 +1,40 @@
+import React from 'react';
+import styled from 'styled-components';
+
+export const SideMenuContainer = styled.ul`
+ display: flex;
+ flex-direction: column;
+ flex-wrap: wrap;
+ position: fixed;
+ top: ${(props) => props.theme.navbar_height};
+ left: 0;
+ height: calc(100vh - ${(props) => props.theme.navbar_height});
+ width: ${(props) => props.theme.sidemenu_width};
+ background: ${(props) => props.theme.background_color};
+`;
+
+const SideMenuOption = styled.li`
+ color: ${(props) => props.theme.icon_color};
+ text-decoration: bold;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ padding: 0.5rem;
+ user-select: none;
+ font-weight: bold;
+ width: 100%;
+
+ &:hover {
+ background: ${({ theme }) => theme.scrollbar_thumb_hover_color};
+ }
+`;
+
+export function SideMenuItem({ Icon, text, fun }) {
+ return (
+
+
+ {text}
+
+ );
+}
diff --git a/src/components/SideMenu/index.js b/src/components/SideMenu/index.js
new file mode 100644
index 000000000..26f24a2dd
--- /dev/null
+++ b/src/components/SideMenu/index.js
@@ -0,0 +1 @@
+export { default } from './SideMenu.component';
diff --git a/src/components/VideoVisualizer/VideoVisualizer.component.jsx b/src/components/VideoVisualizer/VideoVisualizer.component.jsx
new file mode 100644
index 000000000..aecc285e4
--- /dev/null
+++ b/src/components/VideoVisualizer/VideoVisualizer.component.jsx
@@ -0,0 +1,63 @@
+import React, { useState, useEffect, useContext } from 'react';
+import { FaHeart } from 'react-icons/fa';
+import {
+ RelatedVideosContainer,
+ RelatedVideosTitle,
+ VideoPlayer,
+ VideoVisualizerContainer,
+} from './VideoVisualizer.elements';
+import { CardVideo } from '../CardVideo/CardVideo.component';
+import VideoContext from '../Context/VideoContext';
+import { useFavorites } from '../../providers/Favorites';
+
+function VideoVisualizer({ videoId, videos }) {
+ const [listVideos, setListVideo] = useState();
+ const { video } = useContext(VideoContext);
+ useEffect(() => {
+ if (videos) {
+ setListVideo(
+ videos.map((videoRelated) => (
+
+ ))
+ );
+ }
+ }, [videos]);
+
+ const [buttonClicked, setButtonClicked] = useState(false);
+ const [state, dispatch] = useFavorites();
+ console.log(state);
+ const handleFavClick = () => {
+ if (buttonClicked) {
+ dispatch({
+ type: 'DELETE',
+ payload: video,
+ });
+ setButtonClicked(false);
+ } else {
+ dispatch({
+ type: 'ADD',
+ payload: video,
+ });
+ setButtonClicked(true);
+ }
+ };
+
+ return (
+ <>
+
+
+
+
+ Related Videos
+ {listVideos}
+
+
+ >
+ );
+}
+
+export default VideoVisualizer;
diff --git a/src/components/VideoVisualizer/VideoVisualizer.elements.jsx b/src/components/VideoVisualizer/VideoVisualizer.elements.jsx
new file mode 100644
index 000000000..428139ea2
--- /dev/null
+++ b/src/components/VideoVisualizer/VideoVisualizer.elements.jsx
@@ -0,0 +1,55 @@
+import React from 'react';
+import styled from 'styled-components';
+
+export const VideoVisualizerContainer = styled.div`
+ display: flex;
+ flex-wrap: wrap;
+ position: absolute;
+ justify-content: center;
+ padding: 2rem;
+ align-content: center;
+
+ top: ${(props) => props.theme.navbar_height};
+ left: ${(props) => props.theme.sidemenu_width};
+
+ width: calc(100vw - ${(props) => props.theme.sidemenu_width});
+`;
+
+export const RelatedVideosTitle = styled.h2`
+ width: 100%;
+ text-align: center;
+ font-size: 1.5rem;
+ margin-top: 1rem;
+ margin-bottom: 1rem;
+ color: ${(props) => props.theme.text_color};
+`;
+
+export const RelatedVideosContainer = styled.div`
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ padding-bottom: 2rem;
+
+ width: 100%;
+`;
+
+const VideoPlayerEmbeded = styled.iframe`
+ aspect-ratio: 16/9;
+ width: calc(40vw - ${(props) => props.theme.sidemenu_width});
+ min-width: 350px;
+ margin-bottom: 2rem;
+`;
+
+export function VideoPlayer({ videoId }) {
+ const source = `https://www.youtube.com/embed/${videoId}`;
+
+ return (
+
+ );
+}
diff --git a/src/components/VideoVisualizer/index.js b/src/components/VideoVisualizer/index.js
new file mode 100644
index 000000000..7b618269f
--- /dev/null
+++ b/src/components/VideoVisualizer/index.js
@@ -0,0 +1 @@
+export { default } from './VideoVisualizer.component';
diff --git a/src/global.css b/src/global.css
deleted file mode 100644
index 4feb3c75e..000000000
--- a/src/global.css
+++ /dev/null
@@ -1,53 +0,0 @@
-html {
- font-size: 1.125rem;
- line-height: 1.6;
- font-weight: 400;
- font-family: sans-serif;
- box-sizing: border-box;
- scroll-behavior: smooth;
- -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-}
-
-*,
-*::before,
-*::after {
- box-sizing: inherit;
-}
-
-body {
- margin: 0;
- padding: 0;
- text-rendering: optimizeLegibility;
- background-image: linear-gradient(
- 120deg,
- #eea2a2 0,
- #bbc1bf 19%,
- #57c6e1 42%,
- #b49fda 79%,
- #7ac5d8 100%
- );
- background-size: 400% 400%;
- background-position: var(--bg-position);
- transition: background-position 2s ease;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-.separator::before {
- content: '•';
- color: white;
- padding: 0.4rem;
-}
-
-a {
- text-decoration: none;
- font-weight: bold;
- color: white;
-}
-
-a:active {
- color: blueviolet;
-}
-
-hr {
-}
diff --git a/src/globalStyles.js b/src/globalStyles.js
new file mode 100644
index 000000000..140333a38
--- /dev/null
+++ b/src/globalStyles.js
@@ -0,0 +1,60 @@
+import { createGlobalStyle } from 'styled-components';
+
+const NAVBAR_HEIGHT = '70px';
+const SIDE_MENU_WIDTH = '100px';
+
+export const LightTheme = {
+ background_color: 'white',
+ text_color: 'black',
+ icon_color: '#606060',
+ scrollbar_track_color: 'white',
+ scrollbar_thumb_color: '#666',
+ scrollbar_thumb_hover_color: '#CCC',
+ navbar_height: NAVBAR_HEIGHT,
+ sidemenu_width: SIDE_MENU_WIDTH,
+};
+
+export const DarkTheme = {
+ background_color: '#202020',
+ text_color: 'white',
+ icon_color: '#909090',
+ scrollbar_track_color: '#202020',
+ navbar_height: NAVBAR_HEIGHT,
+ sidemenu_width: SIDE_MENU_WIDTH,
+};
+
+export const themes = {
+ light: LightTheme,
+ dark: DarkTheme,
+};
+
+export const GlobalStyle = createGlobalStyle`
+ * {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+ font-size: 16px;
+ }
+
+ body{
+ background: ${(props) => props.theme.background_color};
+ }
+
+ body::-webkit-scrollbar {
+ width: 0.75rem;
+ }
+
+ body::-webkit-scrollbar-track {
+ background: ${(props) => props.theme.scrollbar_track_color};
+ }
+
+ body::-webkit-scrollbar-thumb {
+ background: ${(props) => props.theme.scrollbar_thumb_color};
+ border-radius: 1.25rem;
+ border: 0.15rem solid ${(props) => props.theme.scrollbar_track_color};
+ }
+
+ body::-webkit-scrollbar-thumb:hover {
+ background: ${(props) => props.theme.scrollbar_thumb_hover_color};
+ }
+`;
diff --git a/src/index.js b/src/index.js
index b93eaa337..1d527be7d 100644
--- a/src/index.js
+++ b/src/index.js
@@ -2,7 +2,6 @@ import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
-import './global.css';
ReactDOM.render(
diff --git a/src/pages/FavoriteDetails/FavoriteDetails.page.jsx b/src/pages/FavoriteDetails/FavoriteDetails.page.jsx
new file mode 100644
index 000000000..0e28fd40d
--- /dev/null
+++ b/src/pages/FavoriteDetails/FavoriteDetails.page.jsx
@@ -0,0 +1,73 @@
+import React, { useEffect, useState, useContext } from 'react';
+import { FaHeart } from 'react-icons/fa';
+import Navbar from '../../components/Navbar';
+import SideMenu from '../../components/SideMenu/SideMenu.component';
+import { CardVideoFavorite } from '../../components/CardVideo/CardVideo.component';
+import { useFavorites } from '../../providers/Favorites';
+import {
+ RelatedVideosContainer,
+ RelatedVideosTitle,
+ VideoPlayer,
+ VideoVisualizerContainer,
+} from '../../components/VideoVisualizer/VideoVisualizer.elements';
+import VideoContext from '../../components/Context/VideoContext';
+import VideoFavoriteContext from '../../components/Context/VideoFavorite';
+
+function FavoriteDetailsPage() {
+ const { videoFavorite } = useContext(VideoFavoriteContext);
+
+ const { video } = useContext(VideoContext);
+
+ const [listVideos, setListVideo] = useState();
+ const [state, dispatch] = useFavorites();
+ console.log(dispatch);
+
+ useEffect(() => {
+ if (state) {
+ setListVideo(
+ state.map((videoFavoriteRelated) => (
+
+ ))
+ );
+ }
+ }, [state]);
+
+ const [buttonClicked, setButtonClicked] = useState(false);
+ console.log(state);
+ const handleFavClick = () => {
+ if (buttonClicked) {
+ dispatch({
+ type: 'DELETE',
+ payload: video,
+ });
+ setButtonClicked(false);
+ } else {
+ dispatch({
+ type: 'ADD',
+ payload: video,
+ });
+ setButtonClicked(true);
+ }
+ };
+
+ return (
+ <>
+
+
+
+
+
+
+ Other Favorite Videos
+ {listVideos}
+
+
+ >
+ );
+}
+
+export default FavoriteDetailsPage;
diff --git a/src/pages/FavoriteDetails/index.js b/src/pages/FavoriteDetails/index.js
new file mode 100644
index 000000000..c5769fb50
--- /dev/null
+++ b/src/pages/FavoriteDetails/index.js
@@ -0,0 +1 @@
+export { default } from './FavoritesDetails.page';
diff --git a/src/pages/Favorites/Favorites.page.jsx b/src/pages/Favorites/Favorites.page.jsx
new file mode 100644
index 000000000..211bd09d5
--- /dev/null
+++ b/src/pages/Favorites/Favorites.page.jsx
@@ -0,0 +1,38 @@
+import React, { useEffect, useState } from 'react';
+
+import Navbar from '../../components/Navbar';
+import SideMenu from '../../components/SideMenu/SideMenu.component';
+import { CardVideoFavorite } from '../../components/CardVideo/CardVideo.component';
+import { useFavorites } from '../../providers/Favorites';
+import {
+ RelatedVideosContainer,
+ RelatedVideosTitle,
+} from '../../components/VideoVisualizer/VideoVisualizer.elements';
+
+function FavoritesPage() {
+ const [listVideos, setListVideo] = useState();
+ const [state, dispatch] = useFavorites();
+ console.log(dispatch);
+
+ useEffect(() => {
+ if (state) {
+ setListVideo(
+ state.map((video) => (
+
+ ))
+ );
+ }
+ }, [state]);
+ return (
+ <>
+
+
+
+ Favorite Videos
+ {listVideos}
+
+ >
+ );
+}
+
+export default FavoritesPage;
diff --git a/src/pages/Favorites/index.js b/src/pages/Favorites/index.js
new file mode 100644
index 000000000..ddb7677c3
--- /dev/null
+++ b/src/pages/Favorites/index.js
@@ -0,0 +1 @@
+export { default } from './Favorites.page';
diff --git a/src/pages/Home/Home.page.jsx b/src/pages/Home/Home.page.jsx
index 08d1dd5c0..60748e07c 100644
--- a/src/pages/Home/Home.page.jsx
+++ b/src/pages/Home/Home.page.jsx
@@ -1,38 +1,22 @@
-import React, { useRef } from 'react';
-import { Link, useHistory } from 'react-router-dom';
+import React from 'react';
-import { useAuth } from '../../providers/Auth';
-import './Home.styles.css';
+import { useLocation } from 'react-router';
+import Navbar from '../../components/Navbar';
+import SideMenu from '../../components/SideMenu/SideMenu.component';
+import CardVideoDisplayer from '../../components/CardVideo/CardVideo.component';
+import useVideo from '../../utils/hooks/useVideo';
function HomePage() {
- const history = useHistory();
- const sectionRef = useRef(null);
- const { authenticated, logout } = useAuth();
+ const searchQuery = new URLSearchParams(useLocation().search).get('q');
- function deAuthenticate(event) {
- event.preventDefault();
- logout();
- history.push('/');
- }
+ const { videos } = useVideo({ searchQuery });
return (
-
- Hello stranger!
- {authenticated ? (
- <>
- Good to have you back
-
-
- ← logout
-
-
- show me something cool →
-
- >
- ) : (
- let me in →
- )}
-
+ <>
+
+
+
+ >
);
}
diff --git a/src/pages/Home/Home.styles.css b/src/pages/Home/Home.styles.css
deleted file mode 100644
index 5e0a702c3..000000000
--- a/src/pages/Home/Home.styles.css
+++ /dev/null
@@ -1,8 +0,0 @@
-.homepage {
- text-align: center;
-}
-
-.homepage h1 {
- font-size: 3rem;
- letter-spacing: -2px;
-}
diff --git a/src/pages/Login/Login.page.jsx b/src/pages/Login/Login.page.jsx
index 89367f276..1d2f1bc20 100644
--- a/src/pages/Login/Login.page.jsx
+++ b/src/pages/Login/Login.page.jsx
@@ -11,7 +11,7 @@ function LoginPage() {
function authenticate(event) {
event.preventDefault();
login();
- history.push('/secret');
+ history.push('/');
}
return (
diff --git a/src/pages/VideoPlayer/VideoPlayer.page.jsx b/src/pages/VideoPlayer/VideoPlayer.page.jsx
new file mode 100644
index 000000000..f2782daed
--- /dev/null
+++ b/src/pages/VideoPlayer/VideoPlayer.page.jsx
@@ -0,0 +1,22 @@
+import React from 'react';
+import { useLocation } from 'react-router';
+import Navbar from '../../components/Navbar';
+import SideMenu from '../../components/SideMenu/SideMenu.component';
+import VideoVisualizer from '../../components/VideoVisualizer/VideoVisualizer.component';
+import useVideo from '../../utils/hooks/useVideo';
+
+function VideoPlayer() {
+ const relatedId = new URLSearchParams(useLocation().search).get('v');
+
+ const { videos } = useVideo({ relatedId });
+
+ return (
+ <>
+
+
+
+ >
+ );
+}
+
+export default VideoPlayer;
diff --git a/src/pages/VideoPlayer/index.js b/src/pages/VideoPlayer/index.js
new file mode 100644
index 000000000..1cbe0f4f1
--- /dev/null
+++ b/src/pages/VideoPlayer/index.js
@@ -0,0 +1 @@
+export { default } from './VideoPlayer.page';
diff --git a/src/providers/Favorites/Favorites.provider.jsx b/src/providers/Favorites/Favorites.provider.jsx
new file mode 100644
index 000000000..deb83929c
--- /dev/null
+++ b/src/providers/Favorites/Favorites.provider.jsx
@@ -0,0 +1,26 @@
+import React, { createContext, useContext, useReducer } from 'react';
+import { FAVORITES_STORAGE_KEY } from '../../utils/constants';
+import { storage } from '../../utils/storage';
+import FavoritesReducer from './Favorites.reducer';
+
+const favoriteVideos = storage.get(FAVORITES_STORAGE_KEY) || [];
+const initialState = favoriteVideos.length ? JSON.parse(favoriteVideos) : [];
+const FavoritesContext = createContext();
+
+const useFavorites = () => {
+ const context = useContext(FavoritesContext);
+ if (!context) {
+ throw new Error(`Can't use "useFavorites" without an FavoritesProvider`);
+ }
+ return context;
+};
+
+const FavoritesProvider = ({ children }) => {
+ const favorites = useReducer(FavoritesReducer, initialState);
+
+ return (
+ {children}
+ );
+};
+
+export { useFavorites, FavoritesProvider };
diff --git a/src/providers/Favorites/Favorites.reducer.js b/src/providers/Favorites/Favorites.reducer.js
new file mode 100644
index 000000000..45f044b0a
--- /dev/null
+++ b/src/providers/Favorites/Favorites.reducer.js
@@ -0,0 +1,23 @@
+import { FAVORITES_STORAGE_KEY } from '../../utils/constants';
+import { storage } from '../../utils/storage';
+
+const FavoritesReducer = (state, action) => {
+ switch (action.type) {
+ case 'ADD': {
+ const favoriteVideos = state.concat(action.payload);
+ storage.set(FAVORITES_STORAGE_KEY, JSON.stringify(favoriteVideos));
+ return [...favoriteVideos];
+ }
+ case 'DELETE': {
+ const favoriteVideos = state.filter(
+ (video) => video.id.videoId !== action.payload.id.videoId
+ );
+ storage.set(FAVORITES_STORAGE_KEY, JSON.stringify(favoriteVideos));
+ return [...favoriteVideos];
+ }
+ default:
+ return state;
+ }
+};
+
+export default FavoritesReducer;
diff --git a/src/providers/Favorites/index.js b/src/providers/Favorites/index.js
new file mode 100644
index 000000000..bc4f31fcc
--- /dev/null
+++ b/src/providers/Favorites/index.js
@@ -0,0 +1 @@
+export { useFavorites, FavoritesProvider } from './Favorites.provider';
diff --git a/src/utils/constants.js b/src/utils/constants.js
index 361273c9c..fba4356c6 100644
--- a/src/utils/constants.js
+++ b/src/utils/constants.js
@@ -1,3 +1,4 @@
const AUTH_STORAGE_KEY = 'wa_cert_authenticated';
+const FAVORITES_STORAGE_KEY = 'wa_cert_favorites';
-export { AUTH_STORAGE_KEY };
+export { AUTH_STORAGE_KEY, FAVORITES_STORAGE_KEY };
diff --git a/src/utils/hooks/useVideo.js b/src/utils/hooks/useVideo.js
new file mode 100644
index 000000000..fb109c079
--- /dev/null
+++ b/src/utils/hooks/useVideo.js
@@ -0,0 +1,43 @@
+import { useState, useEffect } from 'react';
+
+const API_URL = 'https://youtube.googleapis.com/youtube/v3/search?';
+const PART = 'snippet';
+const MAX_RESULTS = 15;
+const TYPE = 'video';
+const YOUTUBE_API_KEY = process.env.REACT_APP_YOUTUBE_API_KEY;
+
+function useVideo(params) {
+ const [videos, setVideos] = useState([]);
+ const { relatedId, searchQuery } = params;
+
+ useEffect(() => {
+ let url = `${API_URL}part=${PART}&maxResults=${MAX_RESULTS}`;
+
+ if (relatedId) {
+ url += `&relatedToVideoId=${relatedId}`;
+ }
+
+ if (searchQuery) {
+ url += `&q=${searchQuery}`;
+ }
+
+ url += `&type=${TYPE}&key=${YOUTUBE_API_KEY}`;
+
+ const getVideos = async () => {
+ try {
+ const response = await fetch(url);
+ const resultItem = await response.json();
+ const list = resultItem.items;
+ setVideos(list);
+ } catch (error) {
+ console.error("That's too bad: ", error);
+ }
+ };
+
+ getVideos();
+ }, [relatedId, searchQuery]);
+
+ return { videos };
+}
+
+export default useVideo;
diff --git a/test-coverage.txt b/test-coverage.txt
new file mode 100644
index 000000000..835556d3c
--- /dev/null
+++ b/test-coverage.txt
@@ -0,0 +1,65 @@
+yarn test --coverage --watchAll=false
+yarn run v1.22.10
+$ react-scripts test --coverage --watchAll=false
+ PASS src/components/CardVideo/CardVideo.test.js
+ PASS src/components/Navbar/Navbar.test.js
+--------------------------|----------|----------|----------|----------|-------------------|
+File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
+--------------------------|----------|----------|----------|----------|-------------------|
+All files | 26.83 | 0 | 35.71 | 26.83 | |
+ src | 0 | 100 | 0 | 0 | |
+ globalStyles.js | 0 | 100 | 0 | 0 |... 40,48,52,54,58 |
+ index.js | 0 | 100 | 100 | 0 | 6 |
+ src/components/App | 0 | 100 | 0 | 0 | |
+ App.component.jsx | 0 | 100 | 0 | 0 | 17,19 |
+ index.js | 0 | 0 | 0 | 0 | |
+ src/components/CardVideo | 100 | 100 | 100 | 100 | |
+ CardVideo.component.jsx | 100 | 100 | 100 | 100 | |
+ CardVideo.elements.jsx | 100 | 100 | 100 | 100 | |
+ index.js | 0 | 0 | 0 | 0 | |
+ src/components/Fortune | 0 | 100 | 0 | 0 | |
+ Fortune.component.jsx | 0 | 100 | 0 | 0 | 7,9 |
+ index.js | 0 | 0 | 0 | 0 | |
+ src/components/Layout | 0 | 100 | 0 | 0 | |
+ Layout.component.jsx | 0 | 100 | 0 | 0 | 6 |
+ index.js | 0 | 0 | 0 | 0 | |
+ src/components/Navbar | 100 | 100 | 100 | 100 | |
+ Navbar.component.jsx | 100 | 100 | 100 | 100 | |
+ Navbar.elements.jsx | 100 | 100 | 100 | 100 | |
+ index.js | 0 | 0 | 0 | 0 | |
+ src/components/Private | 0 | 0 | 0 | 0 | |
+ Private.component.jsx | 0 | 0 | 0 | 0 | 7,9,10 |
+ index.js | 0 | 0 | 0 | 0 | |
+ src/components/SideMenu | 0 | 0 | 0 | 0 | |
+ SideMenu.component.jsx | 0 | 0 | 0 | 0 | 8,9,11,13 |
+ SideMenu.elements.jsx | 0 | 100 | 0 | 0 |... 13,16,17,29,34 |
+ index.js | 0 | 0 | 0 | 0 | |
+ src/pages/Home | 0 | 100 | 0 | 0 | |
+ Home.page.jsx | 0 | 100 | 0 | 0 |... 17,18,20,24,27 |
+ index.js | 0 | 0 | 0 | 0 | |
+ src/pages/Login | 0 | 100 | 0 | 0 | |
+ Login.page.jsx | 0 | 100 | 0 | 0 | 8,9,12,13,14,17 |
+ index.js | 0 | 0 | 0 | 0 | |
+ src/pages/NotFound | 0 | 100 | 0 | 0 | |
+ NotFound.page.jsx | 0 | 100 | 0 | 0 | 7 |
+ index.js | 0 | 0 | 0 | 0 | |
+ src/pages/Secret | 0 | 100 | 0 | 0 | |
+ Secret.page.jsx | 0 | 100 | 0 | 0 | 5 |
+ index.js | 0 | 0 | 0 | 0 | |
+ src/providers/Auth | 0 | 0 | 0 | 0 | |
+ Auth.provider.jsx | 0 | 0 | 0 | 0 |... 28,31,32,33,36 |
+ index.js | 0 | 0 | 0 | 0 | |
+ src/utils | 0 | 100 | 0 | 0 | |
+ constants.js | 0 | 100 | 100 | 0 | 1 |
+ fns.js | 0 | 100 | 0 | 0 | 2 |
+ storage.js | 0 | 100 | 0 | 0 | 1,3,4,5,7,8,13 |
+ src/utils/hooks | 0 | 100 | 0 | 0 | |
+ useFortune.js | 0 | 100 | 0 | 0 |... 17,19,21,25,28 |
+--------------------------|----------|----------|----------|----------|-------------------|
+
+Test Suites: 2 passed, 2 total
+Tests: 6 passed, 6 total
+Snapshots: 0 total
+Time: 4.964s
+Ran all test suites.
+Done in 6.05s.
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index 0530c750a..fb28ba1df 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -16,6 +16,13 @@
dependencies:
"@babel/highlight" "^7.10.4"
+"@babel/code-frame@^7.14.5":
+ version "7.14.5"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb"
+ integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==
+ dependencies:
+ "@babel/highlight" "^7.14.5"
+
"@babel/compat-data@^7.10.4", "@babel/compat-data@^7.11.0", "@babel/compat-data@^7.9.0":
version "7.11.0"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.11.0.tgz#e9f73efe09af1355b723a7f39b11bad637d7c99c"
@@ -78,6 +85,22 @@
jsesc "^2.5.1"
source-map "^0.6.1"
+"@babel/generator@^7.14.8":
+ version "7.14.8"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.8.tgz#bf86fd6af96cf3b74395a8ca409515f89423e070"
+ integrity sha512-cYDUpvIzhBVnMzRoY1fkSEhK/HmwEVwlyULYgn/tMQYd6Obag3ylCjONle3gdErfXBW61SVTlR9QR7uWlgeIkg==
+ dependencies:
+ "@babel/types" "^7.14.8"
+ jsesc "^2.5.1"
+ source-map "^0.5.0"
+
+"@babel/helper-annotate-as-pure@^7.0.0":
+ version "7.14.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.14.5.tgz#7bf478ec3b71726d56a8ca5775b046fc29879e61"
+ integrity sha512-EivH9EgBIb+G8ij1B2jAwSH36WnGvkQSEC6CkX/6v6ZFlw5fVOHvsgGF4uiEHO2GzMvunZb6tDLQEQSdrdocrA==
+ dependencies:
+ "@babel/types" "^7.14.5"
+
"@babel/helper-annotate-as-pure@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz#5bf0d495a3f757ac3bda48b5bf3b3ba309c72ba3"
@@ -167,6 +190,15 @@
"@babel/template" "^7.10.4"
"@babel/types" "^7.10.4"
+"@babel/helper-function-name@^7.14.5":
+ version "7.14.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz#89e2c474972f15d8e233b52ee8c480e2cfcd50c4"
+ integrity sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==
+ dependencies:
+ "@babel/helper-get-function-arity" "^7.14.5"
+ "@babel/template" "^7.14.5"
+ "@babel/types" "^7.14.5"
+
"@babel/helper-get-function-arity@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2"
@@ -174,6 +206,13 @@
dependencies:
"@babel/types" "^7.10.4"
+"@babel/helper-get-function-arity@^7.14.5":
+ version "7.14.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz#25fbfa579b0937eee1f3b805ece4ce398c431815"
+ integrity sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==
+ dependencies:
+ "@babel/types" "^7.14.5"
+
"@babel/helper-hoist-variables@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz#d49b001d1d5a68ca5e6604dda01a6297f7c9381e"
@@ -181,6 +220,13 @@
dependencies:
"@babel/types" "^7.10.4"
+"@babel/helper-hoist-variables@^7.14.5":
+ version "7.14.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz#e0dd27c33a78e577d7c8884916a3e7ef1f7c7f8d"
+ integrity sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==
+ dependencies:
+ "@babel/types" "^7.14.5"
+
"@babel/helper-member-expression-to-functions@^7.10.4", "@babel/helper-member-expression-to-functions@^7.10.5":
version "7.11.0"
resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz#ae69c83d84ee82f4b42f96e2a09410935a8f26df"
@@ -188,6 +234,13 @@
dependencies:
"@babel/types" "^7.11.0"
+"@babel/helper-module-imports@^7.0.0":
+ version "7.14.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz#6d1a44df6a38c957aa7c312da076429f11b422f3"
+ integrity sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ==
+ dependencies:
+ "@babel/types" "^7.14.5"
+
"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.8.3":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620"
@@ -269,11 +322,23 @@
dependencies:
"@babel/types" "^7.11.0"
+"@babel/helper-split-export-declaration@^7.14.5":
+ version "7.14.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz#22b23a54ef51c2b7605d851930c1976dd0bc693a"
+ integrity sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==
+ dependencies:
+ "@babel/types" "^7.14.5"
+
"@babel/helper-validator-identifier@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2"
integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==
+"@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.8":
+ version "7.14.8"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz#32be33a756f29e278a0d644fa08a2c9e0f88a34c"
+ integrity sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow==
+
"@babel/helper-wrap-function@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz#8a6f701eab0ff39f765b5a1cfef409990e624b87"
@@ -302,11 +367,25 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
+"@babel/highlight@^7.14.5":
+ version "7.14.5"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9"
+ integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.14.5"
+ chalk "^2.0.0"
+ js-tokens "^4.0.0"
+
"@babel/parser@^7.1.0", "@babel/parser@^7.10.4", "@babel/parser@^7.11.5", "@babel/parser@^7.4.3", "@babel/parser@^7.7.0", "@babel/parser@^7.9.0":
version "7.11.5"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037"
integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==
+"@babel/parser@^7.14.5", "@babel/parser@^7.14.8":
+ version "7.14.8"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.8.tgz#66fd41666b2d7b840bd5ace7f7416d5ac60208d4"
+ integrity sha512-syoCQFOoo/fzkWDeM0dLEZi5xqurb5vuyzwIMNZRNun+N/9A4cUZeQaE7dTrB8jGaKuJRBtEOajtnmw0I5hvvA==
+
"@babel/plugin-proposal-async-generator-functions@^7.10.4", "@babel/plugin-proposal-async-generator-functions@^7.8.3":
version "7.10.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz#3491cabf2f7c179ab820606cec27fed15e0e8558"
@@ -1124,6 +1203,15 @@
"@babel/parser" "^7.10.4"
"@babel/types" "^7.10.4"
+"@babel/template@^7.14.5":
+ version "7.14.5"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4"
+ integrity sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==
+ dependencies:
+ "@babel/code-frame" "^7.14.5"
+ "@babel/parser" "^7.14.5"
+ "@babel/types" "^7.14.5"
+
"@babel/traverse@^7.1.0", "@babel/traverse@^7.10.4", "@babel/traverse@^7.11.5", "@babel/traverse@^7.4.3", "@babel/traverse@^7.7.0", "@babel/traverse@^7.9.0":
version "7.11.5"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3"
@@ -1139,6 +1227,21 @@
globals "^11.1.0"
lodash "^4.17.19"
+"@babel/traverse@^7.4.5":
+ version "7.14.8"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.8.tgz#c0253f02677c5de1a8ff9df6b0aacbec7da1a8ce"
+ integrity sha512-kexHhzCljJcFNn1KYAQ6A5wxMRzq9ebYpEDV4+WdNyr3i7O44tanbDOR/xjiG2F3sllan+LgwK+7OMk0EmydHg==
+ dependencies:
+ "@babel/code-frame" "^7.14.5"
+ "@babel/generator" "^7.14.8"
+ "@babel/helper-function-name" "^7.14.5"
+ "@babel/helper-hoist-variables" "^7.14.5"
+ "@babel/helper-split-export-declaration" "^7.14.5"
+ "@babel/parser" "^7.14.8"
+ "@babel/types" "^7.14.8"
+ debug "^4.1.0"
+ globals "^11.1.0"
+
"@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.11.5", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.9.0":
version "7.11.5"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d"
@@ -1148,6 +1251,14 @@
lodash "^4.17.19"
to-fast-properties "^2.0.0"
+"@babel/types@^7.14.5", "@babel/types@^7.14.8":
+ version "7.14.8"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.8.tgz#38109de8fcadc06415fbd9b74df0065d4d41c728"
+ integrity sha512-iob4soQa7dZw8nodR/KlOQkPh9S4I8RwCxwRIFuiMRYjOzH/KJzdUfDgz6cGi5dDaclXF4P2PAhCdrBJNIg68Q==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.14.8"
+ to-fast-properties "^2.0.0"
+
"@cnakazawa/watch@^1.0.3":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a"
@@ -1166,6 +1277,28 @@
resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-10.1.0.tgz#f0950bba18819512d42f7197e56c518aa491cf18"
integrity sha512-ij4wRiunFfaJxjB0BdrYHIH8FxBJpOwNPhhAcunlmPdXudL1WQV1qoP9un6JsEBAgQH+7UXyyjh0g7jTxXK6tg==
+"@emotion/is-prop-valid@^0.8.8":
+ version "0.8.8"
+ resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a"
+ integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==
+ dependencies:
+ "@emotion/memoize" "0.7.4"
+
+"@emotion/memoize@0.7.4":
+ version "0.7.4"
+ resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb"
+ integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==
+
+"@emotion/stylis@^0.8.4":
+ version "0.8.5"
+ resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04"
+ integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==
+
+"@emotion/unitless@^0.7.4":
+ version "0.7.5"
+ resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
+ integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
+
"@hapi/address@2.x.x":
version "2.1.4"
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
@@ -2390,6 +2523,21 @@ babel-plugin-named-asset-import@^0.3.6:
resolved "https://registry.yarnpkg.com/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.6.tgz#c9750a1b38d85112c9e166bf3ef7c5dbc605f4be"
integrity sha512-1aGDUfL1qOOIoqk9QKGIo2lANk+C7ko/fqH0uIyC71x3PEGz0uVP8ISgfEsFuG+FKmjHTvFK/nNM8dowpmUxLA==
+"babel-plugin-styled-components@>= 1.12.0":
+ version "1.13.2"
+ resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-1.13.2.tgz#ebe0e6deff51d7f93fceda1819e9b96aeb88278d"
+ integrity sha512-Vb1R3d4g+MUfPQPVDMCGjm3cDocJEUTR7Xq7QS95JWWeksN1wdFRYpD2kulDgI3Huuaf1CZd+NK4KQmqUFh5dA==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.0.0"
+ "@babel/helper-module-imports" "^7.0.0"
+ babel-plugin-syntax-jsx "^6.18.0"
+ lodash "^4.17.11"
+
+babel-plugin-syntax-jsx@^6.18.0:
+ version "6.18.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946"
+ integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=
+
babel-plugin-syntax-object-rest-spread@^6.8.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5"
@@ -2837,6 +2985,11 @@ camelcase@5.3.1, camelcase@^5.0.0, camelcase@^5.3.1:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
+camelize@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b"
+ integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=
+
caniuse-api@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0"
@@ -3416,6 +3569,11 @@ css-blank-pseudo@^0.1.4:
dependencies:
postcss "^7.0.5"
+css-color-keywords@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05"
+ integrity sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=
+
css-color-names@0.0.4, css-color-names@^0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
@@ -3487,6 +3645,15 @@ css-select@^2.0.0:
domutils "^1.7.0"
nth-check "^1.0.2"
+css-to-react-native@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-3.0.0.tgz#62dbe678072a824a689bcfee011fc96e02a7d756"
+ integrity sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==
+ dependencies:
+ camelize "^1.0.0"
+ css-color-keywords "^1.0.0"
+ postcss-value-parser "^4.0.2"
+
css-tree@1.0.0-alpha.37:
version "1.0.0-alpha.37"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22"
@@ -5348,7 +5515,7 @@ hmac-drbg@^1.0.0:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
-hoist-non-react-statics@^3.1.0:
+hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
@@ -9113,6 +9280,11 @@ react-error-overlay@^6.0.7:
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108"
integrity sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA==
+react-icons@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.2.0.tgz#6dda80c8a8f338ff96a1851424d63083282630d0"
+ integrity sha512-rmzEDFt+AVXRzD7zDE21gcxyBizD/3NqjbX6cmViAgdqfJ2UiLer8927/QhhrXQV7dEj/1EGuOTPp7JnLYVJKQ==
+
react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
@@ -9868,6 +10040,11 @@ shallow-clone@^3.0.0:
dependencies:
kind-of "^6.0.2"
+shallowequal@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8"
+ integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==
+
shebang-command@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
@@ -10391,6 +10568,22 @@ style-loader@0.23.1:
loader-utils "^1.1.0"
schema-utils "^1.0.0"
+styled-components@^5, styled-components@^5.3.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.3.0.tgz#e47c3d3e9ddfff539f118a3dd0fd4f8f4fb25727"
+ integrity sha512-bPJKwZCHjJPf/hwTJl6TbkSZg/3evha+XPEizrZUGb535jLImwDUdjTNxXqjjaASt2M4qO4AVfoHJNe3XB/tpQ==
+ dependencies:
+ "@babel/helper-module-imports" "^7.0.0"
+ "@babel/traverse" "^7.4.5"
+ "@emotion/is-prop-valid" "^0.8.8"
+ "@emotion/stylis" "^0.8.4"
+ "@emotion/unitless" "^0.7.4"
+ babel-plugin-styled-components ">= 1.12.0"
+ css-to-react-native "^3.0.0"
+ hoist-non-react-statics "^3.0.0"
+ shallowequal "^1.1.0"
+ supports-color "^5.5.0"
+
stylehacks@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5"
@@ -10405,7 +10598,7 @@ supports-color@^2.0.0:
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
-supports-color@^5.3.0:
+supports-color@^5.3.0, supports-color@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==