Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
todos.md
node_modules/
.prettierrc
1 change: 1 addition & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"editor.formatOnSave": true,
"prettier.requireConfig": true
}
36,697 changes: 36,697 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

35 changes: 35 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "video-library",
"version": "1.0.0",
"description": "",
"keywords": [],
"main": "src/index.js",
"dependencies": {
"axios": "^0.21.1",
"faker": "5.5.2",
"history": "5",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-markdown": "^5.0.3",
"react-router-dom": "^6.0.0-beta.0",
"react-scripts": "4.0.0",
"react-toastify": "^7.0.4",
"react-uuid": "^1.0.2"
},
"devDependencies": {
"@babel/runtime": "7.13.8",
"typescript": "4.1.3"
},
"scripts": {
"start": "react-scripts --max_old_space_size=4096 start",
"build": "react-scripts --max_old_space_size=4096 build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}
1 change: 1 addition & 0 deletions public/_redirects
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* /index.html 200
Binary file added public/favicon.ico
Binary file not shown.
2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<link rel="shortcut icon" href="favicon.ico">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Expand Down
29 changes: 29 additions & 0 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import "./styles.css";
import { VideoListing } from "./pages/Videos/VideoListing";
import { Playlists } from "./pages/Playlists/Playlists";
import { PlaylistDetail } from "./pages/Playlists/PlaylistDetail";
import { Routes, Route } from "react-router-dom";
import { NavigationBar } from "./components/NavigationBar";
import { VideoDetail } from "./pages/Videos/VideoDetail";
import { PageNotFound } from "./pages/PageNotFound";
import { History } from "./pages/History/History";
import { SearchResults } from "./pages/SearchResults";
import { ToastContainer } from "react-toastify";
Comment on lines +2 to +11

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of doing separate imports for the same folder, you can use index.js inside page folder and re-export all nested components inside pages folder
e.g.

Suggested change
import { VideoListing } from "./pages/Videos/VideoListing";
import { Playlists } from "./pages/Playlists/Playlists";
import { PlaylistDetail } from "./pages/Playlists/PlaylistDetail";
import { Routes, Route } from "react-router-dom";
import { NavigationBar } from "./components/NavigationBar";
import { VideoDetail } from "./pages/Videos/VideoDetail";
import { PageNotFound } from "./pages/PageNotFound";
import { History } from "./pages/History/History";
import { SearchResults } from "./pages/SearchResults";
import { ToastContainer } from "react-toastify";
import { VideoListing, Playlists, PlaylistDetail,... } from "./pages";


export default function App() {
return (
<div>
<ToastContainer />
<NavigationBar />
<Routes>
<Route path="/" element={<VideoListing />} />
<Route path="/playlists" element={<Playlists />} />
<Route path="/playlists/:playlistId" element={<PlaylistDetail />} />
<Route path="/video/:videoId" element={<VideoDetail />} />
<Route path="/history" element={<History />} />
<Route path="/search" element={<SearchResults />} />s
<Route path="*" element={<PageNotFound />} />
</Routes>
Comment on lines +18 to +26

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extract this to a router.js file

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the number of routes is less, I think it's ok to keep them in this file.

</div>
);
}
39 changes: 39 additions & 0 deletions src/components/BaseCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Thumbnail } from "./Thumbnail";
import { useNavigate } from "react-router-dom";

export function BaseCard({ children, id, avatarSrc, ...rest }) {
const navigate = useNavigate();
const openVideo = (e) => {
navigate(`/video/${id}`);
e.stopPropagation();
};

return (
<div key={id} className="card card--shadow m-1">
<Thumbnail onClick={openVideo} id={id} className="clickable" />

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The onClick={openVideo} function should be on the upper div and not on Thumbnail, due to which clicking on an avatar or video title, the video is not opening up.

<div className="flex flex-no-wrap p-1">
<Avatar avatarSrc={avatarSrc} />
<CardDetails {...rest} />
</div>
{children}
</div>
);
}
export function Avatar({ avatarSrc }) {
return (
<div
className="card__avatar"
style={{
backgroundImage: `url('${avatarSrc}')`,
}}
/>
);
}
function CardDetails({ name, uploadedBy }) {
return (
<div>
<div className="card__title">{name}</div>
<div className="card__author">{uploadedBy}</div>
</div>
);
}
19 changes: 19 additions & 0 deletions src/components/CloseButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useToggleVideo } from "../custom hooks/useToggleVideo";
import { LoadingIndicator } from "./LoadingIndicator";

