Skip to content

Commit

Permalink
performer: stashbox: show age, gender, and image (#3964)
Browse files Browse the repository at this point in the history
* performer: stashbox: show age, gender, and image
* Add flag, improve styling
---------
Co-authored-by: WithoutPants <[email protected]>
  • Loading branch information
StashPRs authored Aug 1, 2023
1 parent ab4f562 commit 50db946
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -1,15 +1,142 @@
import React, { useEffect, useRef, useState } from "react";
import { Button, Form } from "react-bootstrap";
import { useIntl } from "react-intl";
import { Form, Row, Col, Badge } from "react-bootstrap";
import { FormattedMessage, useIntl } from "react-intl";

import * as GQL from "src/core/generated-graphql";
import { ModalComponent } from "src/components/Shared/Modal";
import { LoadingIndicator } from "src/components/Shared/LoadingIndicator";
import { stashboxDisplayName } from "src/utils/stashbox";
import { useDebouncedSetState } from "src/hooks/debounce";

import { TruncatedText } from "src/components/Shared/TruncatedText";
import { stringToGender } from "src/utils/gender";
import TextUtils from "src/utils/text";
import GenderIcon from "src/components/Performers/GenderIcon";
import { CountryFlag } from "src/components/Shared/CountryFlag";

const CLASSNAME = "PerformerScrapeModal";
const CLASSNAME_LIST = `${CLASSNAME}-list`;
const CLASSNAME_LIST_CONTAINER = `${CLASSNAME_LIST}-container`;

interface IPerformerSearchResultDetailsProps {
performer: GQL.ScrapedPerformerDataFragment;
}

const PerformerSearchResultDetails: React.FC<
IPerformerSearchResultDetailsProps
> = ({ performer }) => {
function renderImage() {
if (performer.images && performer.images.length > 0) {
return (
<div className="scene-image-container">
<img
src={performer.images[0]}
alt=""
className="align-self-center scene-image"
/>
</div>
);
}
}

function calculateAge() {
if (performer?.birthdate) {
// calculate the age from birthdate. In future, this should probably be
// provided by the server
return TextUtils.age(performer.birthdate, performer.death_date);
}
}

function renderTags() {
if (performer.tags) {
return (
<Row>
<Col>
{performer.tags?.map((tag) => (
<Badge
className="tag-item"
variant="secondary"
key={tag.stored_id}
>
{tag.name}
</Badge>
))}
</Col>
</Row>
);
}
}

function renderCountry() {
if (performer.country) {
return (
<span>
<CountryFlag
className="performer-result__country-flag"
country={performer.country}
/>
</span>
);
}
}

let age = calculateAge();

return (
<div className="performer-result">
<Row>
{renderImage()}
<div className="col flex-column">
<h4 className="performer-name">
<span>{performer.name}</span>
{performer.disambiguation && (
<span className="performer-disambiguation">
{` (${performer.disambiguation})`}
</span>
)}
</h4>
<h5 className="performer-details">
{performer.gender && (
<span>
<GenderIcon
className="gender-icon"
gender={stringToGender(performer.gender, true)}
/>
</span>
)}
{age && (
<span>
{`${age} `}
<FormattedMessage id="years_old" />
</span>
)}
</h5>
{renderCountry()}
</div>
</Row>
<Row>
<Col>
<TruncatedText text={performer.details ?? ""} lineCount={3} />
</Col>
</Row>
{renderTags()}
</div>
);
};

export interface IPerformerSearchResult {
performer: GQL.ScrapedPerformerDataFragment;
}

export const PerformerSearchResult: React.FC<IPerformerSearchResult> = ({
performer,
}) => {
return (
<div className="mt-3 search-item">
<PerformerSearchResultDetails performer={performer} />
</div>
);
};

export interface IStashBox extends GQL.StashBox {
index: number;
Expand Down Expand Up @@ -48,6 +175,31 @@ const PerformerStashBoxModal: React.FC<IProps> = ({

useEffect(() => inputRef.current?.focus(), []);

function renderResults() {
if (!performers) {
return;
}

return (
<div className={CLASSNAME_LIST_CONTAINER}>
<div className="mt-1">
<FormattedMessage
id="dialogs.performers_found"
values={{ count: performers.length }}
/>
</div>
<ul className={CLASSNAME_LIST}>
{performers.map((p, i) => (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions, react/no-array-index-key
<li key={i} onClick={() => onSelectPerformer(p)}>
<PerformerSearchResult performer={p} />
</li>
))}
</ul>
</div>
);
}

return (
<ModalComponent
show
Expand Down Expand Up @@ -75,16 +227,7 @@ const PerformerStashBoxModal: React.FC<IProps> = ({
<LoadingIndicator inline />
</div>
) : performers.length > 0 ? (
<ul className={CLASSNAME_LIST}>
{performers.map((p) => (
<li key={p.remote_site_id}>
<Button variant="link" onClick={() => onSelectPerformer(p)}>
{p.name}
{p.disambiguation && ` (${p.disambiguation})`}
</Button>
</li>
))}
</ul>
renderResults()
) : (
query !== "" && <h5 className="text-center">No results found.</h5>
)}
Expand Down
21 changes: 16 additions & 5 deletions ui/v2.5/src/components/Performers/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,14 @@

.PerformerScrapeModal {
&-list {
list-style-type: none;
list-style: none;
max-height: 50vh;
overflow-x: auto;
padding-left: 1rem;
overflow-x: hidden;
overflow-y: auto;
padding-inline-start: 0;

.btn {
font-size: 1.2rem;
li {
cursor: pointer;
}
}
}
Expand Down Expand Up @@ -212,3 +213,13 @@
/* stylelint-enable */
padding-right: 0.5rem;
}

.performer-result .performer-details > span {
&::after {
content: "";
}

&:last-child::after {
content: "";
}
}
1 change: 1 addition & 0 deletions ui/v2.5/src/locales/en-GB.json
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,7 @@
"video_previews_tooltip": "Video previews which play when hovering over a scene"
},
"scenes_found": "{count} scenes found",
"performers_found": "{count} performers found",
"scrape_entity_query": "{entity_type} Scrape Query",
"scrape_entity_title": "{entity_type} Scrape Results",
"scrape_results_existing": "Existing",
Expand Down

0 comments on commit 50db946

Please sign in to comment.