Add more patchable components (#6404)

This commit is contained in:
WithoutPants
2025-12-15 07:28:58 +11:00
committed by GitHub
parent 67b1dd8dd0
commit 62babfb332
12 changed files with 1570 additions and 1505 deletions

View File

@@ -9,16 +9,18 @@ import { useToast } from "src/hooks/Toast";
import { useIntl } from "react-intl";
import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { galleryTitle } from "src/core/galleries";
import { IItemListOperation } from "src/components/List/FilteredListToolbar";
import { PatchComponent } from "src/patch";
interface IGalleryAddProps {
active: boolean;
gallery: GQL.GalleryDataFragment;
extraOperations?: IItemListOperation<GQL.FindImagesQueryResult>[];
}
export const GalleryAddPanel: React.FC<IGalleryAddProps> = ({
active,
gallery,
}) => {
export const GalleryAddPanel: React.FC<IGalleryAddProps> = PatchComponent(
"GalleryAddPanel",
({ active, gallery, extraOperations = [] }) => {
const Toast = useToast();
const intl = useIntl();
@@ -84,6 +86,7 @@ export const GalleryAddPanel: React.FC<IGalleryAddProps> = ({
}
const otherOperations = [
...extraOperations,
{
text: intl.formatMessage(
{ id: "actions.add_to_entity" },
@@ -103,4 +106,5 @@ export const GalleryAddPanel: React.FC<IGalleryAddProps> = ({
alterQuery={active}
/>
);
};
}
);

View File

@@ -16,16 +16,19 @@ import { useIntl } from "react-intl";
import { faMinus } from "@fortawesome/free-solid-svg-icons";
import { galleryTitle } from "src/core/galleries";
import { View } from "src/components/List/views";
import { PatchComponent } from "src/patch";
import { IItemListOperation } from "src/components/List/FilteredListToolbar";
interface IGalleryDetailsProps {
active: boolean;
gallery: GQL.GalleryDataFragment;
extraOperations?: IItemListOperation<GQL.FindImagesQueryResult>[];
}
export const GalleryImagesPanel: React.FC<IGalleryDetailsProps> = ({
active,
gallery,
}) => {
export const GalleryImagesPanel: React.FC<IGalleryDetailsProps> =
PatchComponent(
"GalleryImagesPanel",
({ active, gallery, extraOperations = [] }) => {
const intl = useIntl();
const Toast = useToast();
@@ -84,7 +87,9 @@ export const GalleryImagesPanel: React.FC<IGalleryDetailsProps> = ({
intl.formatMessage(
{ id: "toast.updated_entity" },
{
entity: intl.formatMessage({ id: "gallery" }).toLocaleLowerCase(),
entity: intl
.formatMessage({ id: "gallery" })
.toLocaleLowerCase(),
}
)
);
@@ -120,6 +125,7 @@ export const GalleryImagesPanel: React.FC<IGalleryDetailsProps> = ({
}
const otherOperations = [
...extraOperations,
{
text: intl.formatMessage({ id: "actions.set_cover" }),
onClick: setCover,
@@ -144,4 +150,5 @@ export const GalleryImagesPanel: React.FC<IGalleryDetailsProps> = ({
chapters={gallery.chapters}
/>
);
};
}
);

View File

@@ -15,6 +15,8 @@ import { ExportDialog } from "../Shared/ExportDialog";
import { GalleryListTable } from "./GalleryListTable";
import { GalleryCardGrid } from "./GalleryGridCard";
import { View } from "../List/views";
import { PatchComponent } from "src/patch";
import { IItemListOperation } from "../List/FilteredListToolbar";
function getItems(result: GQL.FindGalleriesQueryResult) {
return result?.data?.findGalleries?.galleries ?? [];
@@ -28,13 +30,12 @@ interface IGalleryList {
filterHook?: (filter: ListFilterModel) => ListFilterModel;
view?: View;
alterQuery?: boolean;
extraOperations?: IItemListOperation<GQL.FindGalleriesQueryResult>[];
}
export const GalleryList: React.FC<IGalleryList> = ({
filterHook,
view,
alterQuery,
}) => {
export const GalleryList: React.FC<IGalleryList> = PatchComponent(
"GalleryList",
({ filterHook, view, alterQuery, extraOperations = [] }) => {
const intl = useIntl();
const history = useHistory();
const [isExportDialogOpen, setIsExportDialogOpen] = useState(false);
@@ -43,6 +44,7 @@ export const GalleryList: React.FC<IGalleryList> = ({
const filterMode = GQL.FilterMode.Galleries;
const otherOperations = [
...extraOperations,
{
text: intl.formatMessage({ id: "actions.view_random" }),
onClick: viewRandom,
@@ -171,7 +173,9 @@ export const GalleryList: React.FC<IGalleryList> = ({
selectedImages: GQL.SlimGalleryDataFragment[],
onClose: (applied: boolean) => void
) {
return <EditGalleriesDialog selected={selectedImages} onClose={onClose} />;
return (
<EditGalleriesDialog selected={selectedImages} onClose={onClose} />
);
}
function renderDeleteDialog(
@@ -204,4 +208,5 @@ export const GalleryList: React.FC<IGalleryList> = ({
/>
</ItemListContext>
);
};
}
);

View File

@@ -18,7 +18,10 @@ import {
SearchTermInput,
} from "src/components/List/ListFilter";
import { useFilter } from "src/components/List/FilterProvider";
import { IFilteredListToolbar } from "src/components/List/FilteredListToolbar";
import {
IFilteredListToolbar,
IItemListOperation,
} from "src/components/List/FilteredListToolbar";
import {
showWhenNoneSelected,
showWhenSelected,
@@ -28,6 +31,7 @@ import { useIntl } from "react-intl";
import { useToast } from "src/hooks/Toast";
import { useModal } from "src/hooks/modal";
import { AddSubGroupsDialog } from "./AddGroupsDialog";
import { PatchComponent } from "src/patch";
const useContainingGroupFilterHook = (
group: Pick<GQL.StudioDataFragment, "id" | "name">,
@@ -99,6 +103,7 @@ const Toolbar: React.FC<IFilteredListToolbar> = ({
interface IGroupSubGroupsPanel {
active: boolean;
group: GQL.GroupDataFragment;
extraOperations?: IItemListOperation<GQL.FindGroupsQueryResult>[];
}
const defaultFilter = (() => {
@@ -113,10 +118,10 @@ const defaultFilter = (() => {
return ret;
})();
export const GroupSubGroupsPanel: React.FC<IGroupSubGroupsPanel> = ({
active,
group,
}) => {
export const GroupSubGroupsPanel: React.FC<IGroupSubGroupsPanel> =
PatchComponent(
"GroupSubGroupsPanel",
({ active, group, extraOperations = [] }) => {
const intl = useIntl();
const Toast = useToast();
const { modal, showModal, closeModal } = useModal();
@@ -132,7 +137,10 @@ export const GroupSubGroupsPanel: React.FC<IGroupSubGroupsPanel> = ({
selectedIds: Set<string>
) {
try {
await mutateRemoveSubGroups(group.id, Array.from(selectedIds.values()));
await mutateRemoveSubGroups(
group.id,
Array.from(selectedIds.values())
);
Toast.success(
intl.formatMessage(
@@ -156,6 +164,7 @@ export const GroupSubGroupsPanel: React.FC<IGroupSubGroupsPanel> = ({
}
const otherOperations = [
...extraOperations,
{
text: intl.formatMessage({ id: "actions.add_sub_groups" }),
onClick: onAddSubGroups,
@@ -165,7 +174,9 @@ export const GroupSubGroupsPanel: React.FC<IGroupSubGroupsPanel> = ({
buttonVariant: "secondary",
},
{
text: intl.formatMessage({ id: "actions.remove_from_containing_group" }),
text: intl.formatMessage({
id: "actions.remove_from_containing_group",
}),
onClick: removeSubGroups,
isDisplayed: showWhenSelected,
postRefetch: true,
@@ -201,4 +212,5 @@ export const GroupSubGroupsPanel: React.FC<IGroupSubGroupsPanel> = ({
/>
</>
);
};
}
);

View File

@@ -21,6 +21,7 @@ import {
IFilteredListToolbar,
IItemListOperation,
} from "../List/FilteredListToolbar";
import { PatchComponent } from "src/patch";
const GroupExportDialog: React.FC<{
open?: boolean;
@@ -90,7 +91,9 @@ interface IGroupList extends IGroupListContext {
otherOperations?: IItemListOperation<GQL.FindGroupsQueryResult>[];
}
export const GroupList: React.FC<IGroupList> = ({
export const GroupList: React.FC<IGroupList> = PatchComponent(
"GroupList",
({
filterHook,
alterQuery,
defaultFilter,
@@ -100,7 +103,7 @@ export const GroupList: React.FC<IGroupList> = ({
selectable,
renderToolbar,
otherOperations: providedOperations = [],
}) => {
}) => {
const intl = useIntl();
const history = useHistory();
const [isExportDialogOpen, setIsExportDialogOpen] = useState(false);
@@ -236,4 +239,5 @@ export const GroupList: React.FC<IGroupList> = ({
/>
</GroupListContext>
);
};
}
);

View File

@@ -26,6 +26,7 @@ import { ImageGridCard } from "./ImageGridCard";
import { View } from "../List/views";
import { IItemListOperation } from "../List/FilteredListToolbar";
import { FileSize } from "../Shared/FileSize";
import { PatchComponent } from "src/patch";
interface IImageWallProps {
images: GQL.SlimImageDataFragment[];
@@ -318,13 +319,9 @@ interface IImageList {
chapters?: GQL.GalleryChapterDataFragment[];
}
export const ImageList: React.FC<IImageList> = ({
filterHook,
view,
alterQuery,
extraOperations,
chapters = [],
}) => {
export const ImageList: React.FC<IImageList> = PatchComponent(
"ImageList",
({ filterHook, view, alterQuery, extraOperations = [], chapters = [] }) => {
const intl = useIntl();
const history = useHistory();
const [isExportDialogOpen, setIsExportDialogOpen] = useState(false);
@@ -334,7 +331,7 @@ export const ImageList: React.FC<IImageList> = ({
const filterMode = GQL.FilterMode.Images;
const otherOperations = [
...(extraOperations ?? []),
...extraOperations,
{
text: intl.formatMessage({ id: "actions.view_random" }),
onClick: viewRandom,
@@ -398,7 +395,11 @@ export const ImageList: React.FC<IImageList> = ({
result: GQL.FindImagesQueryResult,
filter: ListFilterModel,
selectedIds: Set<string>,
onSelectChange: (id: string, selected: boolean, shiftKey: boolean) => void,
onSelectChange: (
id: string,
selected: boolean,
shiftKey: boolean
) => void,
onChangePage: (page: number) => void,
pageCount: number
) {
@@ -481,4 +482,5 @@ export const ImageList: React.FC<IImageList> = ({
/>
</ItemListContext>
);
};
}
);

View File

@@ -22,6 +22,8 @@ import { cmToImperial, cmToInches, kgToLbs } from "src/utils/units";
import TextUtils from "src/utils/text";
import { PerformerCardGrid } from "./PerformerCardGrid";
import { View } from "../List/views";
import { IItemListOperation } from "../List/FilteredListToolbar";
import { PatchComponent } from "src/patch";
function getItems(result: GQL.FindPerformersQueryResult) {
return result?.data?.findPerformers?.performers ?? [];
@@ -159,14 +161,12 @@ interface IPerformerList {
view?: View;
alterQuery?: boolean;
extraCriteria?: IPerformerCardExtraCriteria;
extraOperations?: IItemListOperation<GQL.FindPerformersQueryResult>[];
}
export const PerformerList: React.FC<IPerformerList> = ({
filterHook,
view,
alterQuery,
extraCriteria,
}) => {
export const PerformerList: React.FC<IPerformerList> = PatchComponent(
"PerformerList",
({ filterHook, view, alterQuery, extraCriteria, extraOperations = [] }) => {
const intl = useIntl();
const history = useHistory();
const [isExportDialogOpen, setIsExportDialogOpen] = useState(false);
@@ -175,6 +175,7 @@ export const PerformerList: React.FC<IPerformerList> = ({
const filterMode = GQL.FilterMode.Performers;
const otherOperations = [
...extraOperations,
{
text: intl.formatMessage({ id: "actions.open_random" }),
onClick: openRandom,
@@ -280,7 +281,9 @@ export const PerformerList: React.FC<IPerformerList> = ({
}
if (filter.displayMode === DisplayMode.Tagger) {
return (
<PerformerTagger performers={result.data.findPerformers.performers} />
<PerformerTagger
performers={result.data.findPerformers.performers}
/>
);
}
}
@@ -338,4 +341,5 @@ export const PerformerList: React.FC<IPerformerList> = ({
/>
</ItemListContext>
);
};
}
);

View File

@@ -66,7 +66,7 @@ import {
FilteredSidebarHeader,
useFilteredSidebarKeybinds,
} from "../List/Filters/FilterSidebar";
import { PatchContainerComponent } from "src/patch";
import { PatchComponent, PatchContainerComponent } from "src/patch";
import { Pagination, PaginationIndex } from "../List/Pagination";
import { Button, ButtonGroup } from "react-bootstrap";
import { Icon } from "../Shared/Icon";
@@ -380,7 +380,9 @@ const SceneListOperations: React.FC<{
onDelete: () => void;
onPlay: () => void;
onCreateNew: () => void;
}> = ({
}> = PatchComponent(
"SceneListOperations",
({
items,
hasSelection,
operations,
@@ -388,7 +390,7 @@ const SceneListOperations: React.FC<{
onDelete,
onPlay,
onCreateNew,
}) => {
}) => {
const intl = useIntl();
return (
@@ -456,7 +458,8 @@ const SceneListOperations: React.FC<{
</ButtonGroup>
</div>
);
};
}
);
interface IFilteredScenes {
filterHook?: (filter: ListFilterModel) => ListFilterModel;

View File

@@ -17,6 +17,8 @@ import { View } from "../List/views";
import { SceneMarkerCardsGrid } from "./SceneMarkerCardsGrid";
import { DeleteSceneMarkersDialog } from "./DeleteSceneMarkersDialog";
import { EditSceneMarkersDialog } from "./EditSceneMarkersDialog";
import { PatchComponent } from "src/patch";
import { IItemListOperation } from "../List/FilteredListToolbar";
function getItems(result: GQL.FindSceneMarkersQueryResult) {
return result?.data?.findSceneMarkers?.scene_markers ?? [];
@@ -31,19 +33,19 @@ interface ISceneMarkerList {
view?: View;
alterQuery?: boolean;
defaultSort?: string;
extraOperations?: IItemListOperation<GQL.FindSceneMarkersQueryResult>[];
}
export const SceneMarkerList: React.FC<ISceneMarkerList> = ({
filterHook,
view,
alterQuery,
}) => {
export const SceneMarkerList: React.FC<ISceneMarkerList> = PatchComponent(
"SceneMarkerList",
({ filterHook, view, alterQuery, extraOperations = [] }) => {
const intl = useIntl();
const history = useHistory();
const filterMode = GQL.FilterMode.SceneMarkers;
const otherOperations = [
...extraOperations,
{
text: intl.formatMessage({ id: "actions.play_random" }),
onClick: playRandom,
@@ -157,6 +159,7 @@ export const SceneMarkerList: React.FC<ISceneMarkerList> = ({
/>
</ItemListContext>
);
};
}
);
export default SceneMarkerList;

View File

@@ -18,6 +18,8 @@ import { StudioTagger } from "../Tagger/studios/StudioTagger";
import { StudioCardGrid } from "./StudioCardGrid";
import { View } from "../List/views";
import { EditStudiosDialog } from "./EditStudiosDialog";
import { IItemListOperation } from "../List/FilteredListToolbar";
import { PatchComponent } from "src/patch";
function getItems(result: GQL.FindStudiosQueryResult) {
return result?.data?.findStudios?.studios ?? [];
@@ -32,14 +34,12 @@ interface IStudioList {
filterHook?: (filter: ListFilterModel) => ListFilterModel;
view?: View;
alterQuery?: boolean;
extraOperations?: IItemListOperation<GQL.FindStudiosQueryResult>[];
}
export const StudioList: React.FC<IStudioList> = ({
fromParent,
filterHook,
view,
alterQuery,
}) => {
export const StudioList: React.FC<IStudioList> = PatchComponent(
"StudioList",
({ fromParent, filterHook, view, alterQuery, extraOperations = [] }) => {
const intl = useIntl();
const history = useHistory();
const [isExportDialogOpen, setIsExportDialogOpen] = useState(false);
@@ -48,6 +48,7 @@ export const StudioList: React.FC<IStudioList> = ({
const filterMode = GQL.FilterMode.Studios;
const otherOperations = [
...extraOperations,
{
text: intl.formatMessage({ id: "actions.view_random" }),
onClick: viewRandom,
@@ -205,4 +206,5 @@ export const StudioList: React.FC<IStudioList> = ({
/>
</ItemListContext>
);
};
}
);

View File

@@ -26,6 +26,8 @@ import { faTrashAlt } from "@fortawesome/free-solid-svg-icons";
import { TagCardGrid } from "./TagCardGrid";
import { EditTagsDialog } from "./EditTagsDialog";
import { View } from "../List/views";
import { IItemListOperation } from "../List/FilteredListToolbar";
import { PatchComponent } from "src/patch";
function getItems(result: GQL.FindTagsForListQueryResult) {
return result?.data?.findTags?.tags ?? [];
@@ -38,9 +40,12 @@ function getCount(result: GQL.FindTagsForListQueryResult) {
interface ITagList {
filterHook?: (filter: ListFilterModel) => ListFilterModel;
alterQuery?: boolean;
extraOperations?: IItemListOperation<GQL.FindTagsForListQueryResult>[];
}
export const TagList: React.FC<ITagList> = ({ filterHook, alterQuery }) => {
export const TagList: React.FC<ITagList> = PatchComponent(
"TagList",
({ filterHook, alterQuery, extraOperations = [] }) => {
const Toast = useToast();
const [deletingTag, setDeletingTag] =
useState<Partial<GQL.TagListDataFragment> | null>(null);
@@ -63,6 +68,7 @@ export const TagList: React.FC<ITagList> = ({ filterHook, alterQuery }) => {
const [isExportAll, setIsExportAll] = useState(false);
const otherOperations = [
...extraOperations,
{
text: intl.formatMessage({ id: "actions.view_random" }),
onClick: viewRandom,
@@ -375,4 +381,5 @@ export const TagList: React.FC<ITagList> = ({ filterHook, alterQuery }) => {
/>
</ItemListContext>
);
};
}
);

View File

@@ -671,14 +671,20 @@ declare namespace PluginApi {
"GalleryCard.Image": React.FC<any>;
"GalleryCard.Overlays": React.FC<any>;
"GalleryCard.Popovers": React.FC<any>;
GalleryAddPanel: React.FC<any>;
GalleryIDSelect: React.FC<any>;
GalleryImagesPanel: React.FC<any>;
GalleryList: React.FC<any>;
GallerySelect: React.FC<any>;
GroupIDSelect: React.FC<any>;
GroupList: React.FC<any>;
GroupSelect: React.FC<any>;
GroupSubGroupsPanel: React.FC<any>;
HeaderImage: React.FC<any>;
HoverPopover: React.FC<any>;
Icon: React.FC<any>;
ImageInput: React.FC<any>;
ImageList: React.FC<any>;
LightboxLink: React.FC<any>;
LoadingIndicator: React.FC<any>;
"MainNavBar.MenuItems": React.FC<any>;
@@ -699,6 +705,7 @@ declare namespace PluginApi {
PerformerHeaderImage: React.FC<any>;
PerformerIDSelect: React.FC<any>;
PerformerImagesPanel: React.FC<any>;
PerformerList: React.FC<any>;
PerformerPage: React.FC<any>;
PerformerScenesPanel: React.FC<any>;
PerformerSelect: React.FC<any>;
@@ -717,6 +724,9 @@ declare namespace PluginApi {
"SceneCard.Image": React.FC<any>;
"SceneCard.Overlays": React.FC<any>;
"SceneCard.Popovers": React.FC<any>;
SceneList: React.FC<any>;
SceneListOperations: React.FC<any>;
SceneMarkerList: React.FC<any>;
SelectSetting: React.FC<any>;
Setting: React.FC<any>;
SettingGroup: React.FC<any>;
@@ -724,6 +734,7 @@ declare namespace PluginApi {
StringListSetting: React.FC<any>;
StringSetting: React.FC<any>;
StudioIDSelect: React.FC<any>;
StudioList: React.FC<any>;
StudioSelect: React.FC<any>;
SweatDrops: React.FC<any>;
TabTitleCounter: React.FC<any>;
@@ -734,6 +745,7 @@ declare namespace PluginApi {
"TagCard.Popovers": React.FC<any>;
"TagCard.Title": React.FC<any>;
TagLink: React.FC<any>;
TagList: React.FC<any>;
TagSelect: React.FC<any>;
TruncatedText: React.FC<any>;
};