-
Notifications
You must be signed in to change notification settings - Fork 0
PR for Video Library [FE] #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
95f6150
a335c92
7fb726d
51588c6
eb8596a
8e09213
9ec8c82
5537355
06768c6
529b22a
f30b905
15da314
94133ea
bcbb363
a8c93ec
840200d
913f3ae
77a1b8b
ba22d99
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| todos.md | ||
| node_modules/ | ||
| .prettierrc |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| { | ||
| "editor.formatOnSave": true, | ||
| "prettier.requireConfig": true | ||
| } |
Large diffs are not rendered by default.
| 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" | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| /* /index.html 200 |
| 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"; | ||
|
|
||
| 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Extract this to a
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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> | ||
| ); | ||
| } | ||
| 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" /> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
| <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> | ||
| ); | ||
| } | ||
| 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> | ||
| ); | ||
| }; |
| 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> | ||
| ); | ||
| } |
| 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> | ||
| ); | ||
| } |
| 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; | ||
| } |
| 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> | ||
| ); | ||
| } |
| 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"; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use consistent naming convention
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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> | ||
| ); | ||
| } | ||
There was a problem hiding this comment.
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.jsinside page folder and re-export all nested components inside pages foldere.g.