Skip to content
Snippets Groups Projects
Commit e2114292 authored by Joel Dietz's avatar Joel Dietz
Browse files

initial commit

parent 92b12c68
No related branches found
No related tags found
No related merge requests found
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# .gitignore
.env
# dependencies
/node_modules
/.pnp
......
......@@ -11,10 +11,15 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.7.5",
"pexels": "^1.4.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11"
}
},
"node_modules/@adobe/css-tools": {
......@@ -627,9 +632,17 @@
}
},
"node_modules/@babel/plugin-proposal-private-property-in-object": {
"version": "7.21.0-placeholder-for-preset-env.2",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
"integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
"version": "7.21.11",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz",
"integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==",
"deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.",
"dev": true,
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.18.6",
"@babel/helper-create-class-features-plugin": "^7.21.0",
"@babel/helper-plugin-utils": "^7.20.2",
"@babel/plugin-syntax-private-property-in-object": "^7.14.5"
},
"engines": {
"node": ">=6.9.0"
},
......@@ -1888,6 +1901,17 @@
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/preset-env/node_modules/@babel/plugin-proposal-private-property-in-object": {
"version": "7.21.0-placeholder-for-preset-env.2",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
"integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/preset-env/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
......@@ -5375,6 +5399,29 @@
"node": ">=4"
}
},
"node_modules/axios": {
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz",
"integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/axios/node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/axobject-query": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz",
......@@ -9969,6 +10016,15 @@
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
},
"node_modules/isomorphic-fetch": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz",
"integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==",
"dependencies": {
"node-fetch": "^2.6.1",
"whatwg-fetch": "^3.4.1"
}
},
"node_modules/istanbul-lib-coverage": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
......@@ -12706,6 +12762,44 @@
"tslib": "^2.0.3"
}
},
"node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/node-fetch/node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/node-fetch/node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"node_modules/node-fetch/node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/node-forge": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
......@@ -13178,6 +13272,14 @@
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
},
"node_modules/pexels": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/pexels/-/pexels-1.4.0.tgz",
"integrity": "sha512-akpLySokCtw9JHGx7yMavOIAHGVP5721rLUONR/cFKjWkLjUXsHrJ5jndMKss9mx7AEMZRXs7loxEb+vLJf6kA==",
"dependencies": {
"isomorphic-fetch": "^3.0.0"
}
},
"node_modules/picocolors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
......@@ -14599,6 +14701,11 @@
"node": ">= 0.10"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
......
......@@ -6,13 +6,15 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.7.5",
"pexels": "^1.4.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"start": "PORT=3005 react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
......@@ -34,5 +36,8 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11"
}
}
......@@ -24,7 +24,7 @@
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`.
-->
<title>React App</title>
<title>Image Gallery</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
......
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
body, html, #root, .App {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
import logo from './logo.svg';
import React from 'react';
import FullscreenGallery from './FullscreenGallery';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
<FullscreenGallery />
</div>
);
}
......
import React, { useState, useEffect, useCallback } from 'react';
import { createClient } from 'pexels';
const PEXELS_API_KEY = process.env.REACT_APP_PEXELS_API_KEY;
const client = createClient(PEXELS_API_KEY);
function FullscreenGallery() {
const [showInput, setShowInput] = useState(false);
const [query, setQuery] = useState('nature');
const [tempQuery, setTempQuery] = useState('');
const [currentImage, setCurrentImage] = useState(null);
const [orientation, setOrientation] = useState('landscape');
const fetchRandomImage = useCallback(async () => {
try {
const randomPage = Math.floor(Math.random() * 100) + 1;
const response = await client.photos.search({
query: query,
per_page: 1,
page: randomPage,
orientation: orientation
});
if (response.photos.length > 0) {
setCurrentImage(response.photos[0].src.original);
}
} catch (error) {
console.error('Error fetching image:', error);
}
}, [query, orientation]);
const toggleFullscreen = () => {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen();
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
}
}
};
useEffect(() => {
fetchRandomImage();
const interval = setInterval(fetchRandomImage, 3600000); // 1 hour in milliseconds
return () => clearInterval(interval);
}, [query, fetchRandomImage]);
const toggleInput = () => setShowInput(!showInput);
const handleInputChange = (e) => setTempQuery(e.target.value);
const handleSave = () => {
setQuery(tempQuery);
setShowInput(false);
fetchRandomImage();
toggleFullscreen();
console.log('Query updated to:', tempQuery);
};
return (
<div style={{
width: '100vw',
height: '100vh',
backgroundImage: `url(${currentImage})`,
backgroundSize: 'contain',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
backgroundColor: 'black'
}}>
<button
onClick={toggleInput}
style={{
position: 'absolute',
bottom: '0',
right: '0',
width: '100px',
height: '100px',
background: 'transparent',
border: 'none',
cursor: 'pointer'
}}
aria-label="Toggle Fullscreen and Input"
/>
{showInput && (
<div style={{
position: 'absolute',
bottom: '20px',
right: '20px',
background: 'rgba(0, 0, 0, 0.8)',
padding: '20px',
borderRadius: '10px',
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.3)',
display: 'flex',
flexDirection: 'column',
gap: '10px',
maxWidth: '300px'
}}>
<input
type="text"
value={tempQuery}
onChange={handleInputChange}
placeholder="Enter new query"
style={{
padding: '10px',
borderRadius: '5px',
border: '1px solid #ccc',
fontSize: '16px'
}}
/>
<select
value={orientation}
onChange={(e) => setOrientation(e.target.value)}
style={{
padding: '10px',
borderRadius: '5px',
border: '1px solid #ccc',
fontSize: '16px',
marginTop: '10px'
}}
>
<option value="landscape">Landscape</option>
<option value="portrait">Portrait</option>
</select>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<button
onClick={handleSave}
style={{
padding: '10px 20px',
borderRadius: '5px',
border: 'none',
background: '#4CAF50',
color: 'white',
cursor: 'pointer',
fontSize: '16px'
}}
>
Save
</button>
<button
onClick={toggleFullscreen}
style={{
padding: '10px 20px',
borderRadius: '5px',
border: 'none',
background: '#2196F3',
color: 'white',
cursor: 'pointer',
fontSize: '16px'
}}
>
Fullscreen
</button>
<button
onClick={toggleInput}
style={{
padding: '10px 20px',
borderRadius: '5px',
border: 'none',
background: '#f44336',
color: 'white',
cursor: 'pointer',
fontSize: '16px'
}}
>
Close
</button>
</div>
</div>
)}
</div>
);
}
export default FullscreenGallery;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment