Skip to content

Commit c8fff2a

Browse files
Andrés Campos Hernándezcylcrow
authored andcommitted
✨ ♻️ 🚀 Added last features
- Context value passed to hide/show favorite buttons - Added routes for logged user
1 parent ffa05de commit c8fff2a

19 files changed

Lines changed: 296 additions & 166 deletions

‎src/components/App/App.jsx‎

Lines changed: 7 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,23 @@
1-
import React, { useReducer } from 'react';
2-
import { appReducer } from '../../reducers/appReducer';
3-
import { lightTheme, darkTheme } from '../../providers/themes';
4-
import youtubeSearchService from '../../services/youtubeSearchService';
5-
import AppContext from '../../providers/AppContext';
1+
import React from 'react';
2+
import AppWrapper from './AppWrapper';
63
import { createBrowserHistory } from 'history';
7-
import { Router } from 'react-router-dom';
8-
import { LayoutWrapper } from '../Layout';
9-
import {
10-
SET_IS_FIRST_LOAD,
11-
SET_SESSION,
12-
SET_VIDEOS_LIST,
13-
SWITCH_THEME
14-
} from '../../reducers/actionTypes';
4+
import LayoutWrapper from '../Layout/LayoutWrapper';
155
import Content from './Content';
16-
17-
const USER_SESSION_KEY = "user_session";
18-
const initialState = {
19-
videosList: [],
20-
currentVideoId: undefined,
21-
isLightTheme: true,
22-
isFirstLoad: true,
23-
userSession: JSON.parse(window.sessionStorage.getItem(USER_SESSION_KEY)),
24-
};
6+
import { Router } from 'react-router';
257

268
const history = createBrowserHistory();
279

28-
function App() {
29-
const [{
30-
videosList,
31-
isLightTheme,
32-
isFirstLoad,
33-
userSession},
34-
dispatch] = useReducer(appReducer, initialState);
35-
36-
const getThemeConfig = () => ({
37-
theme: isLightTheme ? lightTheme : darkTheme,
38-
switchTheme: () => dispatch({type: SWITCH_THEME}),
39-
});
40-
41-
const search = async (query) => {
42-
const serviceResponse = await youtubeSearchService.search(query);
43-
serviceResponse.error
44-
? dispatch({ type: SET_VIDEOS_LIST, payload: [] })
45-
: dispatch({ type: SET_VIDEOS_LIST, payload: serviceResponse });
46-
}
47-
48-
const firstSearch = async () => {
49-
let counter = 0;
50-
/* global gapi */
51-
/* eslint no-undef: "error" */
52-
while((!gapi || !gapi.client) && counter < 10){
53-
counter+=1;
54-
await new Promise(resolve => setTimeout(resolve, 100));
55-
}
56-
((!gapi || !gapi.client) || counter >= 10)
57-
? dispatch({ type: SET_VIDEOS_LIST, payload: [] })
58-
: search()
59-
}
60-
61-
if(isFirstLoad){
62-
dispatch({ type: SET_IS_FIRST_LOAD, payload: false });
63-
firstSearch();
64-
}
65-
10+
const App = () =>{
6611
return (
67-
<AppContext.Provider value={
68-
{ ...getThemeConfig(),
69-
search,
70-
videosList,
71-
userSession,
72-
setUserSession: (data) => dispatch({type: SET_SESSION, payload: data}),
73-
}
74-
}>
12+
<AppWrapper>
7513
<Router history={history}>
7614
{
7715
<LayoutWrapper>
7816
<Content />
7917
</LayoutWrapper>
8018
}
8119
</Router>
82-
</AppContext.Provider>
20+
</AppWrapper>
8321
);
8422
}
8523

‎src/components/App/AppWrapper.jsx‎

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import React, { useReducer } from 'react';
2+
import { appReducer } from '../../reducers/appReducer';
3+
import { lightTheme, darkTheme } from '../../providers/themes';
4+
import youtubeSearchService from '../../services/youtubeSearchService';
5+
import AppContext from '../../providers/AppContext';
6+
import {
7+
SET_IS_FIRST_LOAD,
8+
SET_SESSION,
9+
SET_VIDEOS_LIST,
10+
SWITCH_THEME
11+
} from '../../reducers/actionTypes';
12+
13+
const USER_SESSION_KEY = "user_session";
14+
const initialState = {
15+
videosList: [],
16+
currentVideoId: undefined,
17+
isLightTheme: true,
18+
isFirstLoad: true,
19+
userSession: JSON.parse(window.sessionStorage.getItem(USER_SESSION_KEY)),
20+
};
21+
22+
function AppWrapper(props) {
23+
const [{
24+
videosList,
25+
isLightTheme,
26+
isFirstLoad,
27+
userSession},
28+
dispatch] = useReducer(appReducer, initialState);
29+
30+
const getThemeConfig = () => ({
31+
theme: isLightTheme ? lightTheme : darkTheme,
32+
switchTheme: () => dispatch({type: SWITCH_THEME}),
33+
});
34+
35+
const search = async (query) => {
36+
const serviceResponse = await youtubeSearchService.search(query);
37+
serviceResponse.error
38+
? dispatch({ type: SET_VIDEOS_LIST, payload: [] })
39+
: dispatch({ type: SET_VIDEOS_LIST, payload: serviceResponse });
40+
}
41+
42+
const firstSearch = async () => {
43+
let counter = 0;
44+
/* global gapi */
45+
/* eslint no-undef: "error" */
46+
while((!gapi || !gapi.client) && counter < 10){
47+
counter+=1;
48+
await new Promise(resolve => setTimeout(resolve, 100));
49+
}
50+
((!gapi || !gapi.client) || counter >= 10)
51+
? dispatch({ type: SET_VIDEOS_LIST, payload: [] })
52+
: search()
53+
}
54+
55+
if(isFirstLoad){
56+
dispatch({ type: SET_IS_FIRST_LOAD, payload: false });
57+
firstSearch();
58+
}
59+
60+
return (
61+
<AppContext.Provider value={
62+
{ ...getThemeConfig(),
63+
search,
64+
videosList,
65+
userSession,
66+
setUserSession: (data) => dispatch({type: SET_SESSION, payload: data}),
67+
}
68+
}>
69+
{ props.children }
70+
</AppContext.Provider>
71+
);
72+
}
73+
74+
export default AppWrapper;

‎src/components/GenericComponents/RelatedVideosList.jsx‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import React from 'react';
1+
import React, { useContext } from 'react';
22
import styled from 'styled-components';
3+
import PlayerContext from '../../providers/PlayerContext';
34
import SmallVideoCard from './SmallVideoCard';
45

56
const StyledVideoList = styled.div`
@@ -8,7 +9,8 @@ const StyledVideoList = styled.div`
89
width: 40%;
910
`;
1011

11-
const RelatedVideosList = ({ videosList, onCaptionClick, addRemoveFavorite }) => {
12+
const RelatedVideosList = ({ videosList, onCaptionClick }) => {
13+
const { hideFavoriteButtons } = useContext(PlayerContext);
1214
return (
1315
<StyledVideoList id="related-videos-list">
1416
{videosList.map((video) => {
@@ -17,8 +19,8 @@ const RelatedVideosList = ({ videosList, onCaptionClick, addRemoveFavorite }) =>
1719
<div data-testid={`small-caption-${key}`} key={key}>
1820
<SmallVideoCard
1921
video={video}
22+
hideFavoriteButtons={hideFavoriteButtons}
2023
onClick={() => onCaptionClick && onCaptionClick(video)}
21-
onFavorite={(video, callback) => addRemoveFavorite(video, callback)}
2224
/>
2325
</div>
2426
);

‎src/components/GenericComponents/RelatedVideosList.spec.jsx‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,20 @@ import { lightTheme } from '../../providers/themes';
55
import { contextWrapper, youtubeMockedData, YTMockedObject } from '../../utils/'
66
import AppContext from '../../providers/AppContext';
77
import RelatedVideosList from './RelatedVideosList';
8-
import { getAllByTestId, getByTestId } from '@testing-library/dom';
8+
import { getAllByTestId } from '@testing-library/dom';
9+
import RelatedVideosContext from '../../providers/RelatedVideosContext';
10+
import PlayerContext from '../../providers/PlayerContext';
911

1012
global.YT = YTMockedObject;
1113

1214
const build = async (Component = <RelatedVideosList />) => {
13-
const contextValue = { theme: lightTheme };
15+
const appContextValue = { theme: lightTheme };
16+
const playerContextValue = { hideFavoriteButtons: false };
17+
const relatedVideosContextValue = { favoritesList: [], addRemoveFavorite: jest.fn() };
1418
let container;
15-
16-
const wrappedContext = contextWrapper(AppContext, contextValue, Component);
19+
let wrappedContext = contextWrapper(AppContext, appContextValue, Component);
20+
wrappedContext = contextWrapper(PlayerContext, playerContextValue, wrappedContext);
21+
wrappedContext = contextWrapper(RelatedVideosContext, relatedVideosContextValue, wrappedContext);
1722
container = render(wrappedContext).container;
1823

1924
return {

‎src/components/GenericComponents/RelatedVideosList.test.jsx‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,20 @@ import { lightTheme } from '../../providers/themes';
55
import { contextWrapper, routerWrapper, youtubeMockedData, YTMockedObject } from '../../utils/'
66
import AppContext from '../../providers/AppContext';
77
import RelatedVideosList from './RelatedVideosList';
8+
import RelatedVideosContext from '../../providers/RelatedVideosContext';
9+
import PlayerContext from '../../providers/PlayerContext';
810

911
global.YT = YTMockedObject;
1012

1113
const build = async (Component = <RelatedVideosList />) => {
12-
const contextValue = { theme: lightTheme };
14+
const appContextValue = { theme: lightTheme };
15+
const playerContextValue = { hideFavoriteButtons: false };
16+
const relatedVideosContextValue = { favoritesList: [], addRemoveFavorite: jest.fn() };
1317
let container;
1418
await act(async () => {
15-
const wrappedContext = contextWrapper(AppContext, contextValue, Component);
19+
let wrappedContext = contextWrapper(AppContext, appContextValue, Component);
20+
wrappedContext = contextWrapper(PlayerContext, playerContextValue, wrappedContext);
21+
wrappedContext = contextWrapper(RelatedVideosContext, relatedVideosContextValue, wrappedContext);
1622
const routeWrap = await routerWrapper(wrappedContext);
1723
container = render(routeWrap.wrap).container;
1824
})

‎src/components/GenericComponents/SmallVideoCard.jsx‎

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import React, { useContext, useState } from 'react';
1+
import React, { useContext } from 'react';
22
import styled from 'styled-components';
33
import AppContext from '../../providers/AppContext';
4+
import RelatedVideosContext from '../../providers/RelatedVideosContext';
45
import { Button } from '../../ui'
56

67
const StyledSmallVideoCard = styled.div`
@@ -27,21 +28,12 @@ const StyledCaption = styled.div`
2728
padding: 4px;
2829
`;
2930

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

33-
const SmallVideoCard = ({ video, onClick, onFavorite }) => {
34-
35-
const getLabel = (videoId) =>
36-
getParsedFavorites() && getParsedFavorites()[videoId]
37-
? "Remove favorite"
38-
: "Add favorite";
33+
const { favoritesList, addRemoveFavorite } = useContext(RelatedVideosContext);
3934

4035
const { theme } = useContext(AppContext);
4136
const { thumbnails, title, description } = video.snippet;
42-
const [buttonLabel, setButtonLabel] = useState(getLabel(video.id.videoId));
43-
44-
const updateLabel = () => { setButtonLabel(getLabel(video.id.videoId)); }
4537

4638
return (
4739
<StyledSmallVideoCard
@@ -51,12 +43,19 @@ const SmallVideoCard = ({ video, onClick, onFavorite }) => {
5143
<img src={thumbnails.default.url} alt={title} />
5244
<StyledCaption>
5345
<StyledSmallVideoCardDescription>{description}</StyledSmallVideoCardDescription>
54-
<Button
55-
data-testid="caption-add-favorite"
56-
onClick={() => { onFavorite && onFavorite(video, updateLabel) }}
57-
>
58-
{buttonLabel}
59-
</Button>
46+
{
47+
hideFavoriteButtons?
48+
<></>:
49+
<Button
50+
data-testid="caption-add-favorite"
51+
onClick={ () => addRemoveFavorite(video) }
52+
>
53+
{
54+
favoritesList && favoritesList.includes(video.id.videoId)
55+
? 'Remove favorite'
56+
: 'Add favorite'
57+
}
58+
</Button>}
6059
</StyledCaption>
6160
</StyledSmallVideoCard>
6261
);

‎src/components/GenericComponents/SmallVideoCard.spec.jsx‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ import { lightTheme } from '../../providers/themes'
77
import { contextWrapper } from '../../utils'
88
import AppContext from '../../providers/AppContext'
99
import SmallVideoCard from './SmallVideoCard';
10+
import RelatedVideosContext from '../../providers/RelatedVideosContext';
1011

1112
const build = (Component = <SmallVideoCard />, theme = lightTheme) => {
12-
const contextValue = { theme };
13-
const wrapped = contextWrapper(AppContext, contextValue, Component);
14-
const { container } = render(wrapped);
13+
const appContextValue = { theme };
14+
const relatedVideosContextValue = { favoritesList: [], addRemoveFavorite: jest.fn() }
15+
let wrappedContext = contextWrapper(AppContext, appContextValue, Component);
16+
wrappedContext = contextWrapper(RelatedVideosContext, relatedVideosContextValue, wrappedContext);
17+
const { container } = render(wrappedContext);
1518
return { container };
1619
};
1720

‎src/components/GenericComponents/SmallVideoCard.test.jsx‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,18 @@ import 'jest-styled-components';
33
import { getByRole } from '@testing-library/dom';
44
import { render } from '@testing-library/react';
55
import { youtubeMockedData } from '../../utils';
6-
import { lightTheme, darkTheme } from '../../providers/themes'
7-
import { contextWrapper } from '../../utils'
8-
import AppContext from '../../providers/AppContext'
6+
import { lightTheme, darkTheme } from '../../providers/themes';
7+
import { contextWrapper } from '../../utils';
8+
import AppContext from '../../providers/AppContext';
9+
import RelatedVideosContext from '../../providers/RelatedVideosContext';
910
import SmallVideoCard from './SmallVideoCard';
1011

1112
const build = (Component = <SmallVideoCard />, theme = lightTheme) => {
12-
const contextValue = { theme };
13-
const wrapped = contextWrapper(AppContext, contextValue, Component);
14-
const { container } = render(wrapped);
13+
const appContextValue = { theme };
14+
const relatedVideosContextValue = { favoritesList: [] };
15+
let wrappedContext = contextWrapper(AppContext, appContextValue, Component);
16+
wrappedContext = contextWrapper(RelatedVideosContext, relatedVideosContextValue, wrappedContext);
17+
const { container } = render(wrappedContext);
1518
return { container };
1619
};
1720

0 commit comments

Comments
 (0)