Skip to content

Commit

Permalink
Major UI and fix prompt refreshing
Browse files Browse the repository at this point in the history
  • Loading branch information
Precious-Macaulay committed Nov 9, 2024
1 parent e20e0e6 commit ff04d26
Show file tree
Hide file tree
Showing 10 changed files with 302 additions and 150 deletions.
2 changes: 1 addition & 1 deletion src/components/GalleryItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const GalleryItem: React.FC<GalleryItemProps> = ({ prompt, index }) => {
key={prompt.id}
className="mb-[2px] cursor-pointer"
onClick={() =>
navigate(`/prompt/${prompt.id}`, { state: { type: "gallery", index: 0 } })
navigate(`/prompt/${prompt.id}/0`, { state: { type: "gallery" } })
}
>
<div className="relative group">
Expand Down
2 changes: 1 addition & 1 deletion src/components/PromptCard/PromptCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const PromptCard: React.FC<PromptCardProps> = ({ prompt: initialPrompt, promptIn
const displayPrompt = retrievedPrompt || initialPrompt;

const handleImageClick = (index: number) => {
navigate(`/prompt/${displayPrompt.id}`, { state: { type: "user", index } });
navigate(`/prompt/${displayPrompt.id}/${index}`, { state: { type: "user" } });
};

return (
Expand Down
4 changes: 2 additions & 2 deletions src/components/PromptDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const PromptDetails: React.FC<PromptDetailsProps> = ({ prompt, imageUrl }) => {
};

return (
<div className="hidden md:w-1/3 h-full md:flex flex-col justify-between rounded-xl p-4 shadow-lg bg-white">
<div className="hidden md:w-2/5 lg:w-1/3 h-full md:flex flex-col justify-between rounded-xl p-4 shadow-lg bg-white">
<div>
<div className="flex mb-2 justify-between items-center">
<h2 className="text-lg font-semibold">{prompt?.id}</h2>
Expand Down Expand Up @@ -81,7 +81,7 @@ const PromptDetails: React.FC<PromptDetailsProps> = ({ prompt, imageUrl }) => {
</div>
</div>
<p className="overflow-y-auto max-h-48 text-sm text-gray-700">
{prompt?.text.length > 100 ? `${prompt?.text.slice(0, 200)}...` : prompt?.text}
{prompt?.text?.length > 100 ? `${prompt?.text.slice(0, 200)}...` : prompt?.text}
</p>
</div>
<div className="flex justify-center p-2 mt-2">
Expand Down
2 changes: 1 addition & 1 deletion src/components/PromptImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const PromptImage: React.FC<PromptImageProps> = ({ imageUrl, type, setDisplay })

return (
<div
className="flex-1 relative cursor-zoom-in overflow-hidden"
className="flex-1 relative cursor-zoom-in overflow-hidden pb-12 md:pb-0"
onClick={() => setDisplay && setDisplay(true)}
>
<div className="h-full p-6 flex justify-center items-center">
Expand Down
189 changes: 106 additions & 83 deletions src/hooks/usePromptNavigation.ts
Original file line number Diff line number Diff line change
@@ -1,115 +1,138 @@
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { Prompt } from "@/types";

export const usePromptNavigation = (
currentPromptIndex: number,
prompts: Prompt[],
type: string,
currentImageIndex: number
) => {

// hooks/usePromptNavigation.ts
import { useEffect, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { Prompt } from '../types';

interface PromptNavigationHookResult {
navigateToNext: () => void;
navigateToPrevious: () => void;
}

interface UsePromptNavigationProps {
currentPromptIndex: number;
prompts: Prompt[];
type: string;
currentImageIndex: number;
}

export const usePromptNavigation = ({
currentPromptIndex,
prompts,
type,
currentImageIndex,
}: UsePromptNavigationProps): PromptNavigationHookResult => {
const navigate = useNavigate();

const navigateToNext = () => {
const nextIndex = currentPromptIndex + 1;

if (nextIndex < prompts.length) {
const nextImageIndex = currentImageIndex + 1;
const images = prompts[currentPromptIndex].images;

console.log(`/prompt/${prompts[currentPromptIndex].id}`, { type, index: nextImageIndex });
if (nextImageIndex < images.length) {
navigate(`/prompt/${prompts[currentPromptIndex].id}`, {
state: { type, index: nextImageIndex },
});
} else {
navigate(`/prompt/${prompts[nextIndex].id}`, {
state: { type, index: 0 },
});
}
const navigateToNext = useCallback(() => {
const currentPrompt = prompts[currentPromptIndex];
if (!currentPrompt) return;

const nextImageIndex = currentImageIndex + 1;

// Check if we can move to the next image in the current prompt
if (nextImageIndex < currentPrompt.images.length) {
navigate(`/prompt/${currentPrompt.id}/${nextImageIndex}`, {
state: { type },
replace: true,
});
return;
}
};

const navigateToPrevious = () => {
const previousIndex = currentPromptIndex - 1;
const images = prompts[previousIndex]?.images;

if (previousIndex >= 0) {
const previousImageIndex = currentImageIndex - 1;

if (previousImageIndex >= 0) {
navigate(`/prompt/${prompts[currentPromptIndex].id}`, {
state: { type, index: previousImageIndex },
});
} else {
navigate(`/prompt/${prompts[previousIndex].id}`, {
state: { type, index: images.length - 1 },
});
}

// Move to the next prompt
const nextPromptIndex = currentPromptIndex + 1;
if (nextPromptIndex < prompts.length) {
navigate(`/prompt/${prompts[nextPromptIndex].id}/0`, {
state: { type },
replace: true,
});
}
};
}, [currentPromptIndex, currentImageIndex, prompts, type, navigate]);

const navigateToPrevious = useCallback(() => {
const currentPrompt = prompts[currentPromptIndex];
if (!currentPrompt) return;

const previousImageIndex = currentImageIndex - 1;

// Check if we can move to the previous image in the current prompt
if (previousImageIndex >= 0) {
navigate(`/prompt/${currentPrompt.id}/${previousImageIndex}`, {
state: { type },
replace: true,
});
return;
}

// Move to the previous prompt
const previousPromptIndex = currentPromptIndex - 1;
if (previousPromptIndex >= 0) {
const previousPrompt = prompts[previousPromptIndex];
navigate(
`/prompt/${previousPrompt.id}/${previousPrompt.images.length - 1}`,
{
state: { type },
replace: true,
}
);
}
}, [currentPromptIndex, currentImageIndex, prompts, type, navigate]);

useEffect(() => {
let touchStartX = 0;

const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === "ArrowRight" || event.key === "ArrowDown") {
navigateToNext();
} else if (event.key === "ArrowLeft" || event.key === "ArrowUp") {
navigateToPrevious();
switch (event.key) {
case 'ArrowRight':
case 'ArrowDown':
event.preventDefault();
navigateToNext();
break;
case 'ArrowLeft':
case 'ArrowUp':
event.preventDefault();
navigateToPrevious();
break;
}
};

const handleScroll = (event: WheelEvent) => {
const handleWheel = (event: WheelEvent) => {
event.preventDefault();
if (event.deltaY > 0) {
navigateToNext();
} else if (event.deltaY < 0) {
navigateToPrevious();
}
};

// Variables to store touch start and end coordinates
let touchStartX = 0;
let touchEndX = 0;

// Handle touch start
const handleTouchStart = (event: TouchEvent) => {
touchStartX = event.touches[0].clientX;
};

// Handle touch move
const handleTouchMove = (event: TouchEvent) => {
touchEndX = event.touches[0].clientX;
};

// Handle touch end
const handleTouchEnd = () => {
if (touchStartX - touchEndX > 50) {
// Swipe left to navigate to the next prompt
const handleTouchEnd = (event: TouchEvent) => {
const touchEndX = event.changedTouches[0].clientX;
const SWIPE_THRESHOLD = 50;

if (touchStartX - touchEndX > SWIPE_THRESHOLD) {
navigateToNext();
} else if (touchEndX - touchStartX > 50) {
// Swipe right to navigate to the previous prompt
} else if (touchEndX - touchStartX > SWIPE_THRESHOLD) {
navigateToPrevious();
}
};

window.addEventListener("keydown", handleKeyDown);
window.addEventListener("wheel", handleScroll);

// Add touch event listeners for mobile support
window.addEventListener("touchstart", handleTouchStart);
window.addEventListener("touchmove", handleTouchMove);
window.addEventListener("touchend", handleTouchEnd);
// Add event listeners with passive: false for better scroll performance
window.addEventListener('keydown', handleKeyDown);
window.addEventListener('wheel', handleWheel, { passive: false });
window.addEventListener('touchstart', handleTouchStart, { passive: true });
window.addEventListener('touchend', handleTouchEnd, { passive: true });

return () => {
window.removeEventListener("keydown", handleKeyDown);
window.removeEventListener("wheel", handleScroll);

// Remove touch event listeners
window.removeEventListener("touchstart", handleTouchStart);
window.removeEventListener("touchmove", handleTouchMove);
window.removeEventListener("touchend", handleTouchEnd);
window.removeEventListener('keydown', handleKeyDown);
window.removeEventListener('wheel', handleWheel);
window.removeEventListener('touchstart', handleTouchStart);
window.removeEventListener('touchend', handleTouchEnd);
};
}, [currentPromptIndex, currentImageIndex, prompts]);
}, [navigateToNext, navigateToPrevious]);

// Return the navigation functions for potential external use
return { navigateToNext, navigateToPrevious };
};
101 changes: 85 additions & 16 deletions src/hooks/usePrompts.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import { useEffect } from "react";
import { useEffect, useCallback, useRef } from "react";
import { useStore } from "@/store/promptStore";
import { fetchGalleryPrompts, fetchUserPrompts } from "@/api/prompts";
import type { Prompt } from "../types";

const usePrompts = (isGallery: boolean) => {
export type PromptType = "user" | "gallery" | null;

interface UsePromptsResult {
galleryPrompts: Prompt[];
userPrompts: Prompt[];
fetchMoreData: (isGallery: boolean) => Promise<boolean>;
findPrompt: (promptId: number) => Promise<PromptType>;
}

const usePrompts = (promptId?: number): UsePromptsResult => {
const {
galleryPrompts,
userPrompts,
Expand All @@ -13,14 +23,15 @@ const usePrompts = (isGallery: boolean) => {
addUserPrompts,
} = useStore();

const fetchMoreData = async () => {
const initialized = useRef(false);

const fetchMoreData = useCallback(async (isGallery: boolean): Promise<boolean> => {
try {
const prompts = isGallery
? await fetchGalleryPrompts(galleryOffset, limit)
: await fetchUserPrompts(userOffset, limit);

if (prompts.length === 0) {
console.log("i ran")
? await fetchGalleryPrompts(galleryOffset, limit)
: await fetchUserPrompts(userOffset, limit);

if (!prompts?.length) {
return false;
}

Expand All @@ -34,19 +45,77 @@ const usePrompts = (isGallery: boolean) => {
console.error(`Error fetching ${isGallery ? "gallery" : "user"} prompts:`, error);
return false;
}
};
}, [galleryOffset, userOffset, limit, addGalleryPrompts, addUserPrompts]);

useEffect(() => {
if (isGallery && galleryPrompts.length === 0) {
fetchMoreData();
} else if (!isGallery && userPrompts.length === 0) {
fetchMoreData();
const findPrompt = useCallback(async (searchPromptId: number): Promise<PromptType> => {
const checkPrompts = () => {
const foundInUser = userPrompts.some((prompt) => prompt.id === searchPromptId);
const foundInGallery = galleryPrompts.some((prompt) => prompt.id === searchPromptId);
return { foundInUser, foundInGallery };
};

let { foundInUser, foundInGallery } = checkPrompts();
if (foundInUser) return "user";
if (foundInGallery) return "gallery";

let userExhausted = false;
let galleryExhausted = false;
let attemptCount = 0; // Limit attempts to avoid infinite loop

while (!userExhausted || !galleryExhausted) {
attemptCount += 1;
if (attemptCount > 5) break; // Limit fetch attempts

const userPromise: Promise<boolean> = !userExhausted
? fetchMoreData(false)
: Promise.resolve(false);

const galleryPromise: Promise<boolean> = !galleryExhausted
? fetchMoreData(true)
: Promise.resolve(false);

const [hasMoreUserPrompts, hasMoreGalleryPrompts] = await Promise.all([
userPromise,
galleryPromise,
]);

({ foundInUser, foundInGallery } = checkPrompts());
if (foundInUser) return "user";
if (foundInGallery) return "gallery";

userExhausted = !hasMoreUserPrompts;
galleryExhausted = !hasMoreGalleryPrompts;
}
}, [isGallery, galleryPrompts.length, userPrompts.length, galleryOffset, userOffset]);

return null;
}, [userPrompts, galleryPrompts, fetchMoreData]);


useEffect(() => {
const initializePrompts = async () => {
if (initialized.current) return;
initialized.current = true;

if (promptId) {
await findPrompt(promptId);
}

if (galleryPrompts.length === 0) {
await fetchMoreData(true);
}
if (userPrompts.length === 0) {
await fetchMoreData(false);
}
};

initializePrompts();
}, [promptId, fetchMoreData, findPrompt]);

return {
prompts: isGallery ? galleryPrompts : userPrompts,
galleryPrompts,
userPrompts,
fetchMoreData,
findPrompt,
};
};

Expand Down
Loading

0 comments on commit ff04d26

Please sign in to comment.