Skip to content

Commit

Permalink
Code cleanups
Browse files Browse the repository at this point in the history
  • Loading branch information
https-sharif committed Oct 2, 2024
1 parent 533664d commit f1c0316
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 119 deletions.
135 changes: 68 additions & 67 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,30 +46,28 @@ type PhotoResult = {
};
};

type UnsplashResponse =
| {
type UnsplashResponse = {
results: PhotoResult[];
user: UserType;
links: {
download: string;
};
}
| PhotoResult[];
errors?: string;
} | PhotoResult[];

type Response = {
url: { raw: string; regular: string };
download: string;
user: UserType;
};

const App: React.FC = () => {
const [loading, setLoading] = useState<boolean>(false);
const [prompt, setPrompt] = useState<string>("");
const [error, setError] = useState<string | null>(null);
const [images, setImages] = useState<
{
url: { raw: string; regular: string };
download: string;
user: UserType;
}[]
>([]);
const [images, setImages] = useState<Response[]>([]);
const [queryType, setQueryType] = useState<string>("");
const [user, setUser] = useState<UserType>(null);
const [haveImages, setHaveImages] = useState<boolean>(true);
const [pages, setPages] = useState<number>(2);
const [previousPrompt, setPreviousPrompt] = useState<string>("");
const [message, setMessage] = useState<string | null>(null);
Expand All @@ -85,29 +83,24 @@ const App: React.FC = () => {
setMessage(null);
}, [prompt]);

const selectUrl = () => {
const selectUrl = (currentPrompt : string) => {
const API_URL = import.meta.env.VITE_UNSPLASH_API_URL;
const ACCESS_KEY = import.meta.env.VITE_UNSPLASH_ACCESS_KEY;
switch (queryType) {
case "random":
return API_URL + "/photos/random" + "?count=10&client_id=" + ACCESS_KEY;
case "user":
return (
API_URL + "/users/" + prompt + "/photos" + "?client_id=" + ACCESS_KEY
API_URL + "/users/" + currentPrompt + "/photos" + "?client_id=" + ACCESS_KEY
);
default:
return (
API_URL +
"/search/photos?query=" +
prompt +
"&client_id=" +
ACCESS_KEY
API_URL + "/search/photos?query=" + currentPrompt + "&client_id=" + ACCESS_KEY
);
}
};

const fetchUser = (response: UnsplashResponse) => {
if (queryType !== "user") return;
try {
console.log("User:", response);
if (Array.isArray(response) && response.length > 0) {
Expand Down Expand Up @@ -143,12 +136,16 @@ const App: React.FC = () => {
download: item.links.download,
user: item.user,
}));
} else {
} else if(response.errors){
throw new Error(response.errors);
}
else {
throw new Error("Unexpected response format");
}
};

