Implemented basic site layout, filtering via search, and added popup preview
This commit is contained in:
parent
ffd2558750
commit
0396da7c26
2
app.js
2
app.js
@ -116,7 +116,7 @@ app.post("/:kind/add",(req,res)=>{
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const port = 3001
|
const port = 3002
|
||||||
app.listen(port, () => console.log(`Listening at http://localhost:${port}`))
|
app.listen(port, () => console.log(`Listening at http://localhost:${port}`))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
-->
|
-->
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||||
|
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
|
||||||
<!--
|
<!--
|
||||||
Notice the use of %PUBLIC_URL% in the tags above.
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
It will be replaced with the URL of the `public` folder during the build.
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
@ -24,7 +25,7 @@
|
|||||||
work correctly both with client-side routing and a non-root public URL.
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
-->
|
-->
|
||||||
<title>React App</title>
|
<title>iTunes App</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
@ -28,6 +28,21 @@
|
|||||||
color: #61dafb;
|
color: #61dafb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gradient {
|
||||||
|
background-image: radial-gradient(#DDDDFF,#F0F0F0);
|
||||||
|
}
|
||||||
|
.gradient:hover {
|
||||||
|
background-image: radial-gradient(#DDDDFF,#FFFFDD);
|
||||||
|
}
|
||||||
|
.shadow {
|
||||||
|
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||||
|
}
|
||||||
|
.bottomright {
|
||||||
|
position:"absolute";
|
||||||
|
right:80vw;
|
||||||
|
bottom:80vh;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes App-logo-spin {
|
@keyframes App-logo-spin {
|
||||||
from {
|
from {
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
|
@ -1,32 +1,53 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
|
|
||||||
|
const playerWidth=320;
|
||||||
|
|
||||||
|
const PreviewWindow = (props) => {
|
||||||
|
return (props.src!==null)?<div>
|
||||||
|
<iframe style={{position:"absolute",
|
||||||
|
left:(window.innerWidth-playerWidth)+"px",bottom:-window.scrollY+"px"}} className="bottomright" id="previewWindow" src={props.src}/>
|
||||||
|
</div>:<React.Fragment/>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const RadioButton = (props) => {
|
const RadioButton = (props) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<br></br>
|
<label for={props.name} className="ml-4">{props.displayName}</label>
|
||||||
<label for={props.name}>{props.name}</label>
|
|
||||||
<input type='radio' name="media-selection" value={props.name} id={props.name}
|
<input type='radio' name="media-selection" value={props.name} id={props.name}
|
||||||
onClick={() => props.setMediaType(props.name)}/>
|
onClick={() => props.setMediaType(props.name)} checked={props.mediaType===props.name}/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const MediaContainer = (props) => {
|
const MediaContainer = (props) => {
|
||||||
const [previewOn, setPreviewOn] = useState(false)
|
const togglePreview = (setPreview,sourceUrl) => {
|
||||||
|
//console.log(setPreview+","+sourceUrl)
|
||||||
const togglePreview = (counter, sourceUrl) => {
|
setPreview(sourceUrl);
|
||||||
document.getElementById(counter).innerHTML = "<iframe src='" + sourceUrl + "'></iframe>"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{props.data.map((media, counter) => <div><img src={media.artworkUrl100} alt={media.artistName} /><span>{media.trackName} </span>
|
{props.data.map((media, counter) =>
|
||||||
<span>{media.trackId} </span> <span>{media.collectionName} </span> <span>{media.collectionId} </span>
|
<React.Fragment>
|
||||||
<span>{media.artistId} </span> <span>{media.artistName} </span> <button type='button' onClick={() => togglePreview(counter, media.previewUrl)}>Toggle Preview</button>
|
<div className="card pt-3 gradient">
|
||||||
<div id={counter}></div><hr/></div>)}
|
<div className="row">
|
||||||
|
<div className="offset-md-2 col-md-6">
|
||||||
|
<h4>{media.trackName}</h4>
|
||||||
|
<b>{media.artistName} </b>: <span>{media.collectionName} </span>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-3 text-center">
|
||||||
|
<img className="shadow" src={media.artworkUrl100} alt={media.artistName}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="row mb-3">
|
||||||
|
<div className="offset-md-8 col-md-3 text-center">
|
||||||
|
<button type='button' onClick={() => togglePreview(props.setPreview,media.previewUrl)}>Show Preview</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</React.Fragment>)}
|
||||||
</div>)
|
</div>)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,19 +55,38 @@ const MediaContainer = (props) => {
|
|||||||
const App = () => {
|
const App = () => {
|
||||||
const [mediaType, setMediaType] = useState("song")
|
const [mediaType, setMediaType] = useState("song")
|
||||||
const [currentData, setCurrentData] = useState([])
|
const [currentData, setCurrentData] = useState([])
|
||||||
|
const [currentPreview, setPreview] = useState(null)
|
||||||
|
const [searchQuery, setSearchQuery] = useState("")
|
||||||
|
|
||||||
useEffect(() => {fetch(`http://localhost:3001/${mediaType}`)
|
useEffect(() => {fetch(`http://localhost:3002/${mediaType}`)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {console.log(data); setCurrentData(data)})}, [mediaType])
|
.then(data => {/*console.log(data);*/ setCurrentData(data)})}, [mediaType])
|
||||||
|
|
||||||
|
function setSearch(e){
|
||||||
|
setSearchQuery(e.target.value)
|
||||||
|
//console.log(e.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
Current media type: {mediaType}
|
<div className="container">
|
||||||
<RadioButton name="song" setMediaType={setMediaType} />
|
<div className="row">
|
||||||
<RadioButton name="music-video" setMediaType={setMediaType} />
|
<div className="text-center col-md-12">
|
||||||
<RadioButton name="feature-movie" setMediaType={setMediaType} />
|
Search Title/Artist/Album: <input style={{"width":"480px","height":"32px"}} type="text" onChange={(e)=>{setSearch(e)}}/>
|
||||||
|
</div>
|
||||||
<MediaContainer data={currentData}/>
|
</div>
|
||||||
|
<div className="row">
|
||||||
|
<div className="offset-md-2 col-md-8">
|
||||||
|
<RadioButton displayName="Songs" mediaType={mediaType} name="song" setMediaType={setMediaType} />
|
||||||
|
<RadioButton displayName="Music Videos" mediaType={mediaType} name="music-video" setMediaType={setMediaType} />
|
||||||
|
<RadioButton displayName="Movies" mediaType={mediaType} name="feature-movie" setMediaType={setMediaType} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<MediaContainer data={(searchQuery.length>0)?currentData.filter((song)=>{
|
||||||
|
return (song.trackName.toLowerCase().includes(searchQuery.toLowerCase())||song.collectionName.toLowerCase().includes(searchQuery.toLowerCase())||song.artistName.toLowerCase().includes(searchQuery.toLowerCase()))
|
||||||
|
}):currentData} setPreview={setPreview}/>
|
||||||
|
<PreviewWindow src={currentPreview}/>
|
||||||
|
</div>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,15 @@ import './index.css';
|
|||||||
import App from './App';
|
import App from './App';
|
||||||
import * as serviceWorker from './serviceWorker';
|
import * as serviceWorker from './serviceWorker';
|
||||||
|
|
||||||
|
document.addEventListener("scroll",()=>{
|
||||||
|
var obj = document.getElementById("previewWindow");
|
||||||
|
if (obj) {
|
||||||
|
obj.style.position="absolute";
|
||||||
|
obj.style.right=window.scrollX+"px";
|
||||||
|
obj.style.bottom=-window.scrollY+"px";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user