Sort performers in popover and card views (#1294)

This commit is contained in:
WithoutPants 2021-04-15 11:33:20 +10:00 committed by GitHub
parent e59018acfb
commit 0b40017b09
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 93 additions and 75 deletions

View file

@ -38,6 +38,7 @@ fragment SlimImageData on Image {
performers {
id
name
gender
favorite
image_path
}

View file

@ -68,6 +68,7 @@ fragment SlimSceneData on Scene {
performers {
id
name
gender
favorite
image_path
}

View file

@ -4,6 +4,7 @@
* Added scene queue.
### 🎨 Improvements
* Sort performers by gender in scene/image/gallery cards and details.
* Add popover buttons for scenes/images/galleries on performer/studio/tag cards.
* Add slideshow to image wall view.
* Support API key via URL query parameter, and added API key to stream link in Scene File Info.

View file

@ -12,6 +12,7 @@ import {
TruncatedText,
} from "src/components/Shared";
import { TextUtils } from "src/utils";
import { PerformerPopoverButton } from "../Shared/PerformerPopoverButton";
interface IProps {
gallery: GQL.GallerySlimDataFragment;
@ -63,30 +64,7 @@ export const GalleryCard: React.FC<IProps> = (props) => {
function maybeRenderPerformerPopoverButton() {
if (props.gallery.performers.length <= 0) return;
const popoverContent = props.gallery.performers.map((performer) => (
<div className="performer-tag-container row" key={performer.id}>
<Link
to={`/performers/${performer.id}`}
className="performer-tag col m-auto zoom-2"
>
<img
className="image-thumbnail"
alt={performer.name ?? ""}
src={performer.image_path ?? ""}
/>
</Link>
<TagLink key={performer.id} performer={performer} className="d-block" />
</div>
));
return (
<HoverPopover placement="bottom" content={popoverContent}>
<Button className="minimal">
<Icon icon="user" />
<span>{props.gallery.performers.length}</span>
</Button>
</HoverPopover>
);
return <PerformerPopoverButton performers={props.gallery.performers} />;
}
function maybeRenderSceneStudioOverlay() {

View file

@ -6,6 +6,7 @@ import { TextUtils } from "src/utils";
import { TagLink, TruncatedText } from "src/components/Shared";
import { PerformerCard } from "src/components/Performers/PerformerCard";
import { RatingStars } from "src/components/Scenes/SceneDetails/RatingStars";
import { sortPerformers } from "src/core/performers";
interface IGalleryDetailProps {
gallery: Partial<GQL.GalleryDataFragment>;
@ -38,7 +39,8 @@ export const GalleryDetailPanel: React.FC<IGalleryDetailProps> = (props) => {
function renderPerformers() {
if (!props.gallery.performers || props.gallery.performers.length === 0)
return;
const cards = props.gallery.performers.map((performer) => (
const performers = sortPerformers(props.gallery.performers);
const cards = performers.map((performer) => (
<PerformerCard
key={performer.id}
performer={performer}

View file

@ -11,6 +11,7 @@ import {
TruncatedText,
} from "src/components/Shared";
import { TextUtils } from "src/utils";
import { PerformerPopoverButton } from "../Shared/PerformerPopoverButton";
interface IImageCardProps {
image: GQL.SlimImageDataFragment;
@ -58,30 +59,7 @@ export const ImageCard: React.FC<IImageCardProps> = (
function maybeRenderPerformerPopoverButton() {
if (props.image.performers.length <= 0) return;
const popoverContent = props.image.performers.map((performer) => (
<div className="performer-tag-container row" key={performer.id}>
<Link
to={`/performers/${performer.id}`}
className="performer-tag col m-auto zoom-2"
>
<img
className="image-thumbnail"
alt={performer.name ?? ""}
src={performer.image_path ?? ""}
/>
</Link>
<TagLink key={performer.id} performer={performer} className="d-block" />
</div>
));
return (
<HoverPopover placement="bottom" content={popoverContent}>
<Button className="minimal">
<Icon icon="user" />
<span>{props.image.performers.length}</span>
</Button>
</HoverPopover>
);
return <PerformerPopoverButton performers={props.image.performers} />;
}
function maybeRenderOCounter() {

View file

@ -5,6 +5,7 @@ import { TextUtils } from "src/utils";
import { TagLink, TruncatedText } from "src/components/Shared";
import { PerformerCard } from "src/components/Performers/PerformerCard";
import { RatingStars } from "src/components/Scenes/SceneDetails/RatingStars";
import { sortPerformers } from "src/core/performers";
interface IImageDetailProps {
image: GQL.ImageDataFragment;
@ -26,7 +27,8 @@ export const ImageDetailPanel: React.FC<IImageDetailProps> = (props) => {
function renderPerformers() {
if (props.image.performers.length === 0) return;
const cards = props.image.performers.map((performer) => (
const performers = sortPerformers(props.image.performers);
const cards = performers.map((performer) => (
<PerformerCard key={performer.id} performer={performer} />
));

View file

@ -12,6 +12,7 @@ import {
TruncatedText,
} from "src/components/Shared";
import { TextUtils } from "src/utils";
import { PerformerPopoverButton } from "../Shared/PerformerPopoverButton";
interface IScenePreviewProps {
isPortrait: boolean;
@ -161,30 +162,7 @@ export const SceneCard: React.FC<ISceneCardProps> = (
function maybeRenderPerformerPopoverButton() {
if (props.scene.performers.length <= 0) return;
const popoverContent = props.scene.performers.map((performer) => (
<div className="performer-tag-container row" key={performer.id}>
<Link
to={`/performers/${performer.id}`}
className="performer-tag col m-auto zoom-2"
>
<img
className="image-thumbnail"
alt={performer.name ?? ""}
src={performer.image_path ?? ""}
/>
</Link>
<TagLink key={performer.id} performer={performer} className="d-block" />
</div>
));
return (
<HoverPopover placement="bottom" content={popoverContent}>
<Button className="minimal">
<Icon icon="user" />
<span>{props.scene.performers.length}</span>
</Button>
</HoverPopover>
);
return <PerformerPopoverButton performers={props.scene.performers} />;
}
function maybeRenderMoviePopoverButton() {

View file

@ -5,6 +5,7 @@ import * as GQL from "src/core/generated-graphql";
import { TextUtils } from "src/utils";
import { TagLink, TruncatedText } from "src/components/Shared";
import { PerformerCard } from "src/components/Performers/PerformerCard";
import { sortPerformers } from "src/core/performers";
import { RatingStars } from "./RatingStars";
interface ISceneDetailProps {
@ -37,7 +38,8 @@ export const SceneDetailPanel: React.FC<ISceneDetailProps> = (props) => {
function renderPerformers() {
if (props.scene.performers.length === 0) return;
const cards = props.scene.performers.map((performer) => (
const performers = sortPerformers(props.scene.performers);
const cards = performers.map((performer) => (
<PerformerCard
key={performer.id}
performer={performer}

View file

@ -0,0 +1,40 @@
import React from "react";
import { Button } from "react-bootstrap";
import { Link } from "react-router-dom";
import * as GQL from "src/core/generated-graphql";
import { sortPerformers } from "src/core/performers";
import { HoverPopover } from "./HoverPopover";
import Icon from "./Icon";
import { TagLink } from "./TagLink";
interface IProps {
performers: Partial<GQL.PerformerDataFragment>[];
}
export const PerformerPopoverButton: React.FC<IProps> = ({ performers }) => {
const sorted = sortPerformers(performers);
const popoverContent = sorted.map((performer) => (
<div className="performer-tag-container row" key={performer.id}>
<Link
to={`/performers/${performer.id}`}
className="performer-tag col m-auto zoom-2"
>
<img
className="image-thumbnail"
alt={performer.name ?? ""}
src={performer.image_path ?? ""}
/>
</Link>
<TagLink key={performer.id} performer={performer} className="d-block" />
</div>
));
return (
<HoverPopover placement="bottom" content={popoverContent}>
<Button className="minimal">
<Icon icon="user" />
<span>{performers.length}</span>
</Button>
</HoverPopover>
);
};

View file

@ -37,3 +37,38 @@ export const performerFilterHook = (
return filter;
};
};
interface IPerformerFragment {
name?: GQL.Maybe<string>;
gender?: GQL.Maybe<GQL.GenderEnum>;
}
export function sortPerformers<T extends IPerformerFragment>(performers: T[]) {
const ret = performers.slice();
ret.sort((a, b) => {
if (a.gender === b.gender) {
// sort by name
return (a.name ?? "").localeCompare(b.name ?? "");
}
// TODO - may want to customise gender order
const genderOrder = [
GQL.GenderEnum.Female,
GQL.GenderEnum.TransgenderFemale,
GQL.GenderEnum.Male,
GQL.GenderEnum.TransgenderMale,
GQL.GenderEnum.Intersex,
GQL.GenderEnum.NonBinary,
];
const aIndex = a.gender
? genderOrder.indexOf(a.gender)
: genderOrder.length;
const bIndex = b.gender
? genderOrder.indexOf(b.gender)
: genderOrder.length;
return aIndex - bIndex;
});
return ret;
}