Skip to content

Commit

Permalink
✨ ♻️ 🚀 Added last features
Browse files Browse the repository at this point in the history
- Context value passed to hide/show favorite buttons
- Added routes for logged user
  • Loading branch information
Andrés Campos Hernández authored and cylcrow committed Mar 29, 2021
1 parent ffa05de commit f94b608
Show file tree
Hide file tree
Showing 16 changed files with 152 additions and 87 deletions.
8 changes: 5 additions & 3 deletions src/components/GenericComponents/RelatedVideosList.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import React, { useContext } from 'react';
import styled from 'styled-components';
import PlayerContext from '../../providers/PlayerContext';
import SmallVideoCard from './SmallVideoCard';

const StyledVideoList = styled.div`
Expand All @@ -8,7 +9,8 @@ const StyledVideoList = styled.div`
width: 40%;
`;

const RelatedVideosList = ({ videosList, onCaptionClick, addRemoveFavorite }) => {
const RelatedVideosList = ({ videosList, onCaptionClick }) => {
const { hideFavoriteButtons } = useContext(PlayerContext);
return (
<StyledVideoList id="related-videos-list">
{videosList.map((video) => {
Expand All @@ -17,8 +19,8 @@ const RelatedVideosList = ({ videosList, onCaptionClick, addRemoveFavorite }) =>
<div data-testid={`small-caption-${key}`} key={key}>
<SmallVideoCard
video={video}
hideFavoriteButtons={hideFavoriteButtons}
onClick={() => onCaptionClick && onCaptionClick(video)}
onFavorite={(video, callback) => addRemoveFavorite(video, callback)}
/>
</div>
);
Expand Down
13 changes: 9 additions & 4 deletions src/components/GenericComponents/RelatedVideosList.spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,20 @@ import { lightTheme } from '../../providers/themes';
import { contextWrapper, youtubeMockedData, YTMockedObject } from '../../utils/'
import AppContext from '../../providers/AppContext';
import RelatedVideosList from './RelatedVideosList';
import { getAllByTestId, getByTestId } from '@testing-library/dom';
import { getAllByTestId } from '@testing-library/dom';
import RelatedVideosContext from '../../providers/RelatedVideosContext';
import PlayerContext from '../../providers/PlayerContext';

global.YT = YTMockedObject;

const build = async (Component = <RelatedVideosList />) => {
const contextValue = { theme: lightTheme };
const appContextValue = { theme: lightTheme };
const playerContextValue = { hideFavoriteButtons: false };
const relatedVideosContextValue = { favoritesList: [], addRemoveFavorite: jest.fn() };
let container;

const wrappedContext = contextWrapper(AppContext, contextValue, Component);
let wrappedContext = contextWrapper(AppContext, appContextValue, Component);
wrappedContext = contextWrapper(PlayerContext, playerContextValue, wrappedContext);
wrappedContext = contextWrapper(RelatedVideosContext, relatedVideosContextValue, wrappedContext);
container = render(wrappedContext).container;

return {
Expand Down
10 changes: 8 additions & 2 deletions src/components/GenericComponents/RelatedVideosList.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,20 @@ import { lightTheme } from '../../providers/themes';
import { contextWrapper, routerWrapper, youtubeMockedData, YTMockedObject } from '../../utils/'
import AppContext from '../../providers/AppContext';
import RelatedVideosList from './RelatedVideosList';
import RelatedVideosContext from '../../providers/RelatedVideosContext';
import PlayerContext from '../../providers/PlayerContext';

global.YT = YTMockedObject;

const build = async (Component = <RelatedVideosList />) => {
const contextValue = { theme: lightTheme };
const appContextValue = { theme: lightTheme };
const playerContextValue = { hideFavoriteButtons: false };
const relatedVideosContextValue = { favoritesList: [], addRemoveFavorite: jest.fn() };
let container;
await act(async () => {
const wrappedContext = contextWrapper(AppContext, contextValue, Component);
let wrappedContext = contextWrapper(AppContext, appContextValue, Component);
wrappedContext = contextWrapper(PlayerContext, playerContextValue, wrappedContext);
wrappedContext = contextWrapper(RelatedVideosContext, relatedVideosContextValue, wrappedContext);
const routeWrap = await routerWrapper(wrappedContext);
container = render(routeWrap.wrap).container;
})
Expand Down
35 changes: 17 additions & 18 deletions src/components/GenericComponents/SmallVideoCard.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useContext, useState } from 'react';
import React, { useContext } from 'react';
import styled from 'styled-components';
import AppContext from '../../providers/AppContext';
import RelatedVideosContext from '../../providers/RelatedVideosContext';
import { Button } from '../../ui'

const StyledSmallVideoCard = styled.div`
Expand All @@ -27,21 +28,12 @@ const StyledCaption = styled.div`
padding: 4px;
`;

