Skip to content

Commit

Permalink
Finish the rest of the features
Browse files Browse the repository at this point in the history
This includes all work for levels 2xx, 3xx and 4xx. The database has already been loaded up and the app has been deployed manually using `fly deploy`
  • Loading branch information
sztupy committed Dec 5, 2023
1 parent cb697fb commit b722f7c
Show file tree
Hide file tree
Showing 13 changed files with 674 additions and 330 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/run-features.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ run-name: Enforce selenium feature tests pass on committed files

on:
workflow_dispatch:
# pull_request:
pull_request:

jobs:
run-features:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/run-server-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ run-name: Enforce tests pass on committed files

on:
workflow_dispatch:
# pull_request:
pull_request:

jobs:
run-server-tests:
Expand Down
182 changes: 98 additions & 84 deletions client/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,120 +4,133 @@ import VideoList from "./components/VideoList";
import VideoSubmission from "./components/VideoSubmission";
import OrderingSelector from "./components/OrderingSelector";

const VIDEO_LIST = [
{
id: 523523,
title: "Never Gonna Give You Up",
url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
rating: 23,
},
{
id: 523427,
title: "The Coding Train",
url: "https://www.youtube.com/watch?v=HerCR8bw_GE",
rating: 230,
},
{
id: 82653,
title: "Mac & Cheese | Basics with Babish",
url: "https://www.youtube.com/watch?v=FUeyrEN14Rk",
rating: 2111,
},
{
id: 858566,
title: "Videos for Cats to Watch - 8 Hour Bird Bonanza",
url: "https://www.youtube.com/watch?v=xbs7FT7dXYc",
rating: 11,
},
{
id: 453538,
title:
"The Complete London 2012 Opening Ceremony | London 2012 Olympic Games",
url: "https://www.youtube.com/watch?v=4As0e4de-rI",
rating: 3211,
},
{
id: 283634,
title: "Learn Unity - Beginner's Game Development Course",
url: "https://www.youtube.com/watch?v=gB1F9G0JXOo",
rating: 211,
},
{
id: 562824,
title: "Cracking Enigma in 2021 - Computerphile",
url: "https://www.youtube.com/watch?v=RzWB5jL5RX0",
rating: 111,
},
{
id: 442452,
title: "Coding Adventure: Chess AI",
url: "https://www.youtube.com/watch?v=U4ogK0MIzqk",
rating: 671,
},
{
id: 536363,
title: "Coding Adventure: Ant and Slime Simulations",
url: "https://www.youtube.com/watch?v=X-iSQQgOd1A",
rating: 76,
},
{
id: 323445,
title: "Why the Tour de France is so brutal",
url: "https://www.youtube.com/watch?v=ZacOS8NBK6U",
rating: 73,
},
];

const App = () => {
let [videos, setVideos] = useState([]);
let [message, setMessage] = useState(null);
let [order, setOrder] = useState("id");

function orderVideos(videos, order, initial) {
function orderVideos(videos, order) {
switch (order) {
case "rating_asc":
return videos.sort((a, b) => a.rating - b.rating);
case "rating_desc":
return videos.sort((a, b) => b.rating - a.rating);
case "random":
if (initial) {
return videos.sort(() => 0.5 - Math.random());
} else {
return videos; // this should only sort once when getting from backend, then keep it as-is
}
return videos; // this should only sort once when getting from backend, then keep it as-is
case "id":
return videos.sort((a, b) => a.id - b.id);
}
}

useEffect(() => {
setVideos([...orderVideos(VIDEO_LIST, order, true)]);
}, [setVideos, order]);
const fetchVideos = async () => {
try {
const videoResults = await fetch(`/api/videos?order=${order}`);
const videoResultsJson = await videoResults.json();
if (videoResultsJson.success) {
setVideos(videoResultsJson.data);
setMessage(null);
} else {
setMessage(
videoResultsJson.message ||
"Error while loading video recommendations, please try again by reloading the page!"
);
}
} catch (error) {
setMessage(
"Error while loading video recommendations, please try again by reloading the page!"
);
}
};
fetchVideos();
}, [setVideos, setMessage, order]);

