mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 12:54:38 +03:00
Change performer height to be numeric (#3060)
* Make height an int. Add height_cm field * Change UI to use height_cm * Use number fields for height/weight * Add migration note
This commit is contained in:
@@ -71,11 +71,11 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
|
||||
);
|
||||
}
|
||||
|
||||
const formatHeight = (height?: string | null) => {
|
||||
const formatHeight = (height?: number | null) => {
|
||||
if (!height) {
|
||||
return "";
|
||||
}
|
||||
return intl.formatNumber(Number.parseInt(height, 10), {
|
||||
return intl.formatNumber(height, {
|
||||
style: "unit",
|
||||
unit: "centimeter",
|
||||
unitDisplay: "narrow",
|
||||
@@ -120,7 +120,7 @@ export const PerformerDetailsPanel: React.FC<IPerformerDetails> = ({
|
||||
getCountryByISO(performer.country, intl.locale) ?? performer.country
|
||||
}
|
||||
/>
|
||||
<TextField id="height" value={formatHeight(performer.height)} />
|
||||
<TextField id="height" value={formatHeight(performer.height_cm)} />
|
||||
<TextField id="weight" value={formatWeight(performer.weight)} />
|
||||
<TextField id="measurements" value={performer.measurements} />
|
||||
<TextField id="fake_tits" value={performer.fake_tits} />
|
||||
|
||||
@@ -106,7 +106,7 @@ export const PerformerEditPanel: React.FC<IPerformerDetails> = ({
|
||||
ethnicity: yup.string().optional(),
|
||||
eye_color: yup.string().optional(),
|
||||
country: yup.string().optional(),
|
||||
height: yup.string().optional(),
|
||||
height_cm: yup.number().optional(),
|
||||
measurements: yup.string().optional(),
|
||||
fake_tits: yup.string().optional(),
|
||||
career_length: yup.string().optional(),
|
||||
@@ -133,7 +133,7 @@ export const PerformerEditPanel: React.FC<IPerformerDetails> = ({
|
||||
ethnicity: performer.ethnicity ?? "",
|
||||
eye_color: performer.eye_color ?? "",
|
||||
country: performer.country ?? "",
|
||||
height: performer.height ?? "",
|
||||
height_cm: performer.height_cm ?? undefined,
|
||||
measurements: performer.measurements ?? "",
|
||||
fake_tits: performer.fake_tits ?? "",
|
||||
career_length: performer.career_length ?? "",
|
||||
@@ -279,7 +279,7 @@ export const PerformerEditPanel: React.FC<IPerformerDetails> = ({
|
||||
formik.setFieldValue("eye_color", state.eye_color);
|
||||
}
|
||||
if (state.height) {
|
||||
formik.setFieldValue("height", state.height);
|
||||
formik.setFieldValue("height_cm", parseInt(state.height, 10));
|
||||
}
|
||||
if (state.measurements) {
|
||||
formik.setFieldValue("measurements", state.measurements);
|
||||
@@ -445,7 +445,8 @@ export const PerformerEditPanel: React.FC<IPerformerDetails> = ({
|
||||
return {
|
||||
...values,
|
||||
gender: stringToGender(values.gender) ?? null,
|
||||
weight: Number(values.weight),
|
||||
height_cm: values.height_cm ? Number(values.height_cm) : null,
|
||||
weight: values.weight ? Number(values.weight) : null,
|
||||
id: performer.id ?? "",
|
||||
};
|
||||
}
|
||||
@@ -454,7 +455,8 @@ export const PerformerEditPanel: React.FC<IPerformerDetails> = ({
|
||||
return {
|
||||
...values,
|
||||
gender: stringToGender(values.gender),
|
||||
weight: Number(values.weight),
|
||||
height_cm: values.height_cm ? Number(values.height_cm) : null,
|
||||
weight: values.weight ? Number(values.weight) : null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -797,16 +799,26 @@ export const PerformerEditPanel: React.FC<IPerformerDetails> = ({
|
||||
);
|
||||
}
|
||||
|
||||
function renderTextField(field: string, title: string, placeholder?: string) {
|
||||
function renderField(
|
||||
field: string,
|
||||
props?: {
|
||||
messageID?: string;
|
||||
placeholder?: string;
|
||||
type?: string;
|
||||
}
|
||||
) {
|
||||
const title = intl.formatMessage({ id: props?.messageID ?? field });
|
||||
|
||||
return (
|
||||
<Form.Group controlId={field} as={Row}>
|
||||
<Form.Label column xs={labelXS} xl={labelXL}>
|
||||
<FormattedMessage id={field} defaultMessage={title} />
|
||||
{title}
|
||||
</Form.Label>
|
||||
<Col xs={fieldXS} xl={fieldXL}>
|
||||
<Form.Control
|
||||
type={props?.type ?? "text"}
|
||||
className="text-input"
|
||||
placeholder={placeholder ?? title}
|
||||
placeholder={props?.placeholder ?? title}
|
||||
{...formik.getFieldProps(field)}
|
||||
isInvalid={!!formik.getFieldMeta(field).error}
|
||||
/>
|
||||
@@ -877,8 +889,8 @@ export const PerformerEditPanel: React.FC<IPerformerDetails> = ({
|
||||
</Col>
|
||||
</Form.Group>
|
||||
|
||||
{renderTextField("birthdate", "Birthdate", "YYYY-MM-DD")}
|
||||
{renderTextField("death_date", "Death Date", "YYYY-MM-DD")}
|
||||
{renderField("birthdate", { placeholder: "YYYY-MM-DD" })}
|
||||
{renderField("death_date", { placeholder: "YYYY-MM-DD" })}
|
||||
|
||||
<Form.Group as={Row}>
|
||||
<Form.Label column xs={labelXS} xl={labelXL}>
|
||||
@@ -892,13 +904,20 @@ export const PerformerEditPanel: React.FC<IPerformerDetails> = ({
|
||||
</Col>
|
||||
</Form.Group>
|
||||
|
||||
{renderTextField("ethnicity", "Ethnicity")}
|
||||
{renderTextField("hair_color", "Hair Color")}
|
||||
{renderTextField("eye_color", "Eye Color")}
|
||||
{renderTextField("height", "Height (cm)")}
|
||||
{renderTextField("weight", "Weight (kg)")}
|
||||
{renderTextField("measurements", "Measurements")}
|
||||
{renderTextField("fake_tits", "Fake Tits")}
|
||||
{renderField("ethnicity")}
|
||||
{renderField("hair_color")}
|
||||
{renderField("eye_color")}
|
||||
{renderField("height_cm", {
|
||||
type: "number",
|
||||
messageID: "height",
|
||||
placeholder: intl.formatMessage({ id: "height_cm" }),
|
||||
})}
|
||||
{renderField("weight", {
|
||||
type: "number",
|
||||
placeholder: intl.formatMessage({ id: "weight_kg" }),
|
||||
})}
|
||||
{renderField("measurements")}
|
||||
{renderField("fake_tits")}
|
||||
|
||||
<Form.Group controlId="tattoos" as={Row}>
|
||||
<Form.Label column sm={labelXS} xl={labelXL}>
|
||||
@@ -928,7 +947,7 @@ export const PerformerEditPanel: React.FC<IPerformerDetails> = ({
|
||||
</Col>
|
||||
</Form.Group>
|
||||
|
||||
{renderTextField("career_length", "Career Length")}
|
||||
{renderField("career_length")}
|
||||
|
||||
<Form.Group controlId="url" as={Row}>
|
||||
<Form.Label column xs={labelXS} xl={labelXL}>
|
||||
@@ -943,8 +962,8 @@ export const PerformerEditPanel: React.FC<IPerformerDetails> = ({
|
||||
</Col>
|
||||
</Form.Group>
|
||||
|
||||
{renderTextField("twitter", "Twitter")}
|
||||
{renderTextField("instagram", "Instagram")}
|
||||
{renderField("twitter")}
|
||||
{renderField("instagram")}
|
||||
<Form.Group controlId="details" as={Row}>
|
||||
<Form.Label column sm={labelXS} xl={labelXL}>
|
||||
<FormattedMessage id="details" />
|
||||
|
||||
@@ -196,7 +196,10 @@ export const PerformerScrapeDialog: React.FC<IPerformerScrapeDialogProps> = (
|
||||
new ScrapeResult<string>(props.performer.eye_color, props.scraped.eye_color)
|
||||
);
|
||||
const [height, setHeight] = useState<ScrapeResult<string>>(
|
||||
new ScrapeResult<string>(props.performer.height, props.scraped.height)
|
||||
new ScrapeResult<string>(
|
||||
props.performer.height_cm?.toString(),
|
||||
props.scraped.height
|
||||
)
|
||||
);
|
||||
const [weight, setWeight] = useState<ScrapeResult<string>>(
|
||||
new ScrapeResult<string>(
|
||||
|
||||
@@ -58,7 +58,7 @@ export const PerformerListTable: React.FC<IPerformerListTableProps> = (
|
||||
</Link>
|
||||
</td>
|
||||
<td>{performer.birthdate}</td>
|
||||
<td>{performer.height}</td>
|
||||
<td>{performer.height_cm}</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ const PerformerModal: React.FC<IPerformerModalProps> = ({
|
||||
ethnicity: performer.ethnicity,
|
||||
eye_color: performer.eye_color,
|
||||
country: performer.country,
|
||||
height: performer.height,
|
||||
height_cm: Number.parseFloat(performer.height ?? "") ?? undefined,
|
||||
measurements: performer.measurements,
|
||||
fake_tits: performer.fake_tits,
|
||||
career_length: performer.career_length,
|
||||
@@ -153,6 +153,10 @@ const PerformerModal: React.FC<IPerformerModalProps> = ({
|
||||
performerData.weight = undefined;
|
||||
}
|
||||
|
||||
if (Number.isNaN(performerData.height ?? 0)) {
|
||||
performerData.height = undefined;
|
||||
}
|
||||
|
||||
if (performer.tags) {
|
||||
performerData.tag_ids = performer.tags
|
||||
.map((t) => t.stored_id)
|
||||
|
||||
@@ -1194,7 +1194,7 @@ export const makePerformerCreateInput = (toCreate: GQL.ScrapedPerformer) => {
|
||||
ethnicity: toCreate.ethnicity,
|
||||
country: toCreate.country,
|
||||
eye_color: toCreate.eye_color,
|
||||
height: toCreate.height,
|
||||
height_cm: toCreate.height ? Number(toCreate.height) : undefined,
|
||||
measurements: toCreate.measurements,
|
||||
fake_tits: toCreate.fake_tits,
|
||||
career_length: toCreate.career_length,
|
||||
|
||||
@@ -3,5 +3,11 @@
|
||||
* Added selector for Country field. ([#1922](https://github.com/stashapp/stash/pull/1922))
|
||||
* Added tag description filter criterion. ([#3011](https://github.com/stashapp/stash/pull/3011))
|
||||
|
||||
### 🎨 Improvements
|
||||
* Changed Performer height to be numeric, and changed filtering accordingly. ((#3060)[https://github.com/stashapp/stash/pull/3060])
|
||||
|
||||
### 🐛 Bug fixes
|
||||
* Scene Player no longer always resumes playing when seeking. ([#3020](https://github.com/stashapp/stash/pull/3020))
|
||||
* Fixed space bar sometimes no playing/pausing the scene player. ([#3020](https://github.com/stashapp/stash/pull/3020))
|
||||
* Fixed scrubber thumbnails not disappearing when seeking on mobile. ([#3020](https://github.com/stashapp/stash/pull/3020))
|
||||
* Fix path filter behaviour to be consistent with previous behaviour. ([#3041](https://github.com/stashapp/stash/pull/3041))
|
||||
|
||||
1
ui/v2.5/src/docs/en/MigrationNotes/38.md
Normal file
1
ui/v2.5/src/docs/en/MigrationNotes/38.md
Normal file
@@ -0,0 +1 @@
|
||||
This migration changes performer height values from strings to numbers. Non-numeric performer height values **will be erased during this migration**.
|
||||
@@ -1,7 +1,9 @@
|
||||
import migration32 from "./32.md";
|
||||
import migration38 from "./38.md";
|
||||
|
||||
type Module = typeof migration32;
|
||||
|
||||
export const migrationNotes: Record<number, Module> = {
|
||||
32: migration32,
|
||||
38: migration38,
|
||||
};
|
||||
|
||||
@@ -807,6 +807,7 @@
|
||||
},
|
||||
"hasMarkers": "Has Markers",
|
||||
"height": "Height",
|
||||
"height_cm": "Height (cm)",
|
||||
"help": "Help",
|
||||
"ignore_auto_tag": "Ignore Auto Tag",
|
||||
"image": "Image",
|
||||
@@ -1065,6 +1066,7 @@
|
||||
"videos": "Videos",
|
||||
"view_all": "View All",
|
||||
"weight": "Weight",
|
||||
"weight_kg": "Weight (kg)",
|
||||
"years_old": "years old",
|
||||
"zip_file_count": "Zip File Count"
|
||||
}
|
||||
|
||||
@@ -147,10 +147,14 @@ export function makeCriteria(type: CriterionType = "none") {
|
||||
return new DuplicatedCriterion();
|
||||
case "country":
|
||||
return new CountryCriterion();
|
||||
case "height":
|
||||
case "height_cm":
|
||||
return new NumberCriterion(
|
||||
new NumberCriterionOption("height", "height_cm", type)
|
||||
);
|
||||
case "ethnicity":
|
||||
case "hair_color":
|
||||
case "eye_color":
|
||||
case "height":
|
||||
case "measurements":
|
||||
case "fake_tits":
|
||||
case "career_length":
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
createMandatoryNumberCriterionOption,
|
||||
createStringCriterionOption,
|
||||
createBooleanCriterionOption,
|
||||
NumberCriterionOption,
|
||||
} from "./criteria/criterion";
|
||||
import { FavoriteCriterionOption } from "./criteria/favorite";
|
||||
import { GenderCriterionOption } from "./criteria/gender";
|
||||
@@ -58,7 +59,6 @@ const stringCriteria: CriterionType[] = [
|
||||
"country",
|
||||
"hair_color",
|
||||
"eye_color",
|
||||
"height",
|
||||
"measurements",
|
||||
"fake_tits",
|
||||
"career_length",
|
||||
@@ -81,6 +81,7 @@ const criterionOptions = [
|
||||
createMandatoryNumberCriterionOption("image_count"),
|
||||
createMandatoryNumberCriterionOption("gallery_count"),
|
||||
createBooleanCriterionOption("ignore_auto_tag"),
|
||||
new NumberCriterionOption("height", "height_cm", "height_cm"),
|
||||
...numberCriteria.map((c) => createNumberCriterionOption(c)),
|
||||
...stringCriteria.map((c) => createStringCriterionOption(c)),
|
||||
];
|
||||
|
||||
@@ -88,6 +88,7 @@ export type CriterionType =
|
||||
| "hair_color"
|
||||
| "eye_color"
|
||||
| "height"
|
||||
| "height_cm"
|
||||
| "weight"
|
||||
| "measurements"
|
||||
| "fake_tits"
|
||||
|
||||
Reference in New Issue
Block a user