feat: add adult content filter and display in manga components #32
@ -222,9 +222,9 @@ export interface PageMangaImportJobDTO {
|
|||||||
|
|
||||||
export interface PageableObject {
|
export interface PageableObject {
|
||||||
offset?: number;
|
offset?: number;
|
||||||
paged?: boolean;
|
|
||||||
pageNumber?: number;
|
pageNumber?: number;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
|
paged?: boolean;
|
||||||
unpaged?: boolean;
|
unpaged?: boolean;
|
||||||
sort?: SortObject;
|
sort?: SortObject;
|
||||||
}
|
}
|
||||||
@ -266,6 +266,7 @@ export interface MangaListDTO {
|
|||||||
authors: string[];
|
authors: string[];
|
||||||
score: number;
|
score: number;
|
||||||
favorite: boolean;
|
favorite: boolean;
|
||||||
|
adult: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PageMangaListDTO {
|
export interface PageMangaListDTO {
|
||||||
@ -318,6 +319,7 @@ export interface MangaDTO {
|
|||||||
chapterCount: number;
|
chapterCount: number;
|
||||||
favorite: boolean;
|
favorite: boolean;
|
||||||
following: boolean;
|
following: boolean;
|
||||||
|
adult: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MangaProviderDTO {
|
export interface MangaProviderDTO {
|
||||||
@ -419,6 +421,7 @@ genreIds?: number[];
|
|||||||
statuses?: string[];
|
statuses?: string[];
|
||||||
userFavorites?: boolean;
|
userFavorites?: boolean;
|
||||||
score?: number;
|
score?: number;
|
||||||
|
adult?: boolean;
|
||||||
/**
|
/**
|
||||||
* Zero-based page index (0..N)
|
* Zero-based page index (0..N)
|
||||||
* @minimum 0
|
* @minimum 0
|
||||||
|
|||||||
@ -3,10 +3,12 @@ import {
|
|||||||
type ReactNode,
|
type ReactNode,
|
||||||
useCallback,
|
useCallback,
|
||||||
useContext,
|
useContext,
|
||||||
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
import type { SortOption } from "@/features/home/components/SortDropdown.tsx";
|
import type { SortOption } from "@/features/home/components/SortDropdown.tsx";
|
||||||
|
import { useAuth } from "./AuthContext.tsx";
|
||||||
|
|
||||||
interface UIStateContextType {
|
interface UIStateContextType {
|
||||||
/* Home Filter State */
|
/* Home Filter State */
|
||||||
@ -42,6 +44,8 @@ interface UIStateContextType {
|
|||||||
const UIStateContext = createContext<UIStateContextType | undefined>(undefined);
|
const UIStateContext = createContext<UIStateContextType | undefined>(undefined);
|
||||||
|
|
||||||
export const UIStateProvider = ({ children }: { children: ReactNode }) => {
|
export const UIStateProvider = ({ children }: { children: ReactNode }) => {
|
||||||
|
const { isAuthenticated } = useAuth();
|
||||||
|
|
||||||
/* Home Filter State */
|
/* Home Filter State */
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [selectedGenres, setSelectedGenres] = useState<number[]>([]);
|
const [selectedGenres, setSelectedGenres] = useState<number[]>([]);
|
||||||
@ -53,6 +57,10 @@ export const UIStateProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
const [searchText, setSearchText] = useState("");
|
const [searchText, setSearchText] = useState("");
|
||||||
const [isSidebarOpen, setIsSidebarOpen] = useState(true);
|
const [isSidebarOpen, setIsSidebarOpen] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setShowAdultContent(isAuthenticated);
|
||||||
|
}, [isAuthenticated]);
|
||||||
|
|
||||||
/* Manga Provider Card State */
|
/* Manga Provider Card State */
|
||||||
const [expandedProviderIds, setExpandedProviderIds] = useState<number[]>([]);
|
const [expandedProviderIds, setExpandedProviderIds] = useState<number[]>([]);
|
||||||
|
|
||||||
|
|||||||
@ -102,13 +102,21 @@ export const MangaCard = ({ manga, queryKey }: MangaCardProps) => {
|
|||||||
isImageLoading ? "opacity-0" : "opacity-100",
|
isImageLoading ? "opacity-0" : "opacity-100",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<div className="absolute right-2 top-2">
|
<div className="absolute right-2 top-2 flex flex-col items-end gap-1">
|
||||||
<Badge
|
<Badge
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="border border-border/50 bg-background/80 text-foreground backdrop-blur-sm"
|
className="border border-border/50 bg-background/80 text-foreground backdrop-blur-sm"
|
||||||
>
|
>
|
||||||
{manga.status}
|
{manga.status}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
{manga.adult && (
|
||||||
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className="border-red-500/50 bg-red-500/10 text-red-500 text-[10px] px-1.5 py-0 backdrop-blur-sm"
|
||||||
|
>
|
||||||
|
18+
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
|||||||
@ -56,6 +56,7 @@ const Home = () => {
|
|||||||
genreIds: selectedGenres,
|
genreIds: selectedGenres,
|
||||||
userFavorites,
|
userFavorites,
|
||||||
score: minRating,
|
score: minRating,
|
||||||
|
adult: showAdultContent,
|
||||||
});
|
});
|
||||||
const isFiltersDisabled = isFetching;
|
const isFiltersDisabled = isFetching;
|
||||||
|
|
||||||
|
|||||||
@ -277,6 +277,14 @@ const Manga = () => {
|
|||||||
>
|
>
|
||||||
{mangaData.data?.status}
|
{mangaData.data?.status}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
{mangaData.data?.adult && (
|
||||||
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className="border-red-500/50 bg-red-500/10 text-red-500"
|
||||||
|
>
|
||||||
|
18+
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
|
||||||
{isAuthenticated && (
|
{isAuthenticated && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user