const FAVORITES_KEY = "favorites";
const getParsedFavorites = () => JSON.parse(window.localStorage.getItem(FAVORITES_KEY));
const SmallVideoCard = ({ video, onClick, hideFavoriteButtons }) => {

const SmallVideoCard = ({ video, onClick, onFavorite }) => {

const getLabel = (videoId) =>
getParsedFavorites() && getParsedFavorites()[videoId]
? "Remove favorite"
: "Add favorite";
const { favoritesList, addRemoveFavorite } = useContext(RelatedVideosContext);

const { theme } = useContext(AppContext);
const { thumbnails, title, description } = video.snippet;
const [buttonLabel, setButtonLabel] = useState(getLabel(video.id.videoId));

const updateLabel = () => { setButtonLabel(getLabel(video.id.videoId)); }

return (
<StyledSmallVideoCard
Expand All @@ -51,12 +43,19 @@ const SmallVideoCard = ({ video, onClick, onFavorite }) => {
<img src={thumbnails.default.url} alt={title} />
<StyledCaption>
<StyledSmallVideoCardDescription>{description}</StyledSmallVideoCardDescription>
<Button
data-testid="caption-add-favorite"
onClick={() => { onFavorite && onFavorite(video, updateLabel) }}
>
{buttonLabel}
</Button>
{
hideFavoriteButtons?
<></>:
<Button
data-testid="caption-add-favorite"
onClick={ () => addRemoveFavorite(video) }
>
{
favoritesList && favoritesList.includes(video.id.videoId)
? 'Remove favorite'
: 'Add favorite'
}
</Button>}
</StyledCaption>
</StyledSmallVideoCard>
);
Expand Down
9 changes: 6 additions & 3 deletions src/components/GenericComponents/SmallVideoCard.spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ import { lightTheme } from '../../providers/themes'
import { contextWrapper } from '../../utils'
import AppContext from '../../providers/AppContext'
import SmallVideoCard from './SmallVideoCard';
import RelatedVideosContext from '../../providers/RelatedVideosContext';

const build = (Component = <SmallVideoCard />, theme = lightTheme) => {
const contextValue = { theme };
const wrapped = contextWrapper(AppContext, contextValue, Component);
const { container } = render(wrapped);
const appContextValue = { theme };
const relatedVideosContextValue = { favoritesList: [], addRemoveFavorite: jest.fn() }
let wrappedContext = contextWrapper(AppContext, appContextValue, Component);
wrappedContext = contextWrapper(RelatedVideosContext, relatedVideosContextValue, wrappedContext);
const { container } = render(wrappedContext);
return { container };
};

Expand Down
15 changes: 9 additions & 6 deletions src/components/GenericComponents/SmallVideoCard.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ import 'jest-styled-components';
import { getByRole } from '@testing-library/dom';
import { render } from '@testing-library/react';
import { youtubeMockedData } from '../../utils';
import { lightTheme, darkTheme } from '../../providers/themes'
import { contextWrapper } from '../../utils'
import AppContext from '../../providers/AppContext'
import { lightTheme, darkTheme } from '../../providers/themes';
import { contextWrapper } from '../../utils';
import AppContext from '../../providers/AppContext';
import RelatedVideosContext from '../../providers/RelatedVideosContext';
import SmallVideoCard from './SmallVideoCard';

const build = (Component = <SmallVideoCard />, theme = lightTheme) => {
const contextValue = { theme };
const wrapped = contextWrapper(AppContext, contextValue, Component);
const { container } = render(wrapped);
const appContextValue = { theme };
const relatedVideosContextValue = { favoritesList: [] };
let wrappedContext = contextWrapper(AppContext, appContextValue, Component);
wrappedContext = contextWrapper(RelatedVideosContext, relatedVideosContextValue, wrappedContext);
const { container } = render(wrappedContext);
return { container };
};

Expand Down
74 changes: 51 additions & 23 deletions src/components/GenericComponents/VideoPlayerContainer.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { useEffect, useState } from 'react';
import React, { useContext, useEffect, useLayoutEffect, useState } from 'react';
import { useLocation } from 'react-router';
import styled from 'styled-components';
import RelatedVideosContext from '../../providers/RelatedVideosContext';
import PlayerContext from '../../providers/PlayerContext';
import { Button } from '../../ui';
import RelatedVideosList from './RelatedVideosList';

Expand All @@ -23,22 +25,31 @@ const StyledPlayerSection = styled.div`
`;

const VideoPlayerContainer = ({ videosList, onCaptionClick }) => {
const location = useLocation();
const { state, search } = location;
const videoId = new URLSearchParams(search).get("id");

const getFavoritesList = () => {
let parsedFavorites = getParsedFavorites();
return parsedFavorites ? Object.keys(parsedFavorites) : [];
};

const getLabel = (videoId) =>
getParsedFavorites() && getParsedFavorites()[videoId]
? "Remove favorite"
: "Add favorite";

const updateButtonLabel = () => { setFavoriteButtonLabel(() => getLabel(videoId)); }

const [favoriteButtonLabel, setFavoriteButtonLabel] = useState(getLabel());
const [favoritesList, setFavoritesList] = useState(getFavoritesList);
const { hideFavoriteButtons } = useContext(PlayerContext);
const location = useLocation();

const { state, search } = location;
const videoId = new URLSearchParams(search).get("id");

const updateButtonLabel = () => { setFavoriteButtonLabel(() => getLabel(videoId)); }

useEffect(() => updateButtonLabel());

useEffect(() => {
useLayoutEffect(() => {
/* global YT */
/* eslint no-undef: "error" */
YT.ready(() => {
Expand All @@ -60,7 +71,7 @@ const VideoPlayerContainer = ({ videosList, onCaptionClick }) => {
}
}, [videoId]);

const addRemoveFavorite = (video, videoId, callback) => {
const addRemoveFavorite = (video, videoId) => {
let favorites = getParsedFavorites();
if(!favorites){
favorites = {[videoId]: video};
Expand All @@ -71,32 +82,49 @@ const VideoPlayerContainer = ({ videosList, onCaptionClick }) => {
}
favorites = JSON.stringify(favorites);
window.localStorage.setItem(FAVORITES_KEY, favorites);
callback && callback();
}

return (
<StyledVideoPlayerContainer id="video-player-container">
<StyledPlayerSection>
<div id="player" />
<ControlsContainer>
<Button
data-testid="add-favorite"
margin="4px"
height="30px"
width="150px"
onClick={() => addRemoveFavorite(state, videoId, updateButtonLabel)}
>
{ favoriteButtonLabel }
</Button>
{
hideFavoriteButtons?
<></>:
<Button
data-testid="add-favorite"
margin="4px"
height="30px"
width="150px"
onClick={
() => {
addRemoveFavorite(state, videoId);
updateButtonLabel();
setFavoritesList(getFavoritesList());
}
}
>
{ favoriteButtonLabel }
</Button>
}
</ControlsContainer>
</StyledPlayerSection>
<RelatedVideosList
videosList={videosList}
onCaptionClick={onCaptionClick}
addRemoveFavorite={
(video, callback) => addRemoveFavorite(video, video.id.videoId, callback)
<RelatedVideosContext.Provider value={
{
favoritesList,
addRemoveFavorite: (video) => {
addRemoveFavorite(video, video.id.videoId);
setFavoritesList(getFavoritesList());
if(video.id.videoId === videoId) { updateButtonLabel(); }
}
}
/>
}>
<RelatedVideosList
videosList={videosList}
onCaptionClick={onCaptionClick}
/>
</RelatedVideosContext.Provider>
</StyledVideoPlayerContainer>
);
};
Expand Down
10 changes: 8 additions & 2 deletions src/components/GenericComponents/VideoPlayerContainer.spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { YTMockedObject, contextWrapper, youtubeMockedData, routerWrapper } from
import VideoPlayerContainer from './VideoPlayerContainer';
import { fireEvent, getByTestId } from '@testing-library/dom';
import AppContext from '../../providers/AppContext';
import PlayerContext from '../../providers/PlayerContext';
import RelatedVideosContext from '../../providers/RelatedVideosContext';


global.YT = YTMockedObject;
Expand All @@ -14,11 +16,15 @@ const EXPECTED_LENGHT = 3
const contextValueVideosList = youtubeMockedData.items.slice(0, EXPECTED_LENGHT);

const build = async ( Component = <VideoPlayerContainer videosList={contextValueVideosList}/>, video ) => {
const contextValue = { search: jest.fn, theme: lightTheme };
const appContextValue = { search: jest.fn, theme: lightTheme };
const playerContextValue = { hideFavoriteButtons: false };
const relatedVideosContextValue = { favoritesList: [], addRemoveFavorite: jest.fn() };
let container;
let routeWrap;
await act(async () => {
let contextWrap = contextWrapper(AppContext, contextValue, Component);
let contextWrap = contextWrapper(AppContext, appContextValue, Component);
contextWrap = contextWrapper(PlayerContext, playerContextValue, contextWrap);
contextWrap = contextWrapper(RelatedVideosContext, relatedVideosContextValue, contextWrap);
routeWrap = await routerWrapper(contextWrap);
container = render(routeWrap.wrap).container;
});
Expand Down
10 changes: 8 additions & 2 deletions src/components/GenericComponents/VideoPlayerContainer.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { act, getAllByTestId, render } from '@testing-library/react';
import { YTMockedObject, contextWrapper, youtubeMockedData, routerWrapper } from '../../utils';
import VideoPlayerContainer from './VideoPlayerContainer';
import AppContext from '../../providers/AppContext';
import RelatedVideosContext from '../../providers/RelatedVideosContext';
import PlayerContext from '../../providers/PlayerContext';

global.YT = YTMockedObject;
const EXPECTED_LENGHT = 3
Expand All @@ -13,11 +15,15 @@ const build = async (
Component = <VideoPlayerContainer videosList={contextValueVideosList}/>,
currentVideoId = contextValueVideosList[0].id.videoId
) => {
const contextValue = { search: jest.fn, currentVideoId, theme: lightTheme };
const appContextValue = { search: jest.fn, currentVideoId, theme: lightTheme };
const playerContextValue = { hideFavoritesButtons: false };
const relatedVideosContextValue = { favoritesList: [], addRemoveFavorite: jest.fn() };
let container;
let routeWrap;
await act(async () => {
let contextWrap = contextWrapper(AppContext, contextValue, Component);
let contextWrap = contextWrapper(AppContext, appContextValue, Component);
contextWrap = contextWrapper(PlayerContext, playerContextValue, contextWrap);
contextWrap = contextWrapper(RelatedVideosContext, relatedVideosContextValue, contextWrap);
routeWrap = await routerWrapper(contextWrap);
container = render(routeWrap.wrap).container;
});
Expand Down
24 changes: 14 additions & 10 deletions src/components/Home/HomePlayer.jsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import React, { useContext } from 'react';
import { useHistory } from 'react-router';
import AppContext from '../../providers/AppContext';
import PlayerContext from '../../providers/PlayerContext'
import VideoPlayerContainer from '../GenericComponents/VideoPlayerContainer';

const HomePlayer = () => {
const { push } = useHistory();
const { videosList } = useContext(AppContext);
return (<VideoPlayerContainer
videosList={videosList}
onCaptionClick={(video) =>{
push({
pathname: `/player`,
search: `?id=${video.id.videoId}`,
state: video
})
}}/>);
const { userSession, videosList } = useContext(AppContext);
return (<PlayerContext.Provider value={{ hideFavoriteButtons: !userSession || false }}>
<VideoPlayerContainer
videosList={videosList}
onCaptionClick={(video) =>{
push({
pathname: `/player`,
search: `?id=${video.id.videoId}`,
state: video
})
}}
/>
</PlayerContext.Provider>);
}

export default HomePlayer;
2 changes: 1 addition & 1 deletion src/components/Home/HomeVideos.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const HomeVideos = () => {

return (<VideoCardContainer
videosList={videosList}
noVideosNotice={NoVideosNotice}
noVideosNotice={NoVideosNotice}
onClick={
(video) => push({
pathname: `/player`,
Expand Down
Loading

0 comments on commit f94b608

Please sign in to comment.