dikdimon's picture
Upload extensionsa using SD-Hub extension
7bed60d verified
import { useState, useEffect, useRef, useReducer } from 'react'
import { debounce } from 'lodash'
import axios from 'axios'
import ReactModal from 'react-modal'
import Image from "./Image"
export default function GetImages() {
const [render, setRender] = useState(false);
const [images, setImages] = useState([])
const [searchTerm, setSearchTerm] = useState('')
const [loading, setLoading] = useState(true)
const [error, setError] = useState(false)
const [sort, setSort] = useState('modified')
const [type, setType] = useState('lora')
const [renderMore, setRenderMore] = useState(false);
const [modalIsOpen, setIsOpen] = useState(false)
const [moreImages, setMoreImages] = useState([])
const [moreLoading, setMoreLoading] = useState(true)
const [moreError, setMoreError] = useState(false)
const [moreIndex, setMoreIndex] = useState(null)
const [moreDocument, setMoreDocument] = useState(null)
const inputReference = useRef(null)
ReactModal.setAppElement('#root')
const customModalStyle = {
overlay: {
backgroundColor: 'rgba(0,0,0,0.97)',
},
content: {
maxHeight: '90%',
maxWidth: '95%',
top: '50%',
left: '50%',
right: 'auto',
bottom: 'auto',
marginRight: '-50%',
transform: 'translate(-50%, -50%)',
backgroundColor: 'rgba(0,0,0,0.25)',
border: 'none',
scrollbarWidth: 'thin',
scrollbarColor: '#141414 #000'
},
}
const sortImages = (dataArray) => {
if (sort == "random") {
for (let i = dataArray.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[dataArray[i], dataArray[j]] = [dataArray[j], dataArray[i]];
}
} else {
dataArray.sort(function(a, b){
if (sort == "modified") {
return b.mtimeMs - a.mtimeMs
} else {
return a.filename - b.filename
}
})
}
return dataArray
}
const fetchImages = () => {
setLoading(true)
setMoreLoading(true)
setImages([])
setMoreImages([])
axios.get(`http://localhost:3000/images?type=${type}&search=${searchTerm}`)
.then(response => {
if (sort != "name") {
setImages(sortImages(response.data))
} else {
setImages(response.data)
}
setLoading(false)
setError(false)
})
.catch(err => {
console.log(err)
setLoading(false)
setError(true)
})
}
const fetchMoreImages = () => {
setMoreLoading(true)
setMoreImages([])
const image = images[moreIndex]
const query = encodeURIComponent(image.path + image.filename)
axios.get(`http://localhost:3000/moreImages?type=${type}&search=${query}`)
.then(response => {
setMoreLoading(false)
setMoreError(false)
if (sort != "name") {
setMoreImages(sortImages(response.data))
} else {
setMoreImages(response.data)
}
openModal()
})
.catch(err => {
console.log(err)
setMoreLoading(false)
setMoreError(true)
})
}
const handleSearchInputChange = debounce((e) => {
setSearchTerm(e.target.value)
}, 500)
const handleSortChange = (sortOption) => {
setSort(sortOption)
// Triggering refresh manually to allow for sorting option reclicks
setRender(!render)
}
const handleTypeChange = (typeOption) => {
setType(typeOption)
// Prevent issue with clicking the same image (eg. 3rd) on each tab not loading.
setMoreIndex(null)
}
const openModal = () => {
setIsOpen(true)
}
const closeModal = () => {
setIsOpen(false)
}
const handleFolderClick = (index) => {
if (moreIndex != null && moreIndex == index && !moreDocument) {
openModal()
} else {
setMoreDocument(null)
setMoreLoading(true)
setMoreIndex(index)
setRenderMore(!renderMore)
openModal()
}
}
const handleDocumentClick = (index) => {
setMoreLoading(true)
setMoreImages([])
setMoreDocument(null)
const image = images[index]
const file = (type == "poses") ?
'readme.txt'
: encodeURIComponent(image.filename.substring(0, image.filename.lastIndexOf('.')) + '.txt')
axios.get(`http://localhost:3000/${image.path.replaceAll('#','%23')}${file}`)
.then(response => {
setMoreLoading(false)
setMoreError(false)
setMoreDocument(response.data)
setMoreIndex(index)
openModal()
})
.catch(err => {
if (err.response.status == "404") {
setMoreLoading(false)
setMoreError(false)
setMoreDocument('No additional information found.')
setMoreIndex(index)
openModal()
} else {
console.log(err)
setMoreLoading(false)
setMoreError(true)
openModal()
}
})
}
const handleModalKeys = (event) => {
event.stopPropagation()
if (event.key == "ArrowLeft") {
if (moreIndex > 0) {
setMoreLoading(true)
setMoreDocument(null)
setMoreIndex(moreIndex - 1)
setRenderMore(!renderMore)
}
} else if (event.key == "ArrowRight") {
if (moreIndex+1 < images.length) {
setMoreLoading(true)
setMoreDocument(null)
setMoreIndex(moreIndex + 1)
setRenderMore(!renderMore)
}
} else if (event.key == "ArrowDown") {
handleDocumentClick(moreIndex)
} else if (event.key == "ArrowUp") {
handleFolderClick(moreIndex)
} else if (event.key == "Escape") {
closeModal()
}
}
useEffect(() => {
fetchImages()
}, [searchTerm, type, render])
useEffect(() => {
if (moreIndex != null) {
fetchMoreImages()
}
}, [renderMore])
return (
<>
<div>
<div id="search">
<div id="typeOptions">
<span title="Lora" className={`typeOption ${type == "lora" ? "highlight" : ""}`} onClick={() => handleTypeChange("lora")}>L</span>
<span title="Style" className={`typeOption ${type == "styles" ? "highlight" : ""}`} onClick={() => handleTypeChange("styles")}>S</span>
<span title="Embedding" className={`typeOption ${type == "embeddings" ? "highlight" : ""}`} onClick={() => handleTypeChange("embeddings")}>E</span>
<span title="HyperNetwork" className={`typeOption ${type == "hypernets" ? "highlight" : ""}`} onClick={() => handleTypeChange("hypernets")}>H</span>
<span title="Checkpoint" className={`typeOption ${type == "checkpoints" ? "highlight" : ""}`} onClick={() => handleTypeChange("checkpoints")}>C</span>
<span title="Poses" className={`typeOption ${type == "Poses" ? "highlight" : ""}`} onClick={() => handleTypeChange("poses")}>P</span>
<span title="Gallery" className={`typeOption ${type == "gallery" ? "highlight" : ""}`} onClick={() => handleTypeChange("gallery")}>G</span>
</div>
<input id="imgSearch" icon='search' placeholder='Search...'
onChange={handleSearchInputChange}
/>
<div id="sortOptions">
<span title="Modified" className={`sortOption ${sort == "modified" ? "highlight" : ""}`} onClick={() => handleSortChange("modified")}>M</span>
<span title="Name" className={`sortOption ${sort == "name" ? "highlight" : ""}`} onClick={() => handleSortChange("name")}>N</span>
<span title="Random" className={`sortOption ${sort == "random" ? "highlight" : ""}`} onClick={() => handleSortChange("random")}>R</span>
</div>
</div>
<div id="imgGrid">
{loading && <div className="lds-ellipsis"><div></div><div></div><div></div><div></div></div>}
{error && <h2>There was an error loading the images.</h2>}
{!loading && !error && images.map((image, index) => (
<Image key={index} index={index} {...image} onFolderClick={handleFolderClick} onDocumentClick={handleDocumentClick} />
))}
</div>
<ReactModal
isOpen={modalIsOpen}
onAfterOpen={() => { inputReference.current.focus() }}
onRequestClose={closeModal}
preventScroll={true}
style={customModalStyle}
>
<div
ref={inputReference}
tabIndex="-1" // Enables key handlers on div
onKeyDown={handleModalKeys}
className="modalContent"
>
{moreLoading && <div className="lds-ellipsis"><div></div><div></div><div></div><div></div></div>}
{moreError && <h2>There was an error during loading.</h2>}
{!moreLoading && !moreError && images[moreIndex] && moreImages[0] && !moreDocument && <div className="modalHeader" style={{width: `calc(224px * ${moreImages.length})`} /*adjust header size for # of images until max width*/}>{images[moreIndex].path + images[moreIndex].filename}</div>}
<div id="modalImgGrid">
{!moreLoading && !moreError && images[moreIndex] && moreImages[0] && !moreDocument && moreImages.map((image, index) => (
<div key={index+1000000} className="imgCard" onClick={() => {navigator.clipboard.writeText(images[moreIndex].prompt)}}>
<img width="224" height="336"
src={"http://localhost:3000/" + image.path.replaceAll('#','%23') + encodeURIComponent(image.filename)}
loading={index <= 60 ? "eager" : "lazy"}
title={image.filename}
/>
</div>
))}
{!moreLoading && !moreError && moreDocument && images[moreIndex] &&
<div className="moreDocument">
<pre>
File: {images[moreIndex].filename}{<br />}
{type != "styles" && `Keywords: ${images[moreIndex].keywords}\n`}
{type != "styles" && `Weight: ${images[moreIndex].weight}`}
{type == "styles" && `Prompt: ${images[moreIndex].prompt}`}
</pre>
<pre>{moreDocument}</pre>
</div>
}
</div>
</div>
</ReactModal>
</div>
</>
)
}