mirror of
https://github.com/D4M13N-D3V/neroshitron.git
synced 2025-08-01 11:39:19 +00:00
fix: tags and search
This commit is contained in:
parent
38eb7530d3
commit
fc831d6340
@ -1,12 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { createClient } from "@/utils/supabase/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 React, { useState, useEffect } from 'react';
|
||||||
import { User } from "@supabase/supabase-js";
|
|
||||||
import Gallery from "@/components/ui/gallery";
|
|
||||||
import Search from "@/components/neroshitron/search";
|
import Search from "@/components/neroshitron/search";
|
||||||
|
|
||||||
function PageComponent() {
|
function PageComponent() {
|
||||||
@ -23,6 +17,13 @@ function PageComponent() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
|
<div className="fixed w-full h-full overflow-hidden z-0 animate-fade-left animate-fade-left animate-once animate-duration-[2000ms] animate-normal animate-fill-forwards">
|
||||||
|
<img
|
||||||
|
src="gallery_girl.png"
|
||||||
|
className="float-right object-cover h-screen w-full lg:w-5/6 xl:w-3/6 opacity-50 overflow-hidden"
|
||||||
|
alt="Background"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<Search/>
|
<Search/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
61
components/neroshitron/galleries.tsx
Normal file
61
components/neroshitron/galleries.tsx
Normal file
@ -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<boolean>(nsfw);
|
||||||
|
const [tagsState, setTagsState] = useState<string[]>(tags);
|
||||||
|
const [searchState, setSearchState] = useState<string>(search);
|
||||||
|
|
||||||
|
const [selectedGallery, setSelectedGallery] = useState<string | null>(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 (
|
||||||
|
<div className="absolute inset-0 mx-auto ml-16 md:ml-0 pt-48 p-4 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-y-60 gap-x-4 animate-in overflow-y-scroll no-scrollbar z-0">
|
||||||
|
{galleries && galleries.map((gallery, index) => (
|
||||||
|
<GalleryThumbnail
|
||||||
|
key={gallery.name + " " + nsfw}
|
||||||
|
id={gallery.name}
|
||||||
|
title={gallery.name}
|
||||||
|
tags={gallery.tags}
|
||||||
|
columns={gallery.columns}
|
||||||
|
showNsfw={nsfw}
|
||||||
|
subscription={gallery.tier as string}
|
||||||
|
onSelect={selectGallery}
|
||||||
|
nsfw={gallery.nsfw}
|
||||||
|
></GalleryThumbnail>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Galleries;
|
@ -1,7 +1,7 @@
|
|||||||
"use client;"
|
"use client;"
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import Tag from "@/components/neroshitron/tag_pill";
|
|
||||||
import SearchInput from '@/components/neroshitron/search_input';
|
import SearchInput from '@/components/neroshitron/search_input';
|
||||||
|
import Galleries from './galleries';
|
||||||
|
|
||||||
interface SearchProps { }
|
interface SearchProps { }
|
||||||
|
|
||||||
@ -13,16 +13,27 @@ const Search = ({ }:SearchProps) => {
|
|||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getData();
|
||||||
|
}, [search]);
|
||||||
|
useEffect(() => {
|
||||||
|
getData();
|
||||||
|
}, [nsfw]);
|
||||||
|
useEffect(() => {
|
||||||
|
getData();
|
||||||
|
}, [tags]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getData();
|
getData();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<Galleries key={search+"-"+tags.length+"-"+nsfw} search={search} nsfw={nsfw} tags={tags} />
|
||||||
<section className="fixed flex items-center w-full p-8 pt-20 opacity-90 animate-in animate-once animate-duration-500">
|
<section className="fixed flex items-center w-full p-8 pt-20 opacity-90 animate-in animate-once animate-duration-500">
|
||||||
<div className="container mx-auto py-8">
|
<div className="container mx-auto py-8">
|
||||||
<SearchInput searchChanged={(search)=>{setSearch(search)}} nsfwChanged={(nsfw)=>{setNsfw(nsfw)}} tagsChanged={(tags)=>{setTags(tags);}} />
|
<SearchInput searchChanged={(search)=>{setSearch(search)}} nsfwChanged={(nsfw)=>{setNsfw(nsfw)}} tagsChanged={(tags)=>{setTags(tags);}} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
"use client;"
|
"use client;"
|
||||||
import React, { useState, useEffect, useRef,forwardRef } from 'react';
|
import React, { useState, useEffect, useRef,forwardRef } from 'react';
|
||||||
import { on } from 'events';
|
|
||||||
import TagSelector from '../neroshitron/tag_selector';
|
import TagSelector from '../neroshitron/tag_selector';
|
||||||
|
|
||||||
interface SearchInputProps {
|
interface SearchInputProps {
|
||||||
@ -12,15 +11,16 @@ interface SearchInputProps {
|
|||||||
const SearchInput = ({ tagsChanged, searchChanged, nsfwChanged}: SearchInputProps) => {
|
const SearchInput = ({ tagsChanged, searchChanged, nsfwChanged}: SearchInputProps) => {
|
||||||
|
|
||||||
const [search, setSearch] = useState<string>('');
|
const [search, setSearch] = useState<string>('');
|
||||||
|
const [tagSearch, setTagSearch] = useState<string>('');
|
||||||
const [nsfw, setNsfw] = useState<boolean>(false);
|
const [nsfw, setNsfw] = useState<boolean>(false);
|
||||||
const [selectedTags, setSelectedTags] = useState<string[]>([]);
|
const [selectedTags, setSelectedTags] = useState<string[]>([]);
|
||||||
const [selectingTags, setSelectingTags] = useState<boolean>(false);
|
const [selectingTags, setSelectingTags] = useState<boolean>(false);
|
||||||
const tagSelectorRef = React.useRef(null);
|
const tagSelectorRef = React.useRef(null);
|
||||||
|
const [tags, setTags] = useState<any[]>([]);
|
||||||
|
|
||||||
|
|
||||||
const updateTags = (newTags: string[]) => {
|
const updateTags = (newTags: string[]) => {
|
||||||
setSelectedTags(newTags)
|
setSelectedTags(newTags)
|
||||||
tagsChanged(newTags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const onTagsClosed = (tags:string[]) => {
|
const onTagsClosed = (tags:string[]) => {
|
||||||
@ -35,32 +35,73 @@ const SearchInput = ({ tagsChanged, searchChanged, nsfwChanged}: SearchInputProp
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
setSelectedTags(selectedTags)
|
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(() => {
|
useEffect(() => {
|
||||||
getData();
|
getData();
|
||||||
}, [selectedTags]);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="relative md:w-full lg:w-1/2 mx-auto flex flex-col items-center justify-center">
|
<div className="relative md:w-full lg:w-1/2 mx-auto flex flex-col items-center justify-center z-10">
|
||||||
<div className="search-box mx-auto my-auto w-full sm:w-full md:w-full lg:w-3/4 xl:w-3/4">
|
<div className="search-box mx-auto my-auto w-full sm:w-full md:w-full lg:w-3/4 xl:w-3/4">
|
||||||
<div className="flex flex-row">
|
<div className="flex flex-row">
|
||||||
<input autoFocus value={search} onChange={(e) => 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?" />
|
|
||||||
<span className="flex items-center bg-gray-100 rounded rounded-l-none border-0 px-3 font-bold text-grey-100">
|
{(selectingTags) ? (
|
||||||
<button onClick={()=>{openTags()}} type="button" className={`bg-neroshi-blue-900 hover:bg-neroshi-blue-800 text-lg text-white font-bold py-3 px-6 rounded ${selectedTags.length === 0 ? 'animate-pulse animate-infinite animate-ease-out' : ''}`}>
|
<>
|
||||||
Tags
|
<input autoFocus value={tagSearch} onChange={(e) => 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?" />
|
||||||
<span className="ml-1 bg-neroshi-blue-300 text-white rounded-full px-2 py-1 text-xs absolute bottom-7 right-76 transform translate-x-1/2 -translate-y-1/2">{selectedTags.length}</span>
|
|
||||||
</button>
|
<span className="flex items-center bg-gray-100 rounded rounded-l-none border-0 px-3 font-bold text-grey-100">
|
||||||
<button onClick={()=>{ setNsfw(!nsfw) }} type="button" className={` w-full ${nsfw ? "bg-pink-900 hover:bg-pink-800":"bg-green-900 hover:bg-green-800"} text-lg text-white font-bold py-3 px-6 rounded ml-6`}>
|
<button key="back" onClick={()=>{openTags()}} type="button" className={`animate-in bg-pink-900 hover:bg-pink-800 text-lg text-white font-bold py-3 px-6 rounded`}>
|
||||||
{nsfw ? "NSFW" : "SFW"}
|
Back
|
||||||
</button>
|
</button>
|
||||||
</span>
|
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
:(
|
||||||
|
<>
|
||||||
|
<input autoFocus value={search} onChange={(e) => 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?" />
|
||||||
|
|
||||||
|
<span className="flex items-center bg-gray-100 rounded rounded-l-none border-0 px-3 font-bold text-grey-100">
|
||||||
|
<button key="tags" data-tip={selectedTags.join(',')} onClick={()=>{openTags()}} type="button" className={`bg-neroshi-blue-900 hover:bg-neroshi-blue-800 text-lg text-white font-bold py-3 px-6 rounded ${selectedTags.length == 0 ? 'animate-pulse animate-infinite animate-ease-out' : 'animate-in'}`}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="size-6">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" d="M9.568 3H5.25A2.25 2.25 0 0 0 3 5.25v4.318c0 .597.237 1.17.659 1.591l9.581 9.581c.699.699 1.78.872 2.607.33a18.095 18.095 0 0 0 5.223-5.223c.542-.827.369-1.908-.33-2.607L11.16 3.66A2.25 2.25 0 0 0 9.568 3Z" />
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" d="M6 6h.008v.008H6V6Z" />
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={()=>{ setNsfw(!nsfw) }}
|
||||||
|
type="button"
|
||||||
|
className={`animate-in text-sm w-28 text-lg text-white font-bold py-3 px-6 rounded ml-2 ${nsfw ? "bg-pink-900 hover:bg-pink-800":"bg-green-900 hover:bg-green-800"}`}
|
||||||
|
|
||||||
|
>
|
||||||
|
{nsfw ? "NSFW" : "SFW"}
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{(selectingTags) && <TagSelector tagsChanged={(newTags:string[])=>{ updateTags(newTags) }} selectedTagsInput={selectedTags} ref={tagSelectorRef} />}
|
{(selectingTags) &&
|
||||||
|
<TagSelector tagsInput={tags} key={tagSearch} tagSearch={tagSearch} tagsChanged={(newTags:string[])=>{ updateTags(newTags) }} selectedTagsInput={selectedTags} ref={tagSelectorRef} />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -7,9 +7,8 @@ const Tag = ({ onTagClicked, selected, tag, }:TagProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
key={tag+"-button"}
|
|
||||||
type="button"
|
type="button"
|
||||||
className={`animate-in w-full m-4 rounded-md no-underline text-white py-1 font-medium text-center ${selected ? 'hover:bg-pink-800 bg-pink-900' : 'hover:bg-neroshi-blue-700 bg-neroshi-blue-800'}`}
|
className={`animate-in w-full h-8 rounded-md no-underline text-sm text-white py-1 font-medium text-center ${selected ? 'hover:bg-pink-800 bg-pink-900' : 'hover:bg-pink-600 bg-neroshi-blue-800 border-neroshi-blue-900 border-2'}`}
|
||||||
onClick={() => onTagClicked(tag)}
|
onClick={() => onTagClicked(tag)}
|
||||||
>
|
>
|
||||||
{tag}
|
{tag}
|
||||||
|
@ -1,19 +1,21 @@
|
|||||||
"use client;"
|
"use client;"
|
||||||
import React, { useImperativeHandle,forwardRef, useState, useEffect } from 'react';
|
import React, { forwardRef, useState, useEffect } from 'react';
|
||||||
import { createClient } from "@/utils/supabase/client";
|
|
||||||
import Tag from './tag_pill';
|
import Tag from './tag_pill';
|
||||||
import Masonry from 'react-masonry-css';
|
|
||||||
|
|
||||||
interface TagSelectorProps {
|
interface TagSelectorProps {
|
||||||
|
tagsInput: any[],
|
||||||
|
tagSearch: string,
|
||||||
selectedTagsInput: string[],
|
selectedTagsInput: string[],
|
||||||
tagsChanged: (tags: string[]) => void
|
tagsChanged: (tags: string[]) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const TagSelector = forwardRef<TagSelectorProps, { selectedTagsInput: string[], tagsChanged: (tags: string[]) => void }>((props, ref) => {
|
const TagSelector = forwardRef<TagSelectorProps, {tagsInput:any[], tagSearch: string, selectedTagsInput: string[], tagsChanged: (tags: string[]) => void }>((props, ref) => {
|
||||||
|
|
||||||
const [tags, setTags] = useState<any[]>([]);
|
const [tags, setTags] = useState<any[]>(props.tagsInput);
|
||||||
|
const [tagSearch, setTagSearch] = useState<string>(props.tagSearch);
|
||||||
const [selectedTags, setSelectedTags] = useState<string[]>(props.selectedTagsInput);
|
const [selectedTags, setSelectedTags] = useState<string[]>(props.selectedTagsInput);
|
||||||
|
|
||||||
|
console.log()
|
||||||
|
|
||||||
const handleTag = (tag: string) => {
|
const handleTag = (tag: string) => {
|
||||||
if (selectedTags.includes(tag)) {
|
if (selectedTags.includes(tag)) {
|
||||||
@ -24,40 +26,35 @@ const TagSelector = forwardRef<TagSelectorProps, { selectedTagsInput: string[],
|
|||||||
setTags(selectedTags);
|
setTags(selectedTags);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getData = async () => {
|
|
||||||
const tagsResponse = await fetch(`/api/galleries/tags`);
|
|
||||||
const tagsData = await tagsResponse.json();
|
|
||||||
setTags(tagsData);
|
|
||||||
}
|
|
||||||
|
|
||||||
const generateRandomString = (length: number): string => {
|
const getData = async () => {
|
||||||
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
}
|
||||||
let result = '';
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
const randomIndex = Math.floor(Math.random() * characters.length);
|
|
||||||
result += characters.charAt(randomIndex);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
props.tagsChanged(selectedTags);
|
props.tagsChanged(selectedTags);
|
||||||
getData();
|
getData();
|
||||||
}, [selectedTags]);
|
}, [selectedTags,tagSearch,tags]);
|
||||||
|
|
||||||
useEffect(() => {
|
const generateRandomString = () => {
|
||||||
getData();
|
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
}, []);
|
let result = '';
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
return (
|
result += characters.charAt(Math.floor(Math.random() * characters.length));
|
||||||
<div className="animate-in flex md:w-full lg:w-1/2 animate-in mx-auto pt-4">
|
}
|
||||||
<div className="grid grid-cols-5 gap-4 w-full mx-auto pt-4 bg-neroshi-blue-900 pr-8 pb-4 rounded-md opacity-75 backdrop-filter backdrop-blur-md">
|
return result;
|
||||||
{tags.map((tag: any) => (
|
};
|
||||||
<Tag key={tag.name} tag={tag.name} selected={selectedTags.includes(tag.name)} onTagClicked={(tag) => handleTag(tag)} />
|
return (
|
||||||
))}
|
(tags.length > 0)? (
|
||||||
</div>
|
<div className="animate-in flex md:w-full animate-in pt-4 justify-center items-center">
|
||||||
</div>
|
<div className="z-10 grid p-4 grid-cols-2 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-1 w-full h-max-72 overflow-y-scroll no-scrollbar pt-4 bg-neroshi-blue-900 rounded-md opacity-90 backdrop-filter backdrop-blur-md mx-auto">
|
||||||
);
|
{props.tagsInput.map((tag: any) => (
|
||||||
|
(tagSearch === '' || tag.name.toLowerCase().includes(tagSearch.toLowerCase())) && // Updated condition
|
||||||
|
<Tag key={generateRandomString()} tag={tag.name} selected={selectedTags.includes(tag.name)} onTagClicked={(tag) => handleTag(tag)} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
):(<></>)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
export default TagSelector;
|
export default TagSelector;
|
Loading…
x
Reference in New Issue
Block a user