import React, { useEffect, useMemo, useState } from "react"; import { FormattedMessage, IntlShape, useIntl } from "react-intl"; import { useFindSavedFilters } from "src/core/StashService"; import { LoadingIndicator } from "../Shared/LoadingIndicator"; import { Button, Form, Modal } from "react-bootstrap"; import * as GQL from "src/core/generated-graphql"; import { ConfigurationContext } from "src/hooks/Config"; import { ISavedFilterRow, ICustomFilter, FrontPageContent, generatePremadeFrontPageContent, getFrontPageContent, } from "src/core/config"; interface IAddSavedFilterModalProps { onClose: (content?: FrontPageContent) => void; existingSavedFilterIDs: string[]; candidates: GQL.FindSavedFiltersQuery; } const FilterModeToMessageID = { [GQL.FilterMode.Galleries]: "galleries", [GQL.FilterMode.Images]: "images", [GQL.FilterMode.Movies]: "groups", [GQL.FilterMode.Groups]: "groups", [GQL.FilterMode.Performers]: "performers", [GQL.FilterMode.SceneMarkers]: "markers", [GQL.FilterMode.Scenes]: "scenes", [GQL.FilterMode.Studios]: "studios", [GQL.FilterMode.Tags]: "tags", }; type SavedFilter = Pick; function filterTitle(intl: IntlShape, f: SavedFilter) { const typeMessage = intl.formatMessage({ id: FilterModeToMessageID[f.mode] }); return `${typeMessage}: ${f.name}`; } const AddContentModal: React.FC = ({ onClose, existingSavedFilterIDs, candidates, }) => { const intl = useIntl(); const premadeFilterOptions = useMemo( () => generatePremadeFrontPageContent(intl), [intl] ); const [contentType, setContentType] = useState( "front_page.types.premade_filter" ); const [premadeFilterIndex, setPremadeFilterIndex] = useState< number | undefined >(0); const [savedFilter, setSavedFilter] = useState(); function onTypeSelected(t: string) { setContentType(t); switch (t) { case "front_page.types.premade_filter": setPremadeFilterIndex(0); setSavedFilter(undefined); break; case "front_page.types.saved_filter": setPremadeFilterIndex(undefined); setSavedFilter(undefined); break; } } function isValid() { switch (contentType) { case "front_page.types.premade_filter": return premadeFilterIndex !== undefined; case "front_page.types.saved_filter": return savedFilter !== undefined; } return false; } const savedFilterOptions = useMemo(() => { const ret = [ { value: "", text: "", }, ].concat( candidates.findSavedFilters .filter((f) => { return !existingSavedFilterIDs.includes(f.id); }) .map((f) => { return { value: f.id, text: filterTitle(intl, f), }; }) ); ret.sort((a, b) => { return a.text.localeCompare(b.text); }); return ret; }, [candidates, existingSavedFilterIDs, intl]); function renderTypeSelect() { const options = [ "front_page.types.premade_filter", "front_page.types.saved_filter", ]; return ( onTypeSelected(e.target.value)} className="btn-secondary" > {options.map((c) => ( ))} ); } function maybeRenderPremadeFiltersSelect() { if (contentType !== "front_page.types.premade_filter") return; return ( setPremadeFilterIndex(parseInt(e.target.value))} className="btn-secondary" > {premadeFilterOptions.map((c, i) => ( ))} ); } function maybeRenderSavedFiltersSelect() { if (contentType !== "front_page.types.saved_filter") return; return ( setSavedFilter(e.target.value)} className="btn-secondary" > {savedFilterOptions.map((c) => ( ))} ); } function doAdd() { switch (contentType) { case "front_page.types.premade_filter": onClose(premadeFilterOptions[premadeFilterIndex!]); return; case "front_page.types.saved_filter": onClose({ __typename: "SavedFilter", savedFilterId: parseInt(savedFilter!), }); return; } onClose(); } return ( onClose()}>
{renderTypeSelect()} {maybeRenderSavedFiltersSelect()} {maybeRenderPremadeFiltersSelect()}
); }; interface IFilterRowProps { content: FrontPageContent; allSavedFilters: SavedFilter[]; onDelete: () => void; } const ContentRow: React.FC = (props: IFilterRowProps) => { const intl = useIntl(); function title() { switch (props.content.__typename) { case "SavedFilter": const savedFilterId = String(props.content.savedFilterId); const savedFilter = props.allSavedFilters.find( (f) => f.id === savedFilterId ); if (!savedFilter) return ""; return filterTitle(intl, savedFilter); case "CustomFilter": const asCustomFilter = props.content as ICustomFilter; if (asCustomFilter.message) return intl.formatMessage( { id: asCustomFilter.message.id }, asCustomFilter.message.values ); return asCustomFilter.title ?? ""; } } return (

{title()}

); }; interface IFrontPageConfigProps { onClose: (content?: FrontPageContent[]) => void; } export const FrontPageConfig: React.FC = ({ onClose, }) => { const { configuration, loading } = React.useContext(ConfigurationContext); const ui = configuration?.ui; const { data: allFilters, loading: loading2 } = useFindSavedFilters(); const [isAdd, setIsAdd] = useState(false); const [currentContent, setCurrentContent] = useState([]); const [dragIndex, setDragIndex] = useState(); useEffect(() => { if (!allFilters?.findSavedFilters) { return; } const frontPageContent = getFrontPageContent(ui); if (frontPageContent) { setCurrentContent( // filter out rows where the saved filter no longer exists frontPageContent.filter((r) => { if (r.__typename === "SavedFilter") { const savedFilterId = String(r.savedFilterId); return allFilters.findSavedFilters.some( (f) => f.id === savedFilterId ); } return true; }) ); } }, [allFilters, ui]); function onDragStart(event: React.DragEvent, index: number) { event.dataTransfer.effectAllowed = "move"; setDragIndex(index); } function onDragOver(event: React.DragEvent, index?: number) { if (dragIndex !== undefined && index !== undefined && index !== dragIndex) { const newFilters = [...currentContent]; const moved = newFilters.splice(dragIndex, 1); newFilters.splice(index, 0, moved[0]); setCurrentContent(newFilters); setDragIndex(index); } event.dataTransfer.dropEffect = "move"; event.preventDefault(); } function onDragOverDefault(event: React.DragEvent) { event.dataTransfer.dropEffect = "move"; event.preventDefault(); } function onDrop() { // assume we've already set the temp filter list // feed it up setDragIndex(undefined); } if (loading || loading2) { return ; } const existingSavedFilterIDs = currentContent .filter( (f) => f.__typename === "SavedFilter" && (f as ISavedFilterRow).savedFilterId ) .map((f) => (f as ISavedFilterRow).savedFilterId.toString()); function addSavedFilter(content?: FrontPageContent) { setIsAdd(false); if (!content) { return; } setCurrentContent([...currentContent, content]); } function deleteSavedFilter(index: number) { setCurrentContent(currentContent.filter((f, i) => i !== index)); } return ( <> {isAdd && allFilters && ( )}
{currentContent.map((content, index) => (
onDragStart(e, index)} onDragEnter={(e) => onDragOver(e, index)} onDrop={() => onDrop()} > deleteSavedFilter(index)} />
))}
); };