const handleFetchImages = async () => {
setMessage(null);
if (previousPrompt !== prompt) {
setImages([]);
setPreviousPrompt(prompt);
Expand All @@ -157,30 +154,33 @@ const App: React.FC = () => {
setUser(null);
setError(null);
setLoading(true);
setHaveImages(true);
try {
const requestUrl = selectUrl();
const requestUrl = selectUrl(prompt);

const response = await fetch(requestUrl).then((res) => res.json());
const result = formatResult(response) as {
url: { raw: string; regular: string };
download: string;
user: UserType;
}[];
console.log("Response:", response);

const result = formatResult(response) as Response[];
console.log("Result:", result);
setHaveImages(result.length > 0);

if(result.length === 0) {
setMessage("No images found");
}

const preloadedImages = await preloadImages(result);
setImages(preloadedImages);
console.log("Preloaded Images:", preloadedImages);
fetchUser(response);

if(queryType === "user") {
if(response.length === 0) {
setMessage("No images found for this user");
}
else {
fetchUser(response);
}
}
} catch (error) {
console.error("Error in handleFetchImages:", error);
setError(
`An unexpected error occurred: ${
error instanceof Error ? error.message : String(error)
}`
);
setError(error instanceof Error ? error.message : String(error));
}
setLoading(false);
};
Expand All @@ -189,26 +189,19 @@ const App: React.FC = () => {
setPrompt(previousPrompt);
setLoading(true);
try {
const requestUrl = selectUrl() + "&page=" + pages;
const requestUrl = selectUrl(previousPrompt) + "&page=" + pages;
setPages(pages + 1);
console.log("Request URL:", requestUrl);

const response = await fetch(requestUrl).then((res) => res.json());
console.log("Response:", response);
const result = formatResult(response) as {
url: { raw: string; regular: string };
download: string;
user: UserType;
}[];

const result = formatResult(response) as Response[];
console.log("Result:", result);

if (result.length === 0) {
setMessage("No more images found");
}

setHaveImages(preloadImages.length > 0);

const preloadedImages = await preloadImages(result);
setImages([...images, ...preloadedImages]);
} catch (error) {
Expand All @@ -222,27 +215,13 @@ const App: React.FC = () => {
setLoading(false);
};

const preloadImages = (
imageArray: {
url: { raw: string; regular: string };
download: string;
user: UserType;
}[]
) => {
return new Promise<
{
url: { raw: string; regular: string };
download: string;
user: UserType;
}[]
>((resolve, reject) => {
const loadedImages: {
url: { raw: string; regular: string };
download: string;
user: UserType;
}[] = [];
const preloadImages = ( imageArray: Response[]) => {
return new Promise<Response[]>((resolve, reject) => {
const loadedImages: Response[] = [];
let loadedCount = 0;

if (loadedCount === imageArray.length) {
console.log("All images preloaded");
resolve(loadedImages);
}

Expand All @@ -255,6 +234,7 @@ const App: React.FC = () => {
loadedCount++;

if (loadedCount === imageArray.length) {
console.log("All images preloaded");
resolve(loadedImages);
}
};
Expand Down Expand Up @@ -317,7 +297,7 @@ const App: React.FC = () => {
/>
)}

<ImageCanvas images={images} haveImages={haveImages} />
<ImageCanvas images={images} />

{message && (
<div className="text-white text-2xl font-bold mb-10">{message}</div>
Expand All @@ -328,13 +308,34 @@ const App: React.FC = () => {
tabIndex={0}
onClick={getMoreImages}
disabled={loading}
className="w-28 rounded-2xl text-white font-bold h-12 bg-[#386FA4] active:bg-[#2b5680] disabled:opacity-50 mb-20"
className="w-28 select-none border-solid border-2 border-[#133C55] rounded-2xl text-white font-bold h-12 bg-[#386FA4] active:bg-[#2b5680] disabled:opacity-50 mb-20"
>
More
{loading ? (
<div className="flex justify-center items-center space-x-1">
<span className="sr-only">Loading...</span>
<div className="h-2 w-2 bg-white rounded-full animate-bounce [animation-delay:-0.3s]"></div>
<div className="h-2 w-2 bg-white rounded-full animate-bounce [animation-delay:-0.15s]"></div>
<div className="h-2 w-2 bg-white rounded-full animate-bounce"></div>
</div>
) : (
"More"
)}
</button>
)}
</div>
);
};

export default App;


// {loading ? (
// <div className="flex justify-center items-center space-x-1">
// <span className="sr-only">Loading...</span>
// <div className="h-2 w-2 bg-white rounded-full animate-bounce [animation-delay:-0.3s]"></div>
// <div className="h-2 w-2 bg-white rounded-full animate-bounce [animation-delay:-0.15s]"></div>
// <div className="h-2 w-2 bg-white rounded-full animate-bounce"></div>
// </div>
// ) : (
// buttonName
// )}
30 changes: 16 additions & 14 deletions src/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,10 @@ type Props = {
setQueryType: (queryType: string) => void;
};

const Button: React.FC<Props> = ({
onClick,
loading,
queryType,
setQueryType,
}) => {
const Button: React.FC<Props> = ({ onClick, loading, queryType, setQueryType }) => {
const [isDropdownOpen, setDropdownOpen] = useState(false);

const toggleDropdown = (
event: React.MouseEvent<HTMLSpanElement, MouseEvent>
) => {
const toggleDropdown = ( event: React.MouseEvent<HTMLSpanElement, MouseEvent> ) => {
if (loading) return;
event.stopPropagation();
setDropdownOpen(!isDropdownOpen);
Expand Down Expand Up @@ -49,25 +42,34 @@ const Button: React.FC<Props> = ({
return () => {
document.removeEventListener("click", handleClickOutside);
};
});
},[]);

const buttonName: string = queryName();

return (
<div className="relative inline-block">
<div className="h-10 m-w-200 items-center flex justify-center rounded-2xl select-none min-w-200 text-white font-bold hover:shadow-md hover:shadow-[#3b7ab8] transition-shadow duration-500">
<div className="h-10 m-w-200 items-center flex justify-center rounded-2xl select-none min-w-200 text-white font-bold hover:shadow-md hover:shadow-[#3b7ab8] transition-shadow duration-500 cursor-pointer">
<button
className="h-10 w-10/12 bg-[#386FA4] rounded-l-2xl active:bg-[#2b5680] disabled:opacity-50"
className="h-10 w-10/12 bg-[#386FA4] rounded-l-2xl active:bg-[#2b5680] disabled:opacity-50 border-l-2 border-solid border-y-2 border-[#133C55]"
type="button"
disabled={loading}
onClick={onClick}
>
{loading ? "Loading..." : buttonName}
{loading ? (
<div className="flex justify-center items-center space-x-1">
<span className="sr-only">Loading...</span>
<div className="h-2 w-2 bg-white rounded-full animate-bounce [animation-delay:-0.3s]"></div>
<div className="h-2 w-2 bg-white rounded-full animate-bounce [animation-delay:-0.15s]"></div>
<div className="h-2 w-2 bg-white rounded-full animate-bounce"></div>
</div>
) : (
buttonName
)}
</button>
<span
className={`${
loading && "opacity-50"
} flex h-10 items-center justify-center rounded-r-2xl active:bg-[#2b5680] right-0 bg-[#386FA4] w-2/12`}
} flex h-10 items-center justify-center rounded-r-2xl active:bg-[#2b5680] right-0 bg-[#386FA4] w-2/12 border-r-2 border-y-2 border-solid border-[#133C55]`}
onClick={toggleDropdown}
onKeyDown={(event) => {
if (event.key === "Enter") {
Expand Down
2 changes: 1 addition & 1 deletion src/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const Form: React.FC<Props> = ({
/>

{error && (
<p className="h-16 text-red-600 font-bold absolute -bottom-20">
<p className="h-16 text-red-500 font-bold absolute -bottom-20">
{error}
</p>
)}
Expand Down
31 changes: 13 additions & 18 deletions src/Images.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,20 @@ type Props = {
download: string;
user: UserType;
}[];
haveImages: boolean;
};

const ImageCanvas: React.FC<Props> = ({ images, haveImages }) => {
const handleImageClick = ({
url,
download,
user,
}: {
url: { raw: string; regular: string };
download: string;
user: UserType;
}) => {
type UserProps = {
url: { raw: string; regular: string };
download: string;
user: UserType;
};

const ImageCanvas: React.FC<Props> = ({ images }) => {

const handleImageClick = ({ url, download, user } : UserProps ) => {

document.body.style.overflow = "hidden";

// Create overlay
const overlay = document.createElement("div");
overlay.style.cssText = `
Expand Down Expand Up @@ -75,7 +75,7 @@ const ImageCanvas: React.FC<Props> = ({ images, haveImages }) => {

// Create image element
const image = document.createElement("img");
// Preload the image

// Create loading spinner
const loadingSpinner = document.createElement("div");
loadingSpinner.className =
Expand Down Expand Up @@ -214,11 +214,6 @@ const ImageCanvas: React.FC<Props> = ({ images, haveImages }) => {

return (
<div className="select-none mt-20 w-11/12 mb-20 ">
{!haveImages && (
<div className="font-bold text-white text-2xl w-full text-center">
No images found
</div>
)}
<div className="columns-2 md:columns-3 lg:columns-4 xl:columns-5 gap-2 ">
{images.map(
(
Expand All @@ -233,7 +228,7 @@ const ImageCanvas: React.FC<Props> = ({ images, haveImages }) => {
key={index}
tabIndex={index + 1}
src={image.url.regular}
className="rounded-md hover:scale-105 hover:shadow-[20px_35px_60px_-15px_rgba(0,0,0,0.3)] w-96 h-auto object-contain mb-2 hover:z-10 transition-all duration-500"
className="rounded-md hover:scale-105 border-2 border-solid border-black hover:shadow-[20px_35px_60px_-15px_rgba(0,0,0,0.3)] w-96 h-auto object-contain mb-2 hover:z-10 transition-all duration-500"
alt={`Image ${index + 1}`}
onClick={() => handleImageClick(image)}
draggable={false}
Expand Down
Loading

0 comments on commit f1c0316

Please sign in to comment.