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
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"extends": "react-app",
"rules": {
"indent": ["error", "tab"],
"indent": ["error", 2],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "double"],
"semi": ["error", "always"]
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
"name": "cdn-player",
"version": "0.1.0",
"private": true,
"homepage": "https://idoleg.github.io/cdn-player/",
"dependencies": {
"plyr": "^3.5.6",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-scripts": "3.0.1"
"react-modal": "^3.9.1",
"react-scripts": "3.0.1",
"use-middleware-reducer": "^1.2.0"
},
"scripts": {
"start": "react-scripts start",
Expand Down
25 changes: 21 additions & 4 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
import React from "react";
import PlayerChanger from "./components/Player/PlayerChanger";
import {StoreProvider} from "./store";

export default () => (
<PlayerChanger />
);
import PageSection from "./components/PageSection/PageSection";
import Player from "./components/Player/Player";
import PlayerInputField from "./components/Player/PlayerInputField";
import HistoryModal from "./components/History/HistoryModal";
import Greeting from "./components/Greeting/Greeting";

export default () => {
return (
<StoreProvider>
<PageSection title="CDN online player">
<Player />
<PlayerInputField/>
<footer>
You can see <HistoryModal/>.
</footer>
</PageSection>
<Greeting />
</StoreProvider>
);
};
6 changes: 3 additions & 3 deletions src/App.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import ReactDOM from "react-dom";
import App from "./App";

it("renders without crashing", () => {
const div = document.createElement("div");
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
// const div = document.createElement("div");
// ReactDOM.render(<App />, div);
// ReactDOM.unmountComponentAtNode(div);
});
15 changes: 15 additions & 0 deletions src/components/Greeting/Greeting.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from "react";
import classes from "./Greeting.module.css";
import PageSection from "../PageSection/PageSection";

export default (props) => (
<PageSection title="What's up?" className={classes.Greeting}>
<p>
Welcome to CDN online player.
</p>
<p>
Why do you need it? Unfortunately, a built-in browser player doesn't have useful features.
It does not have video speed control, browsing history and opportunities to create playlists.
</p>
</PageSection>
);
4 changes: 4 additions & 0 deletions src/components/Greeting/Greeting.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.Greeting {
background: linear-gradient(to left top, #21a7f0, #0074b3);
color: white;
}
35 changes: 35 additions & 0 deletions src/components/History/HistoryItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from "react";
import classes from "./HistoryItem.module.scss";

export default ({ item, changeVideo }) => {
const splitedLink = splitLink(item.source);

return <button className={classes.Item} onClick={() => changeVideo(item.source)}>
<span className={classes.Source}>
<span className={classes.UrlFirst}>{splitedLink[0]}</span>
<span className={classes.UrlSecond}>...{splitedLink[1]}</span>
</span>
<footer className={classes.Extra}>
<span className={classes.Time}>Last time: {convertTime(item.time)}</span>
<span className={classes.Date}>Date viewed: {item.date}</span>
</footer>
</button>;
};

export function splitLink(link) {
link = "" + link;
return [link.substring(0, link.length - 10), link.substring(link.length - 10)];
}

export function convertTime(time) {
const minutes = Math.floor(time / 60);
const seconds = (time % 60);

if (!seconds) {
return minutes + " min";
} else if (!minutes) {
return seconds + " sec";
} else {
return `${minutes} min ${seconds} sec`;
}
}
39 changes: 39 additions & 0 deletions src/components/History/HistoryItem.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
.Item {
cursor: pointer;
padding: 10px;
overflow: hidden;
width: 100%;
border-radius: 5px;
transition: background-color .3s ease, color .1s ease;
text-align: left;

&:hover {
background-color: #1aafff;
color: #fff;
}

&:not(:last-child) {
margin-bottom: 2px;
}

}

.Source {
display: flex;
}

.Extra, .Time, .Date {
font-size: 14px;
}

.UrlFirst, .UrlSecond {
font-size: 16px;
}

.UrlFirst {
overflow: hidden;
}

.Date {
float: right;
}
49 changes: 49 additions & 0 deletions src/components/History/HistoryModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React, { useContext, useState } from "react";
import Modal from "react-modal";
import classes from "./HistoryModal.module.scss";
import Link from "../Link/Link";
import HistoryItem from "./HistoryItem";
import { StoreContext } from "../../store";
import { changeVideo } from "../../store/actions";


export default () => {
const [state, dispatch] = useContext(StoreContext);
const [isShow, setIsShow] = useState(false);

const handleOpenModal = () => {
setIsShow(true);
};
const handleCloseModal = () => {
setIsShow(false);
};
const handleChangeVideo = (...args) => {
dispatch(changeVideo(...args));
handleCloseModal();
};

return (
<span>
<Link tag="button" onClick={handleOpenModal}>your history of watching video</Link>
<Modal
isOpen={isShow}
contentLabel="onRequestClose history"
onRequestClose={handleCloseModal}
className={classes.HistoryModal}
overlayClassName={classes.Overlay}
>
<header className={classes.Header}>
<h3>Browsing history</h3>
<span>Here you can see your 15 last video views. Please note that browsing history is stored locally and is
not available on other yours devices.</span>
<button className={classes.CloseHeaderButton} onClick={handleCloseModal}>✕</button>
</header>
<main className={classes.HistoryList}>
{state.history.map((item, index) =>
<HistoryItem key={index} item={item} changeVideo={handleChangeVideo} />
)}
</main>
</Modal>
</span>
);
};
105 changes: 105 additions & 0 deletions src/components/History/HistoryModal.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
.Overlay {
position: fixed;
z-index: 10;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, .50);
}

.HistoryModal{
box-shadow: 0 2px 5px rgba(0, 0, 0, .2);
position: fixed;
max-width: 500px;
width: 90vw;
left: 50%;
transform: translateX(-50%);
border: none;
border-radius: 5px;
animation: show-modal .3s;
height: 70vh;
top: 10%;
display: flex;
flex-direction: column;
outline: none;
overflow: hidden;
}

.Header {
position: relative;
background: linear-gradient(to left top, #81ecec, #00cec9);
/* background: linear-gradient(to left top,#21a7f0,#0074b3); */
color: white;
border-radius: 5px 5px 0 0;
padding: 20px;
text-align: center;

& h3 {
font-weight: 600;
padding-bottom: 10px;
}

& span {
display: inline-block;
font-weight: 400;
font-size: 15px;
line-height: 18px;
}
}

.CloseHeaderButton {
cursor: pointer;
position: absolute;
right: 0px;
top: 10px;
padding: 6px 25px 6px 6px;
font-size: 14px;
color: transparent;
background-color: rgba(0, 0, 0, .15);
border-radius: 50% 0px 0px 50%;

&::after {
content: "✕";
transition: transform .3s ease;
font-size: 12px;
background-color: #00cec9;
color: #fff;
position: absolute;
top: 6px;
left: 5px;
bottom: 6px;
width: 21px;
line-height: 21px;
z-index: 1;
border-radius: 50%;
}

&:hover::after {
transform: rotate(180deg);
/* scale(1.4); */
background-color: #e74c3c;
}
}

.HistoryList{
height: 100%;
overflow-y: auto;
background-color: white;
padding: 20px;
}

@keyframes show-modal {
0% {
transform: scale(.7) translateX(-50%);
}
45% {
transform: scale(1.05) translateX(-50%);
}
80% {
transform: scale(.95) translateX(-50%);
}
100% {
transform: scale(1) translateX(-50%);
}
}
14 changes: 14 additions & 0 deletions src/components/Link/Link.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from "react";
import classes from "./Link.module.scss";

export default (props) => {
if (props.tag === "button") {
return <button className={classes.Link} onClick={props.onClick}>
{props.children}
</button>;
} else {
return <a className={classes.Link} href={props.href || "#"}>
{props.children}
</a>;
}
};
35 changes: 35 additions & 0 deletions src/components/Link/Link.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.Link {
color: #1aafff;
position: relative;
font-weight: 600;
cursor: pointer;
border-bottom: 1px dotted currentColor;
border-radius: 2px;

&:hover {
cursor: pointer;
/* border-bottom: 1px solid #1aafff; */
}

&::after {
background: currentColor;
content: '';
height: 1px;
left: 50%;
position: absolute;
top: 100%;
transform: translateX(-50%);
transition: width .2s ease;
width: 0%;
}

&:hover::after {
width: 100%;
}

&:focus {
box-shadow: 0px 0px 0px 4px rgba(26, 175, 255, .5);
transition: box-shadow .3s ease;
}

}
14 changes: 7 additions & 7 deletions src/components/PageSection/PageSection.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import React from "react";
import classes from "./PageSection.module.css";

export default (props) => (
<main className={classes.PageSection}>
<section className={classes.Content}>
<header className={classes.Title}>{props.title || "Title"}</header>
{props.children}
</section>
</main>
);
<main className={[classes.PageSection, props.className].join(" ")}>
<section className={classes.Content}>
<header className={classes.Title}>{props.title || "Title"}</header>
{props.children}
</section>
</main>
);
Loading