const addVideo = function (title, url) {
const newVideo = {
id: Math.max(...videos.map((v) => v.id)) + 1,
title: title,
url: url,
rating: 0,
const publishToApi = async () => {
try {
const results = await fetch("/api/videos", {
method: "POST",
body: JSON.stringify({ title: title, url: url }),
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
});
const data = await results.json();
if (data.success) {
setVideos(orderVideos([...videos, data.data], order));
} else {
setMessage(
data.message ||
"Error while publishing the new video. Please reload the page and try again!"
);
}
} catch (error) {
setMessage(
"Error while publishing the new video. Please reload the page and try again!"
);
}
};
VIDEO_LIST.push(newVideo);
setVideos([...orderVideos(VIDEO_LIST, order)]);
publishToApi();
};

const updateVideo = function (video, action) {
const deleteVideo = async (selectedVideo) => {
VIDEO_LIST.splice(VIDEO_LIST.indexOf(selectedVideo), 1);
setVideos([...orderVideos(VIDEO_LIST, order)]);
try {
const results = await fetch(`/api/videos/${selectedVideo.id}`, {
method: "DELETE",
headers: {
"Access-Control-Allow-Origin": "*",
},
});
const data = await results.json();
if (data.success) {
videos = videos.filter((e) => e.id !== selectedVideo.id);
} else {
selectedVideo.message =
data.message ||
"There was an error while trying to delete the video. Please reload the page and try again!";
}
} catch (error) {
selectedVideo.message =
"There was an error while trying to delete the video. Please reload the page and try again!";
}
setVideos(orderVideos([...videos], order));
};

const voteOnVideo = async (selectedVideo, action) => {
selectedVideo.rating += action == "up" ? 1 : -1;
setVideos([...orderVideos(VIDEO_LIST, order)]);
try {
const results = await fetch(
`/api/videos/${selectedVideo.id}/${action}`,
{
method: "POST",
headers: {
"Access-Control-Allow-Origin": "*",
},
}
);
const data = await results.json();
if (data.success) {
selectedVideo.rating = data.data.rating;
selectedVideo.message = null;
} else {
selectedVideo.message =
data.message ||
"There was an error while updating rating to the video. Please reload the page and try again!";
}
} catch (error) {
selectedVideo.message =
"There was an error while updating rating to the video. Please reload the page and try again!";
}
setVideos(orderVideos([...videos], order));
};

let selectedVideo = VIDEO_LIST.find((e) => e.id === video.id);
let selectedVideo = videos.find((e) => e.id === video.id);

if (selectedVideo) {
selectedVideo.message = " ";
setVideos(orderVideos([...videos], order));

switch (action) {
case "up":
case "down":
Expand All @@ -133,6 +146,7 @@ const App = () => {
return (
<>
<h1>Video Recommendations</h1>
{message && <h2 className="message">{message}</h2>}
<OrderingSelector order={order} setOrder={setOrder} />
<VideoList videos={videos} updateVideo={updateVideo} />
<VideoSubmission addVideo={addVideo} />
Expand Down
24 changes: 21 additions & 3 deletions client/src/components/Video.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,35 @@ export default function Video({ video, updateVideo }) {
allowFullScreen
></iframe>
)}
<h3>Recommended since</h3>
<div title="Recommended since" className="recommended-since">
{new Date(video.created_at).toLocaleString()}
</div>
<h3>Rating</h3>
<div title="Rating" className="rating">
{video.rating}
</div>
<h3>Controls</h3>
<div className="control-message">{video.message}</div>
<div className="controls">
<button onClick={() => updateVideo(video, "delete")}>
<button
disabled={video.message ? true : false}
onClick={() => updateVideo(video, "delete")}
>
Remove video
</button>
<button onClick={() => updateVideo(video, "up")}>Up Vote</button>
<button onClick={() => updateVideo(video, "down")}>Down Vote</button>
<button
disabled={video.message ? true : false}
onClick={() => updateVideo(video, "up")}
>
Up Vote
</button>
<button
disabled={video.message ? true : false}
onClick={() => updateVideo(video, "down")}
>
Down Vote
</button>
</div>
</li>
);
Expand Down
35 changes: 35 additions & 0 deletions client/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ h1 {
text-align: center;
}

.message {
width: auto;
padding: 1em;
margin: 1em;
border: 1px solid red;
border-radius: 5px;
background-color: lightsalmon;
}

#videos {
display: grid;
grid-auto-flow: row;
Expand Down Expand Up @@ -83,6 +92,25 @@ h1 {
background-color: bisque;
}

.video .recommended-since:before {
content: "🕒 ";
}

.video .recommended-since {
max-width: fit-content;
padding: 0.5em;

display: block;
font-size: 1em;

margin: 0.5em auto;

border: 1px solid black;
border-radius: 5px;

background-color: bisque;
}

.video h3 {
position: absolute;
left: -10000px;
Expand Down Expand Up @@ -118,6 +146,13 @@ h1 {
grid-template-columns: 1fr 1fr 1fr;
}

.control-message {
position: absolute;
bottom: 0;
background-color: white;
border: 1px solid red;
}

#submit-video {
text-align: center;
width: auto;
Expand Down
Loading

0 comments on commit b722f7c

Please sign in to comment.