Gallery card patched component (#5880)

* Gallery card patched component
* Define in pluginApi.d.ts
This commit is contained in:
QxxxGit
2025-06-02 03:20:34 -04:00
committed by GitHub
parent 8e697b50eb
commit 044ed2708f
3 changed files with 174 additions and 132 deletions

View File

@@ -16,6 +16,7 @@ import { StudioOverlay } from "../Shared/GridCard/StudioOverlay";
import { GalleryPreviewScrubber } from "./GalleryPreviewScrubber"; import { GalleryPreviewScrubber } from "./GalleryPreviewScrubber";
import cx from "classnames"; import cx from "classnames";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
import { PatchComponent } from "src/patch";
interface IGalleryPreviewProps { interface IGalleryPreviewProps {
gallery: GQL.SlimGalleryDataFragment; gallery: GQL.SlimGalleryDataFragment;
@@ -53,7 +54,7 @@ export const GalleryPreview: React.FC<IGalleryPreviewProps> = ({
); );
}; };
interface IProps { interface IGalleryCardProps {
gallery: GQL.SlimGalleryDataFragment; gallery: GQL.SlimGalleryDataFragment;
cardWidth?: number; cardWidth?: number;
selecting?: boolean; selecting?: boolean;
@@ -62,148 +63,179 @@ interface IProps {
onSelectedChanged?: (selected: boolean, shiftKey: boolean) => void; onSelectedChanged?: (selected: boolean, shiftKey: boolean) => void;
} }
export const GalleryCard: React.FC<IProps> = (props) => { const GalleryCardPopovers = PatchComponent(
const history = useHistory(); "GalleryCard.Popovers",
(props: IGalleryCardProps) => {
function maybeRenderScenePopoverButton() {
if (props.gallery.scenes.length === 0) return;
function maybeRenderScenePopoverButton() { const popoverContent = props.gallery.scenes.map((scene) => (
if (props.gallery.scenes.length === 0) return; <SceneLink key={scene.id} scene={scene} />
));
const popoverContent = props.gallery.scenes.map((scene) => (
<SceneLink key={scene.id} scene={scene} />
));
return (
<HoverPopover
className="scene-count"
placement="bottom"
content={popoverContent}
>
<Button className="minimal">
<Icon icon={faPlayCircle} />
<span>{props.gallery.scenes.length}</span>
</Button>
</HoverPopover>
);
}
function maybeRenderTagPopoverButton() {
if (props.gallery.tags.length <= 0) return;
const popoverContent = props.gallery.tags.map((tag) => (
<TagLink key={tag.id} tag={tag} linkType="gallery" />
));
return (
<HoverPopover
className="tag-count"
placement="bottom"
content={popoverContent}
>
<Button className="minimal">
<Icon icon={faTag} />
<span>{props.gallery.tags.length}</span>
</Button>
</HoverPopover>
);
}
function maybeRenderPerformerPopoverButton() {
if (props.gallery.performers.length <= 0) return;
return (
<PerformerPopoverButton
performers={props.gallery.performers}
linkType="gallery"
/>
);
}
function maybeRenderImagesPopoverButton() {
if (!props.gallery.image_count) return;
return (
<PopoverCountButton
className="image-count"
type="image"
count={props.gallery.image_count}
url={NavUtils.makeGalleryImagesUrl(props.gallery)}
/>
);
}
function maybeRenderOrganized() {
if (props.gallery.organized) {
return ( return (
<OverlayTrigger <HoverPopover
overlay={<Tooltip id="organised-tooltip">{"Organized"}</Tooltip>} className="scene-count"
placement="bottom" placement="bottom"
content={popoverContent}
> >
<div className="organized"> <Button className="minimal">
<Button className="minimal"> <Icon icon={faPlayCircle} />
<Icon icon={faBox} /> <span>{props.gallery.scenes.length}</span>
</Button> </Button>
</div> </HoverPopover>
</OverlayTrigger>
); );
} }
}
function maybeRenderPopoverButtonGroup() { function maybeRenderTagPopoverButton() {
if ( if (props.gallery.tags.length <= 0) return;
props.gallery.scenes.length > 0 ||
props.gallery.performers.length > 0 || const popoverContent = props.gallery.tags.map((tag) => (
props.gallery.tags.length > 0 || <TagLink key={tag.id} tag={tag} linkType="gallery" />
props.gallery.organized || ));
props.gallery.image_count > 0
) {
return ( return (
<> <HoverPopover
<hr /> className="tag-count"
<ButtonGroup className="card-popovers"> placement="bottom"
{maybeRenderImagesPopoverButton()} content={popoverContent}
{maybeRenderTagPopoverButton()} >
{maybeRenderPerformerPopoverButton()} <Button className="minimal">
{maybeRenderScenePopoverButton()} <Icon icon={faTag} />
{maybeRenderOrganized()} <span>{props.gallery.tags.length}</span>
</ButtonGroup> </Button>
</> </HoverPopover>
); );
} }
}
return ( function maybeRenderPerformerPopoverButton() {
<GridCard if (props.gallery.performers.length <= 0) return;
className={`gallery-card zoom-${props.zoomIndex}`}
url={`/galleries/${props.gallery.id}`} return (
width={props.cardWidth} <PerformerPopoverButton
title={galleryTitle(props.gallery)} performers={props.gallery.performers}
linkClassName="gallery-card-header" linkType="gallery"
image={ />
<> );
<GalleryPreview }
gallery={props.gallery}
onScrubberClick={(i) => { function maybeRenderImagesPopoverButton() {
history.push(`/galleries/${props.gallery.id}/images/${i}`); if (!props.gallery.image_count) return;
}}
/> return (
<RatingBanner rating={props.gallery.rating100} /> <PopoverCountButton
</> className="image-count"
type="image"
count={props.gallery.image_count}
url={NavUtils.makeGalleryImagesUrl(props.gallery)}
/>
);
}
function maybeRenderOrganized() {
if (props.gallery.organized) {
return (
<OverlayTrigger
overlay={<Tooltip id="organised-tooltip">{"Organized"}</Tooltip>}
placement="bottom"
>
<div className="organized">
<Button className="minimal">
<Icon icon={faBox} />
</Button>
</div>
</OverlayTrigger>
);
} }
overlays={<StudioOverlay studio={props.gallery.studio} />} }
details={
<div className="gallery-card__details"> function maybeRenderPopoverButtonGroup() {
<span className="gallery-card__date">{props.gallery.date}</span> if (
<TruncatedText props.gallery.scenes.length > 0 ||
className="gallery-card__description" props.gallery.performers.length > 0 ||
text={props.gallery.details} props.gallery.tags.length > 0 ||
lineCount={3} props.gallery.organized ||
/> props.gallery.image_count > 0
</div> ) {
return (
<>
<hr />
<ButtonGroup className="card-popovers">
{maybeRenderImagesPopoverButton()}
{maybeRenderTagPopoverButton()}
{maybeRenderPerformerPopoverButton()}
{maybeRenderScenePopoverButton()}
{maybeRenderOrganized()}
</ButtonGroup>
</>
);
} }
popovers={maybeRenderPopoverButtonGroup()} }
selected={props.selected}
selecting={props.selecting} return <>{maybeRenderPopoverButtonGroup()}</>;
onSelectedChanged={props.onSelectedChanged} }
/> );
);
}; const GalleryCardDetails = PatchComponent(
"GalleryCard.Details",
(props: IGalleryCardProps) => {
return (
<div className="gallery-card__details">
<span className="gallery-card__date">{props.gallery.date}</span>
<TruncatedText
className="gallery-card__description"
text={props.gallery.details}
lineCount={3}
/>
</div>
);
}
);
const GalleryCardOverlays = PatchComponent(
"GalleryCard.Overlays",
(props: IGalleryCardProps) => {
return <StudioOverlay studio={props.gallery.studio} />;
}
);
const GalleryCardImage = PatchComponent(
"GalleryCard.Image",
(props: IGalleryCardProps) => {
const history = useHistory();
return (
<>
<GalleryPreview
gallery={props.gallery}
onScrubberClick={(i) => {
history.push(`/galleries/${props.gallery.id}/images/${i}`);
}}
/>
<RatingBanner rating={props.gallery.rating100} />
</>
);
}
);
export const GalleryCard = PatchComponent(
"GalleryCard",
(props: IGalleryCardProps) => {
return (
<GridCard
className={`gallery-card zoom-${props.zoomIndex}`}
url={`/galleries/${props.gallery.id}`}
width={props.cardWidth}
title={galleryTitle(props.gallery)}
linkClassName="gallery-card-header"
image={<GalleryCardImage {...props} />}
overlays={<GalleryCardOverlays {...props} />}
details={<GalleryCardDetails {...props} />}
popovers={<GalleryCardPopovers {...props} />}
selected={props.selected}
selecting={props.selecting}
onSelectedChanged={props.onSelectedChanged}
/>
);
}
);

View File

@@ -154,6 +154,11 @@ Returns `void`.
- `ExternalLinksButton` - `ExternalLinksButton`
- `FolderSelect` - `FolderSelect`
- `FrontPage` - `FrontPage`
- `GalleryCard`
- `GalleryCard.Details`
- `GalleryCard.Image`
- `GalleryCard.Overlays`
- `GalleryCard.Popovers`
- `GalleryIDSelect` - `GalleryIDSelect`
- `GallerySelect` - `GallerySelect`
- `GallerySelect.sort` - `GallerySelect.sort`

View File

@@ -715,6 +715,11 @@ declare namespace PluginApi {
"ScenePage.TabContent": React.FC<any>; "ScenePage.TabContent": React.FC<any>;
ScenePlayer: React.FC<any>; ScenePlayer: React.FC<any>;
FrontPage: React.FC<any>; FrontPage: React.FC<any>;
GalleryCard: React.FC<any>;
"GalleryCard.Details": React.FC<any>;
"GalleryCard.Image": React.FC<any>;
"GalleryCard.Overlays": React.FC<any>;
"GalleryCard.Popovers": React.FC<any>;
}; };
type PatchableComponentNames = keyof typeof components | string; type PatchableComponentNames = keyof typeof components | string;
namespace utils { namespace utils {