From f2bd76487e473dae44fde3e43fd07dbce29e0105 Mon Sep 17 00:00:00 2001 From: Damien Ostler Date: Sat, 1 Jun 2024 06:21:59 -0400 Subject: [PATCH] feat: tags * fix: navigation bar code is minimized, started seperating out components and refactoring them * feat: search components * Update search.tsx * fix: autofocus * fix: tags and search --- app/api/galleries/[id]/images/route.ts | 4 +- app/gallery/page.tsx | 180 +----------- app/gallery/page_old.tsx | 131 +++++++++ app/layout.tsx | 2 +- components/NavigationBar.tsx | 150 ---------- components/neroshitron/galleries.tsx | 61 +++++ components/{ui => neroshitron}/gallery.tsx | 256 ++++++++---------- .../{ui => neroshitron}/gallery_thumbnail.tsx | 0 components/neroshitron/navigation_bar.tsx | 110 ++++++++ components/neroshitron/search.tsx | 42 +++ components/neroshitron/search_input.tsx | 109 ++++++++ components/neroshitron/tag_pill.tsx | 19 ++ components/neroshitron/tag_selector.tsx | 60 ++++ components/ui/search.tsx | 96 +++++++ components/ui/tag_selector.tsx | 27 ++ 15 files changed, 781 insertions(+), 466 deletions(-) create mode 100644 app/gallery/page_old.tsx delete mode 100644 components/NavigationBar.tsx create mode 100644 components/neroshitron/galleries.tsx rename components/{ui => neroshitron}/gallery.tsx (68%) rename components/{ui => neroshitron}/gallery_thumbnail.tsx (100%) create mode 100644 components/neroshitron/navigation_bar.tsx create mode 100644 components/neroshitron/search.tsx create mode 100644 components/neroshitron/search_input.tsx create mode 100644 components/neroshitron/tag_pill.tsx create mode 100644 components/neroshitron/tag_selector.tsx create mode 100644 components/ui/search.tsx create mode 100644 components/ui/tag_selector.tsx diff --git a/app/api/galleries/[id]/images/route.ts b/app/api/galleries/[id]/images/route.ts index 7821de4..9858114 100644 --- a/app/api/galleries/[id]/images/route.ts +++ b/app/api/galleries/[id]/images/route.ts @@ -54,8 +54,8 @@ export async function GET( .select('*') .eq('user_id', userId) .single(); - console.log(subscription) - console.log(gallery.tier) + //console.log(subscription) + //console.log(gallery.tier) switch(gallery.tier){ case "Tier 3": if(subscription?.tier!="Tier 3"){ diff --git a/app/gallery/page.tsx b/app/gallery/page.tsx index 7ebe962..f1c6255 100644 --- a/app/gallery/page.tsx +++ b/app/gallery/page.tsx @@ -1,85 +1,22 @@ "use client"; import { createClient } from "@/utils/supabase/client"; -import { redirect } from "next/navigation"; -import GalleryThumbnail from "@/components/ui/gallery_thumbnail"; - - import React, { useState, useEffect } from 'react'; -import { User } from "@supabase/supabase-js"; -import Gallery from "@/components/ui/gallery"; +import Search from "@/components/neroshitron/search"; function PageComponent() { const supabase = createClient(); - const [showNSFW, setShowNSFW] = useState(true); - const [randomIds, setRandomIds] = useState([]); // replace any with your gallery type - const [isOpen, setIsOpen] = useState(false); - const [galleries, setGalleries] = useState([]); // replace any with your gallery type - const [user, setUser] = useState(null); - const [loading, setLoading] = useState(true); - const [selectedGallery, setSelectedGallery] = useState(null); - const [tags, setTags] = useState([]); - const [search, setSearch] = useState(''); - const [galleryColumns, setColumns] = useState(0); - const [selectedTags, setSelectedTags] = useState([]); - const generateRandomString = function (length: number) { - let result = ''; - let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - let charactersLength = characters.length; - for (let i = 0; i < length; i++) { - result += characters.charAt(Math.floor(Math.random() * charactersLength)); - } - return result; - } - - const selectGallery = (gallery: string, columns: number) => { - setRandomIds([generateRandomString(3), generateRandomString(3), generateRandomString(3), generateRandomString(3)]); - setSelectedGallery(gallery); - setColumns(columns); - setIsOpen(true); - }; - - const closeGallery = () => { - setSelectedGallery(null); - setColumns(0); - setIsOpen(false); - } - const getData = async () => { - let { data: { user } } = await supabase.auth.getUser(); - const galleriesResponse = await fetch(`/api/galleries?search=` + search + '&nsfw=' + showNSFW, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ tags: selectedTags }) - }); - //console.log(galleriesResponse) - const galleriesData = await galleriesResponse.json(); - const tagsResponse = await fetch(`/api/galleries/tags`); - const tagsData = await tagsResponse.json(); - setGalleries(galleriesData); - setTags(tagsData); - setUser(user); - setLoading(false); } + useEffect(() => { getData(); + }, []); - }, [selectedTags, search, showNSFW]); - - const handleTagClick = (tag: number) => { - if (selectedTags.includes(tag)) { - setSelectedTags(selectedTags.filter((selectedTag) => selectedTag !== tag)); - } else { - setSelectedTags([...selectedTags, tag]); - } - //console.log(selectedTags) - }; return ( -
+
Background
- - - {/* - THIS IS THE SEARCH BAR AND TAGS SECTION - THIS IS THE SEARCH BAR AND TAGS SECTION - THIS IS THE SEARCH BAR AND TAGS SECTION - THIS IS THE SEARCH BAR AND TAGS SECTION - THIS IS THE SEARCH BAR AND TAGS SECTION - THIS IS THE SEARCH BAR AND TAGS SECTION - */} -
- {(tags.length > 0) ? ( -
-
- setSearch(e.target.value)} - style={{ - animation: 'expandFromLeft 2s ease-out forwards', - paddingRight: '2rem', // make room for the checkbox - }} - /> - -
- {(tags.length > 0) ? ( - - ) : ( -
-

Loading Tags...

-
- )} -
- ) : ( -
- )} -
- - - - - - {/* - These are the thumbnails for the gallery below the search bar - */} -
- {galleries && galleries.map((gallery, index) => ( -
- -
- ))} -
- {isOpen ? ( - <> - {/* - This is the modal for holding the gallery - */} - - - ) : null} +
); } diff --git a/app/gallery/page_old.tsx b/app/gallery/page_old.tsx new file mode 100644 index 0000000..510cdcf --- /dev/null +++ b/app/gallery/page_old.tsx @@ -0,0 +1,131 @@ +"use client"; +import { createClient } from "@/utils/supabase/client"; +import { redirect } from "next/navigation"; +import GalleryThumbnail from "@/components/ui/gallery_thumbnail"; + + +import React, { useState, useEffect } from 'react'; +import { User } from "@supabase/supabase-js"; +import Gallery from "@/components/ui/gallery"; +import Search from "@/components/ui/search"; + +function PageComponent() { + + const supabase = createClient(); + + const [showNSFW, setShowNSFW] = useState(true); + const [randomIds, setRandomIds] = useState([]); // replace any with your gallery type + const [isOpen, setIsOpen] = useState(false); + const [galleries, setGalleries] = useState([]); // replace any with your gallery type + const [user, setUser] = useState(null); + const [loading, setLoading] = useState(true); + const [selectedGallery, setSelectedGallery] = useState(null); + const [search, setSearch] = useState(''); + const [galleryColumns, setColumns] = useState(0); + const [selectedTags, setSelectedTags] = useState([]); + const generateRandomString = function (length: number) { + let result = ''; + let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let charactersLength = characters.length; + for (let i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; + } + + const selectGallery = (gallery: string, columns: number) => { + setRandomIds([generateRandomString(3), generateRandomString(3), generateRandomString(3), generateRandomString(3)]); + setSelectedGallery(gallery); + setColumns(columns); + setIsOpen(true); + }; + + const closeGallery = () => { + setSelectedGallery(null); + setColumns(0); + setIsOpen(false); + } + + const getData = async () => { + let { data: { user } } = await supabase.auth.getUser(); + const galleriesResponse = await fetch(`/api/galleries?search=` + search + '&nsfw=' + showNSFW, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ tags: selectedTags }) + }); + const galleriesData = await galleriesResponse.json(); + setGalleries(galleriesData); + setUser(user); + setLoading(false); + } + + useEffect(() => { + getData(); + }, [selectedTags, search, showNSFW]); + + + return ( +
+
+ Background +
+ + + + + {/* + These are the thumbnails for the gallery below the search bar + */} +
+ {galleries && galleries.map((gallery, index) => ( +
+ +
+ ))} +
+ {isOpen ? ( + <> + {/* + This is the modal for holding the gallery + */} + + + ) : null} +
+ ); +} + +export default PageComponent; \ No newline at end of file diff --git a/app/layout.tsx b/app/layout.tsx index 999c7e1..0a4c0b6 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,6 +1,6 @@ import { GeistSans } from "geist/font/sans"; import "./globals.css"; -import NavigationBar from "@/components/NavigationBar"; +import NavigationBar from "@/components/neroshitron/navigation_bar"; import { SpeedInsights } from "@vercel/speed-insights/next" import { Analytics } from "@vercel/analytics/react" const defaultUrl = process.env.VERCEL_URL diff --git a/components/NavigationBar.tsx b/components/NavigationBar.tsx deleted file mode 100644 index 834b9a2..0000000 --- a/components/NavigationBar.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import { createClient } from "@/utils/supabase/server"; -import Link from "next/link"; -import { redirect, useRouter } from "next/navigation"; -import crypto from 'crypto'; -import { headers } from "next/headers"; - - -export default async function AuthButton() { - const supabase = createClient(); - - const { - data: { user }, - } = await supabase.auth.getUser(); - - const signOut = async () => { - "use server"; - - const supabase = createClient(); - await supabase.auth.signOut(); - return redirect("/login"); - }; - - - - // ... - - const heads = headers() - const currentPage = heads.get('x-path') - if(user){ - let email = user.email; - if(email != null){ - const emailHash = crypto.createHash('md5').update(email.trim().toLowerCase()).digest('hex'); - const gravatarUrl = `https://www.gravatar.com/avatar/${emailHash}`; - return( -
- -
) - } - } - else{ - return(
- -
) - } -} diff --git a/components/neroshitron/galleries.tsx b/components/neroshitron/galleries.tsx new file mode 100644 index 0000000..78c87a4 --- /dev/null +++ b/components/neroshitron/galleries.tsx @@ -0,0 +1,61 @@ +"use client;" +import React, { useState, useEffect } from 'react'; +import GalleryThumbnail from './gallery_thumbnail'; + +interface TagProps { + nsfw: boolean; + tags: string[]; + search: string; +} + +const Galleries = ({ nsfw, tags, search }:TagProps) => { + + const [galleries, setGalleries] = useState([]); + const [nsfwState, setNsfwState] = useState(nsfw); + const [tagsState, setTagsState] = useState(tags); + const [searchState, setSearchState] = useState(search); + + const [selectedGallery, setSelectedGallery] = useState(null); + + const selectGallery = (gallery: string) => { + setSelectedGallery(gallery); + }; + + console.log(tags) + + const getData = async () => { + const galleriesResponse = await fetch(`/api/galleries?search=` + searchState + '&nsfw=' + nsfwState, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ tags: tagsState }) + }); + const galleriesData = await galleriesResponse.json(); + setGalleries(galleriesData); + } + + useEffect(() => { + getData(); + }, [tagsState]); + + return ( +
+ {galleries && galleries.map((gallery, index) => ( + + ))} +
+ ); +}; + +export default Galleries; \ No newline at end of file diff --git a/components/ui/gallery.tsx b/components/neroshitron/gallery.tsx similarity index 68% rename from components/ui/gallery.tsx rename to components/neroshitron/gallery.tsx index 2e92548..c2d4650 100644 --- a/components/ui/gallery.tsx +++ b/components/neroshitron/gallery.tsx @@ -12,18 +12,11 @@ interface GalleryProps { const Gallery = ({ id, columns, closeMenu }: GalleryProps) => { - const [isSingle, setIsSingle] = useState(false); - const [loaded, setLoaded] = useState({}) const [selectedImage, setSelectedImage] = useState(null); const [images, setImages] = useState([]); const [galleryId, setGalleryId] = useState(id as string); const [currentIndex, setCurrentIndex] = useState(0); - - const getData = async () => { - const thumbnailResponse = await fetch('/api/galleries/' + String(galleryId) + '/images'); - const thumbnailUrl = await thumbnailResponse.json() as string[]; - setImages(thumbnailUrl); - } + const panZoomRef = useRef(null); const next = () => { if (currentIndex < images.length - 1) { @@ -41,6 +34,76 @@ const Gallery = ({ id, columns, closeMenu }: GalleryProps) => { } } + const handleDownload = (image: string) => { + const link = document.createElement('a'); + link.href = image; + link.download = 'image.jpg'; // or any other filename + link.style.display = 'none'; + + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + }; + + const getData = async () => { + const thumbnailResponse = await fetch('/api/galleries/' + String(galleryId) + '/images'); + const thumbnailUrl = await thumbnailResponse.json() as string[]; + setImages(thumbnailUrl); + } + + useEffect(() => { + getData(); + const handleKeyDown = (event: KeyboardEvent) => { + switch (event.key) { + case 'ArrowLeft': + case 'a': + case 'A': + previous(); + break; + case 'ArrowRight': + case 'd': + case 'D': + next(); + break; + case 'Escape': + close(); + break; + default: + break; + } + }; + + setSelectedImage(images[currentIndex]); + window.addEventListener('keydown', handleKeyDown); + + // Clean up the event listener when the component is unmounted + return () => { + window.removeEventListener('keydown', handleKeyDown); + }; + + }, [selectedImage, currentIndex]); + + const handleClick = (image: string) => { + setSelectedImage(image); + setCurrentIndex(images.indexOf(image)); + }; + + const resetPanZoom = (event: any) => { + if (panZoomRef.current && event.target.id != "image-container") { + panZoomRef.current.autoCenter(); + } + }; + + const close = () => { + if (selectedImage != null) { + setSelectedImage(null); + setImages([]); + } + else { + closeMenu(); + } + }; + const renderButtons = () => { return (
@@ -85,142 +148,59 @@ const Gallery = ({ id, columns, closeMenu }: GalleryProps) => {
); }; - const handleDownload = (image: string) => { - const link = document.createElement('a'); - link.href = image; - link.download = 'image.jpg'; // or any other filename - link.style.display = 'none'; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - }; - useEffect(() => { - getData(); - const handleKeyDown = (event: KeyboardEvent) => { - switch (event.key) { - case 'ArrowLeft': - case 'a': - case 'A': - previous(); - break; - case 'ArrowRight': - case 'd': - case 'D': - next(); - break; - case 'Escape': - close(); - break; - default: - break; - } - }; - - setSelectedImage(images[currentIndex]); - window.addEventListener('keydown', handleKeyDown); - - // Clean up the event listener when the component is unmounted - return () => { - window.removeEventListener('keydown', handleKeyDown); - }; - - }, [selectedImage,currentIndex]); - - const handleClick = (image: string) => { - setSelectedImage(image); - setCurrentIndex(images.indexOf(image)); - }; - - const open = () => { - if (selectedImage === null) return; - //console.log(selectedImage) - let base64Image = selectedImage.split(';base64,').pop(); - if (!base64Image) return; - let blob = new Blob([Uint8Array.from(atob(base64Image), c => c.charCodeAt(0))], { type: 'image/jpeg' }); // adjust the type as needed - let url = URL.createObjectURL(blob); - window.open(url, '_blank'); - } - - const panZoomRef = useRef(null); - - const resetPanZoom = (event: any) => { - if (panZoomRef.current && event.target.id != "image-container") { - panZoomRef.current.autoCenter(); - } - }; - - const close = () => { - if (selectedImage != null) { - setSelectedImage(null); - setImages([]); - } - else { - closeMenu(); - } - } - - const breakpointColumnsObj = { - default: 3 - }; return (
-
-
- {renderButtons()} +
+
+ {renderButtons()}
- {selectedImage ? ( - <> - -{/* -
resetPanZoom()} className='w-full h-full z-10'> -
*/} -
- - -
-
- - ) : ( -
- - - {images - .filter((img) => img !== selectedImage) - .map((image, index) => ( - handleClick(image)} - className={`animate-in animate-once animate-duration-1000 animate-ease-out animate-reverse hover:scale-105 p-2 cursor-pointer my-2 transition-all opacity-100 duration-500 ease-in-out transform`} - /> - ))} - -
+ {selectedImage ? ( <> + +
+ + +
+
-
- )} -
+ ) : ( +
+ + + {images + .filter((img) => img !== selectedImage) + .map((image, index) => ( + handleClick(image)} + className={`animate-in animate-once animate-duration-1000 animate-ease-out animate-reverse hover:scale-105 p-2 cursor-pointer my-2 transition-all opacity-100 duration-500 ease-in-out transform`} + /> + ))} + +
+
+ )} +
); } diff --git a/components/ui/gallery_thumbnail.tsx b/components/neroshitron/gallery_thumbnail.tsx similarity index 100% rename from components/ui/gallery_thumbnail.tsx rename to components/neroshitron/gallery_thumbnail.tsx diff --git a/components/neroshitron/navigation_bar.tsx b/components/neroshitron/navigation_bar.tsx new file mode 100644 index 0000000..a67c5e5 --- /dev/null +++ b/components/neroshitron/navigation_bar.tsx @@ -0,0 +1,110 @@ +import { createClient } from "@/utils/supabase/server"; +import Link from "next/link"; +import { redirect, useRouter } from "next/navigation"; +import crypto from 'crypto'; +import { headers } from "next/headers"; + + +export default async function AuthButton() { + const supabase = createClient(); + + const { + data: { user }, + } = await supabase.auth.getUser(); + + const signOut = async () => { + "use server"; + + const supabase = createClient(); + await supabase.auth.signOut(); + return redirect("/login"); + }; + + const heads = headers() + const currentPage = heads.get('x-path') + + const getGravatarUrl = () => { + if(user==null) + { + return; + } + let email = user.email; + if(email != null){ + const emailHash = crypto.createHash('md5').update(email.trim().toLowerCase()).digest('hex'); + return `https://www.gravatar.com/avatar/${emailHash}`; + } + return ""; + } + const url = getGravatarUrl(); + return( +
+ +
) +} diff --git a/components/neroshitron/search.tsx b/components/neroshitron/search.tsx new file mode 100644 index 0000000..20976cd --- /dev/null +++ b/components/neroshitron/search.tsx @@ -0,0 +1,42 @@ +"use client;" +import React, { useState, useEffect } from 'react'; +import SearchInput from '@/components/neroshitron/search_input'; +import Galleries from './galleries'; + +interface SearchProps { } + +const Search = ({ }:SearchProps) => { + const [tags, setTags] = useState([]); + const [search, setSearch] = useState(''); + const [nsfw, setNsfw] = useState(false); + + const getData = async () => { + } + + useEffect(() => { + getData(); + }, [search]); + useEffect(() => { + getData(); + }, [nsfw]); + useEffect(() => { + getData(); + }, [tags]); + useEffect(() => { + getData(); + }, []); + + return ( + <> + +
+
+ {setSearch(search)}} nsfwChanged={(nsfw)=>{setNsfw(nsfw)}} tagsChanged={(tags)=>{setTags(tags);}} /> +
+ +
+ + ); +}; + +export default Search; \ No newline at end of file diff --git a/components/neroshitron/search_input.tsx b/components/neroshitron/search_input.tsx new file mode 100644 index 0000000..1ba173f --- /dev/null +++ b/components/neroshitron/search_input.tsx @@ -0,0 +1,109 @@ +"use client;" +import React, { useState, useEffect, useRef,forwardRef } from 'react'; +import TagSelector from '../neroshitron/tag_selector'; + +interface SearchInputProps { + tagsChanged: (tags: string[]) => void; + searchChanged: (search: string) => void; + nsfwChanged: (nsfw: boolean) => void; +} + +const SearchInput = ({ tagsChanged, searchChanged, nsfwChanged}: SearchInputProps) => { + + const [search, setSearch] = useState(''); + const [tagSearch, setTagSearch] = useState(''); + const [nsfw, setNsfw] = useState(false); + const [selectedTags, setSelectedTags] = useState([]); + const [selectingTags, setSelectingTags] = useState(false); + const tagSelectorRef = React.useRef(null); + const [tags, setTags] = useState([]); + + + const updateTags = (newTags: string[]) => { + setSelectedTags(newTags) + } + + const onTagsClosed = (tags:string[]) => { + setSelectingTags(false); + } + + const openTags = () => { + setSelectingTags(true); + if(selectingTags){ + onTagsClosed(selectedTags); + } + } + + const getData = async () => { + const tagsResponse = await fetch(`/api/galleries/tags`); + const tagsData = await tagsResponse.json(); + setTags(tagsData); + } + useEffect(() => { + searchChanged(search); + }, [search]); + useEffect(() => { + tagsChanged(selectedTags); + }, [selectedTags]); + useEffect(() => { + nsfwChanged(nsfw); + }, [nsfw]); + useEffect(() => { + getData(); + }, []); + + + return ( + <> +
+
+
+ + {(selectingTags) ? ( + <> + setTagSearch(e.target.value)} className="rounded-l-md h-16 bg-gray-100 text-grey-darker py-2 font-normal text-grey-darkest border border-gray-100 font-bold w-full py-1 px-2 outline-none text-lg text-gray-600" type="text" placeholder="Looking for specific tag?" /> + + + + + + + ) + :( + <> + setSearch(e.target.value)} className="rounded-l-md h-16 bg-gray-100 text-grey-darker py-2 font-normal text-grey-darkest border border-gray-100 font-bold w-full py-1 px-2 outline-none text-lg text-gray-600" type="text" placeholder="Looking for a specific collection?" /> + + + + + + + + + + )} +
+
+
+ {(selectingTags) && + { updateTags(newTags) }} selectedTagsInput={selectedTags} ref={tagSelectorRef} />} + + ); +}; + +export default SearchInput; \ No newline at end of file diff --git a/components/neroshitron/tag_pill.tsx b/components/neroshitron/tag_pill.tsx new file mode 100644 index 0000000..16265a7 --- /dev/null +++ b/components/neroshitron/tag_pill.tsx @@ -0,0 +1,19 @@ +"use client;" +import React, { useState, useEffect } from 'react'; + +interface TagProps { onTagClicked: (tag: string ) => void, selected:boolean, tag:string } + +const Tag = ({ onTagClicked, selected, tag, }:TagProps) => { + + return ( + + ); +}; + +export default Tag; \ No newline at end of file diff --git a/components/neroshitron/tag_selector.tsx b/components/neroshitron/tag_selector.tsx new file mode 100644 index 0000000..53dbed6 --- /dev/null +++ b/components/neroshitron/tag_selector.tsx @@ -0,0 +1,60 @@ +"use client;" +import React, { forwardRef, useState, useEffect } from 'react'; +import Tag from './tag_pill'; + +interface TagSelectorProps { + tagsInput: any[], + tagSearch: string, + selectedTagsInput: string[], + tagsChanged: (tags: string[]) => void + } + +const TagSelector = forwardRef void }>((props, ref) => { + + const [tags, setTags] = useState(props.tagsInput); + const [tagSearch, setTagSearch] = useState(props.tagSearch); + const [selectedTags, setSelectedTags] = useState(props.selectedTagsInput); + + console.log() + + const handleTag = (tag: string) => { + if (selectedTags.includes(tag)) { + setSelectedTags(selectedTags.filter(t => t !== tag)); + } else { + setSelectedTags([...selectedTags, tag]); + } + setTags(selectedTags); + }; + + + const getData = async () => { + } + + useEffect(() => { + props.tagsChanged(selectedTags); + getData(); + }, [selectedTags,tagSearch,tags]); + + const generateRandomString = () => { + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let result = ''; + for (let i = 0; i < 10; i++) { + result += characters.charAt(Math.floor(Math.random() * characters.length)); + } + return result; + }; + return ( + (tags.length > 0)? ( +
+
+ {props.tagsInput.map((tag: any) => ( + (tagSearch === '' || tag.name.toLowerCase().includes(tagSearch.toLowerCase())) && // Updated condition + handleTag(tag)} /> + ))} +
+
+ ):(<>) + ); +}); + +export default TagSelector; \ No newline at end of file diff --git a/components/ui/search.tsx b/components/ui/search.tsx new file mode 100644 index 0000000..d645081 --- /dev/null +++ b/components/ui/search.tsx @@ -0,0 +1,96 @@ +"use client;" +import React, { useState, useEffect } from 'react'; +import TagSelector from './tag_selector'; + +interface SearchProps { } + +const Search = ({ }:SearchProps) => { + const [nsfw, setNsfw] = useState(false); + const [tags, setTags] = useState([]); + const [search, setSearch] = useState(''); + const [selectedTags, setSelectedTagsState] = useState([]); + const [selectingTags, setSelectingTags] = useState(false); + + const getNsfw = () => { + return nsfw; + }; + + const getTags = () => { + return tags; + }; + + const getSearch = () => { + return search; + }; + + const getSelectedTags = () => { + return selectedTags; + }; + + const getData = async () => { + const tagsResponse = await fetch(`/api/galleries/tags`); + const tagsData = await tagsResponse.json(); + setTags(tagsData); + } + + useEffect(() => { + getData(); + }, [selectingTags]); + + return ( + <> +
+ {(tags.length > 0) ? ( +
+
+ setSearch(e.target.value)} + /> +
+ +
+
+ +
+ ) : ( +
+ )} + {(selectingTags) ??( + + )} +
+ + ); +}; + +export default Search; \ No newline at end of file diff --git a/components/ui/tag_selector.tsx b/components/ui/tag_selector.tsx new file mode 100644 index 0000000..c65ece3 --- /dev/null +++ b/components/ui/tag_selector.tsx @@ -0,0 +1,27 @@ +"use client;" +import React, { useState, useEffect } from 'react'; + +interface SearchProps { } + +const TagSelector = ({ }:SearchProps) => { + + + const getData = async () => { + } + + useEffect(() => { + getData(); + }, []); + + return ( + + ); +}; + +export default TagSelector; \ No newline at end of file