added image support
This commit is contained in:
parent
65ed1548a7
commit
15fc3944b4
8 changed files with 255 additions and 187 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1 +1,3 @@
|
||||||
node_modules
|
node_modules
|
||||||
|
env
|
||||||
|
.env
|
||||||
13
package-lock.json
generated
13
package-lock.json
generated
|
|
@ -4513,9 +4513,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dotenv": {
|
"dotenv": {
|
||||||
"version": "10.0.0",
|
"version": "16.4.7",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
|
||||||
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q=="
|
"integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ=="
|
||||||
},
|
},
|
||||||
"dotenv-expand": {
|
"dotenv-expand": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
|
|
@ -10368,6 +10368,13 @@
|
||||||
"webpack-dev-server": "^4.6.0",
|
"webpack-dev-server": "^4.6.0",
|
||||||
"webpack-manifest-plugin": "^4.0.2",
|
"webpack-manifest-plugin": "^4.0.2",
|
||||||
"workbox-webpack-plugin": "^6.4.1"
|
"workbox-webpack-plugin": "^6.4.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"dotenv": {
|
||||||
|
"version": "10.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
|
||||||
|
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q=="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"read-cache": {
|
"read-cache": {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"dotenv": "^16.4.7",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-scripts": "5.0.1"
|
"react-scripts": "5.0.1"
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,7 @@ function App() {
|
||||||
setSelectedProjectId(null);
|
setSelectedProjectId(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectedProject = selectedProjectId
|
const selectedProject = selectedProjectId ? projects.find(p => p.id === selectedProjectId) : null;
|
||||||
? projects.find(p => p.id === selectedProjectId)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="app">
|
<div className="app">
|
||||||
|
|
|
||||||
|
|
@ -2,44 +2,63 @@ import React, { useState, useEffect } from 'react';
|
||||||
|
|
||||||
const ImageCarousel = ({ projectId }) => {
|
const ImageCarousel = ({ projectId }) => {
|
||||||
const [images, setImages] = useState([]);
|
const [images, setImages] = useState([]);
|
||||||
|
const [highResImages, setHighResImages] = useState([]);
|
||||||
|
const [fileIds, setFileIds] = useState([]);
|
||||||
const [currentImageIndex, setCurrentImageIndex] = useState(0);
|
const [currentImageIndex, setCurrentImageIndex] = useState(0);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [viewMode, setViewMode] = useState('thumbnail');
|
||||||
|
|
||||||
|
const getDirectDownloadUrl = (fileId) => {
|
||||||
|
return `https://lh3.google.com/u/0/d/${fileId}`
|
||||||
|
};
|
||||||
|
|
||||||
|
const getThumbnailUrl = (fileId) => {
|
||||||
|
return `https://drive.google.com/thumbnail?id=${fileId}&sz=w300`;
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
const loadImages = async () => {
|
const loadImages = async () => {
|
||||||
try {
|
try {
|
||||||
|
const apiKey = process.env.REACT_APP_GOOGLE_API_KEY;
|
||||||
|
|
||||||
const importAll = (r) => r.keys().map(r);
|
if (!apiKey) {
|
||||||
|
throw new Error("Google API key not found in environment variables");
|
||||||
let imageContext = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
switch(projectId){
|
|
||||||
case "mycology-lab":
|
|
||||||
imageContext = require.context(`../assets/mycology-lab`, false, /\.(png|jpe?g|svg)$/);
|
|
||||||
break
|
|
||||||
case "loanterra":
|
|
||||||
imageContext = require.context(`../assets/loanterra`, false, /\.(png|jpe?g|svg)$/);
|
|
||||||
break
|
|
||||||
case "orchard-market":
|
|
||||||
imageContext = require.context(`../assets/orchard-market`, false, /\.(png|jpe?g|svg)$/);
|
|
||||||
break
|
|
||||||
case "fecal-vision-model":
|
|
||||||
imageContext = require.context(`../assets/fecal-vision-model`, false, /\.(png|jpe?g|svg)$/);
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
imageContext = require.context(`../assets/mycology-lab`, false, /\.(png|jpe?g|svg)$/);
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
console.log("Error", e);
|
let folderId = "1kLdnnm47c7GkmKgiyIbv2QXh7sqFZ6p4";
|
||||||
|
|
||||||
|
let query = encodeURIComponent(`'${folderId}' in parents and trashed=false`);
|
||||||
|
|
||||||
|
let driveResponse = await fetch(
|
||||||
|
`https://www.googleapis.com/drive/v3/files?q=${query}&fields=files(id,name,mimeType)&key=${apiKey}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!driveResponse.ok) {
|
||||||
|
throw new Error(`Google Drive API error: ${driveResponse.status}`);
|
||||||
}
|
}
|
||||||
if(imageContext){
|
|
||||||
const imageFiles = importAll(imageContext);
|
const folderData = await driveResponse.json();
|
||||||
setImages(imageFiles);
|
let foundFolder = folderData.files.filter(file => file.mimeType.startsWith('application/')).find(item => item.name === projectId);
|
||||||
|
|
||||||
|
if (foundFolder) {
|
||||||
|
folderId = foundFolder.id;
|
||||||
|
query = encodeURIComponent(`'${folderId}' in parents and trashed=false`);
|
||||||
|
driveResponse = await fetch(
|
||||||
|
`https://www.googleapis.com/drive/v3/files?q=${query}&fields=files(id,name,mimeType)&key=${apiKey}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const fileData = await driveResponse.json();
|
||||||
|
const ids = fileData.files
|
||||||
|
.filter(file => file.mimeType.startsWith('image/'))
|
||||||
|
.map(file => file.id);
|
||||||
|
|
||||||
|
setFileIds(ids);
|
||||||
|
const thumbnailUrls = ids.map(id => getThumbnailUrl(id));
|
||||||
|
setImages(thumbnailUrls);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error loading images:", error);
|
console.error("Error loading images:", error);
|
||||||
|
|
||||||
setImages([]);
|
setImages([]);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
@ -47,17 +66,40 @@ const ImageCarousel = ({ projectId }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
loadImages();
|
loadImages();
|
||||||
|
|
||||||
setCurrentImageIndex(0);
|
setCurrentImageIndex(0);
|
||||||
|
setViewMode('thumbnail');
|
||||||
}, [projectId]);
|
}, [projectId]);
|
||||||
|
|
||||||
|
const toggleViewMode = () => {
|
||||||
|
if (viewMode === 'thumbnail') {
|
||||||
|
setViewMode('highres');
|
||||||
|
setHighResImages([]);
|
||||||
|
setLoading(true);
|
||||||
|
const highResUrls = fileIds.map(id => getDirectDownloadUrl(id));
|
||||||
|
setHighResImages(highResUrls);
|
||||||
|
setLoading(false);
|
||||||
|
} else {
|
||||||
|
setViewMode('thumbnail');
|
||||||
|
|
||||||
|
setImages([]);
|
||||||
|
setLoading(true);
|
||||||
|
const thumbnailUrls = fileIds.map(id => getThumbnailUrl(id));
|
||||||
|
setImages(thumbnailUrls);
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const nextImage = () => {
|
const nextImage = () => {
|
||||||
|
if(viewMode === 'highres')
|
||||||
|
toggleViewMode();
|
||||||
setCurrentImageIndex((prevIndex) =>
|
setCurrentImageIndex((prevIndex) =>
|
||||||
prevIndex === images.length - 1 ? 0 : prevIndex + 1
|
prevIndex === images.length - 1 ? 0 : prevIndex + 1
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const prevImage = () => {
|
const prevImage = () => {
|
||||||
|
if(viewMode === 'highres')
|
||||||
|
toggleViewMode();
|
||||||
setCurrentImageIndex((prevIndex) =>
|
setCurrentImageIndex((prevIndex) =>
|
||||||
prevIndex === 0 ? images.length - 1 : prevIndex - 1
|
prevIndex === 0 ? images.length - 1 : prevIndex - 1
|
||||||
);
|
);
|
||||||
|
|
@ -70,7 +112,10 @@ const ImageCarousel = ({ projectId }) => {
|
||||||
if (images.length === 0) {
|
if (images.length === 0) {
|
||||||
return <div className="carousel-empty">No images available</div>;
|
return <div className="carousel-empty">No images available</div>;
|
||||||
}
|
}
|
||||||
|
const addDefaultImg = ev => {
|
||||||
|
// ev.target.src = images[currentImageIndex]
|
||||||
|
ev.target.src = 'https://upload.wikimedia.org/wikipedia/commons/1/14/No_Image_Available.jpg'
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className="carousel">
|
<div className="carousel">
|
||||||
<button
|
<button
|
||||||
|
|
@ -83,12 +128,25 @@ const ImageCarousel = ({ projectId }) => {
|
||||||
|
|
||||||
<div className="carousel-image-container">
|
<div className="carousel-image-container">
|
||||||
<img
|
<img
|
||||||
src={images[currentImageIndex]}
|
src={viewMode === "thumbnail" ? images[currentImageIndex] : highResImages[currentImageIndex]}
|
||||||
alt={`Project ${projectId} - ${currentImageIndex + 1}`}
|
alt={`Project ${projectId} - ${currentImageIndex + 1}`}
|
||||||
className="carousel-image"
|
className="carousel-image"
|
||||||
|
onClick={toggleViewMode}
|
||||||
|
onError={(e) => addDefaultImg}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
height: 'auto'
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="carousel-counter">
|
<div className="carousel-counter">
|
||||||
{currentImageIndex + 1} / {images.length}
|
{currentImageIndex + 1} / {images.length}
|
||||||
|
{viewMode === 'thumbnail' && (
|
||||||
|
<span className="image-quality-indicator"> [SD](Click Image for HD)</span>
|
||||||
|
)}
|
||||||
|
{viewMode === 'highres' && (
|
||||||
|
<span className="image-quality-indicator"> [HD](Click Image for SD)</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -103,4 +161,6 @@ const ImageCarousel = ({ projectId }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default ImageCarousel;
|
export default ImageCarousel;
|
||||||
Loading…
Reference in a new issue