export const CloseButton = ({ video, playlistId }) => {
const { isLoading, toggleVideoInPlaylist } = useToggleVideo({
video,
playlistId,
});
return (
<button
className="btn-close btn--close--card btn-lg"
onClick={() => toggleVideoInPlaylist(true)}
>
<LoadingIndicator isLoading={isLoading} small>
<i className="fas fa-times" />
</LoadingIndicator>
</button>
);
};
18 changes: 18 additions & 0 deletions src/components/EditIcons.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export function EditIcons({ onSave, onCancel }) {
return (
<div>
<button
className="btn btn--icon btn--success playlist__icon"
onClick={onSave}
>
<i className="fas fa-check"></i>
</button>
<button
className="btn btn--icon btn--warning playlist__icon"
onClick={onCancel}
>
<i className="fas fa-times"></i>
</button>
</div>
);
}
14 changes: 14 additions & 0 deletions src/components/Iframe.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export function Iframe({ id }) {
return (
<div className="container--iframe">
<iframe
className="iframe"
src={`https://www.youtube.com/embed/${id}`}
title="YouTube"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
/>
</div>
);
}
12 changes: 12 additions & 0 deletions src/components/LoadingIndicator.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export function LoadingIndicator({ isLoading, children, small }) {
if (isLoading) {
return small ? (
<i className="fa fa-spinner fa-pulse" />
) : (
<span className="font--primary spinner--large">
<i className="fa fa-spinner fa-pulse m-2" />
</span>
);
}
return children;
}
14 changes: 14 additions & 0 deletions src/components/Modal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export function Modal({ children, showModal, onCloseClick }) {
return (
<div className={`modal-bg ${showModal ? "" : "modal-hide"}`}>
<div className={`modal ${showModal ? "" : "modal-hide"}`}>
<div className={`modal-content ${showModal ? "" : "modal-hide"}`}>
<button onClick={onCloseClick} className="btn-close btn-lg">
<i className="fas fa-times"></i>
</button>
{children}
</div>
</div>
</div>
);
}
122 changes: 122 additions & 0 deletions src/components/NavigationBar.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { useEffect, useState } from "react";
import { NavLink } from "react-router-dom";
import { usePlaylists } from "../pages/Playlists/playlists-context";
import { SET_VIDEOS } from "../pages/Videos/videos-reducer";
import { useAxios } from "../custom hooks/useAxios";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use consistent naming convention custom hooks rename to custom-hooks

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Will update this.

import { API_VIDEOS, API_PLAYLISTS } from "../urls";
import { useVideos } from "../pages/Videos/videos-context";
import {
SET_LIKED_VIDEOS_ID,
SET_PLAYLISTS,
SET_WATCH_LATER_ID,
} from "../pages/Playlists/playlists-reducer";
import { SearchBar } from "./SearchBar";

export function NavigationBar() {
const [expandNavbar, setExpandNavbar] = useState(false);
return (
<div className="navigation">
<header className="header">
<div className="container">
<Hamberger setExpandNavbar={setExpandNavbar} />
<Brand />
<Navigation expandNavbar={expandNavbar} />
</div>
</header>
</div>
);
}

function Navigation({ expandNavbar }) {
const { videosDispatch } = useVideos();
const { playlistsDispatch } = usePlaylists();
const { getData: getVideos } = useAxios(API_VIDEOS);
const { getData: getPlaylists } = useAxios(API_PLAYLISTS);

useEffect(() => {
(async () => {
const fetchedVideos = await getVideos();
if (fetchedVideos) {
videosDispatch({ type: SET_VIDEOS, fetchedVideos });
}
})();
(async () => {
const fetchedPlaylists = await getPlaylists();
if (fetchedPlaylists) {
setDefaultPlaylistIds(fetchedPlaylists);
playlistsDispatch({ type: SET_PLAYLISTS, fetchedPlaylists });
}
})();
}, []);

const setDefaultPlaylistIds = (playlists) => {
let isLikedVideosIdSet = false;
let isWatchLaterIdSet = false;
let i = 0;
while (
(!isLikedVideosIdSet || !isWatchLaterIdSet) &&
i < playlists.length
) {
Comment on lines +57 to +59

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could extract this logic out to function to improve readability

const { defaultPlaylist, _id, name } = playlists[i];
if (defaultPlaylist) {
if (name === "Liked Videos") {
isLikedVideosIdSet = true;
playlistsDispatch({ type: SET_LIKED_VIDEOS_ID, playlistId: _id });
} else {
isWatchLaterIdSet = true;
playlistsDispatch({ type: SET_WATCH_LATER_ID, playlistId: _id });
}
}
i++;
}
};

return (
<nav className={`nav ${expandNavbar ? "" : "nav-hide"}`}>
<div className="nav__search-bar">{<SearchBar />}</div>
<ul className="nav__list nav__list--primary">
<NavigationItem route="/">Home</NavigationItem>
<NavigationItem route="playlists">Playlists</NavigationItem>
<NavigationItem route="history">History</NavigationItem>
</ul>
</nav>
);
}

function NavigationItem({ route, children }) {
return (
<li className="nav__item pos-rel">
<NavLink
to={route}
end
className="nav__link"
activeClassName="nav__link--active"
>
{children}
</NavLink>
</li>
);
}

function Brand() {
return (
<NavLink to="/">
<img
className="img logo clickable"
src="https://purvasheth.github.io/Ceres-Component-Lib/images/logo.png"
alt="logo"
/>
</NavLink>
);
}

function Hamberger({ setExpandNavbar }) {
return (
<button
className="btn hamburger btn-wishlist font--primary"
onClick={() => setExpandNavbar((prev) => !prev)}
>
<i className="fa fa-bars"></i>
</button>
);
}
Loading