diff --git a/ui/v2.5/.eslintrc.json b/ui/v2.5/.eslintrc.json index 4fa535557..f26d60fca 100644 --- a/ui/v2.5/.eslintrc.json +++ b/ui/v2.5/.eslintrc.json @@ -47,6 +47,9 @@ "react/prop-types": "off", "react/destructuring-assignment": "off", "react/jsx-props-no-spreading": "off", + "react/style-prop-object": ["error", { + "allow": ["FormattedNumber"] + }], "spaced-comment": ["error", "always", { "markers": ["/"] }], diff --git a/ui/v2.5/src/App.tsx b/ui/v2.5/src/App.tsx index ed8037878..b521d4603 100755 --- a/ui/v2.5/src/App.tsx +++ b/ui/v2.5/src/App.tsx @@ -24,6 +24,12 @@ import Movies from "./components/Movies/Movies"; // Set fontawesome/free-solid-svg as default fontawesome icons library.add(fas); +const intlFormats = { + date: { + long: { year: "numeric", month: "long", day: "numeric" }, + }, +}; + export const App: React.FC = () => { const config = useConfiguration(); const language = config.data?.configuration?.interface?.language ?? "en-US"; @@ -33,7 +39,7 @@ export const App: React.FC = () => { return ( - +
diff --git a/ui/v2.5/src/components/List/Pagination.tsx b/ui/v2.5/src/components/List/Pagination.tsx index 4b0c8c68a..ec68b86d4 100644 --- a/ui/v2.5/src/components/List/Pagination.tsx +++ b/ui/v2.5/src/components/List/Pagination.tsx @@ -1,6 +1,6 @@ import React from "react"; import { Button, ButtonGroup } from "react-bootstrap"; -import { useIntl } from "react-intl"; +import { FormattedNumber, useIntl } from "react-intl"; interface IPaginationProps { itemsPerPage: number; @@ -62,7 +62,7 @@ export const Pagination: React.FC = ({ active={currentPage === page} onClick={() => onChangePage(page)} > - {page} + )); diff --git a/ui/v2.5/src/components/Movies/MovieCard.tsx b/ui/v2.5/src/components/Movies/MovieCard.tsx index f029e03e8..f90ab5088 100644 --- a/ui/v2.5/src/components/Movies/MovieCard.tsx +++ b/ui/v2.5/src/components/Movies/MovieCard.tsx @@ -1,5 +1,6 @@ import { Card } from "react-bootstrap"; import React, { FunctionComponent } from "react"; +import { FormattedPlural } from "react-intl"; import { Link } from "react-router-dom"; import * as GQL from "src/core/generated-graphql"; @@ -26,7 +27,16 @@ export const MovieCard: FunctionComponent = (props: IProps) => { function maybeRenderSceneNumber() { if (!props.sceneIndex) { - return {props.movie.scene_count} scenes.; + return ( + + {props.movie.scene_count}  + + + ); } return Scene number: {props.sceneIndex}; diff --git a/ui/v2.5/src/components/Movies/MovieDetails/Movie.tsx b/ui/v2.5/src/components/Movies/MovieDetails/Movie.tsx index 2a5330c58..029edbe72 100644 --- a/ui/v2.5/src/components/Movies/MovieDetails/Movie.tsx +++ b/ui/v2.5/src/components/Movies/MovieDetails/Movie.tsx @@ -1,5 +1,6 @@ /* eslint-disable react/no-this-in-sfc */ import React, { useEffect, useState, useCallback } from "react"; +import { useIntl } from "react-intl"; import * as GQL from "src/core/generated-graphql"; import { useFindMovie, @@ -65,6 +66,8 @@ export const Movie: React.FC = () => { getMovieInput() as GQL.MovieDestroyInput ); + const intl = useIntl(); + function updateMovieEditState(state: Partial) { setName(state.name ?? undefined); setAliases(state.aliases ?? undefined); @@ -238,8 +241,10 @@ export const Movie: React.FC = () => { setDuration(value ? Number.parseInt(value, 10) : undefined), })} {TableUtils.renderInputGroup({ - title: "Date (YYYY-MM-DD)", - value: date, + title: `Date ${isEditing ? "(YYYY-MM-DD)" : ""}`, + value: isEditing + ? date + : intl.formatDate(date, { format: "long" }), isEditing, onChange: setDate, })} diff --git a/ui/v2.5/src/components/Performers/PerformerCard.tsx b/ui/v2.5/src/components/Performers/PerformerCard.tsx index c67c1fda7..5c379846f 100644 --- a/ui/v2.5/src/components/Performers/PerformerCard.tsx +++ b/ui/v2.5/src/components/Performers/PerformerCard.tsx @@ -1,6 +1,7 @@ import React from "react"; import { Card } from "react-bootstrap"; import { Link } from "react-router-dom"; +import { FormattedNumber, FormattedPlural } from "react-intl"; import * as GQL from "src/core/generated-graphql"; import { NavUtils, TextUtils } from "src/utils"; import { CountryFlag } from "src/components/Shared"; @@ -39,8 +40,17 @@ export const PerformerCard: React.FC = ({ {age !== 0 ?
{ageString}
: ""}
- Stars in {performer.scene_count}{" "} - scenes. + Stars in  + +   + + + + .
diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerDetailsPanel.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerDetailsPanel.tsx index a58be6eaa..1a55366c3 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerDetailsPanel.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerDetailsPanel.tsx @@ -1,6 +1,7 @@ /* eslint-disable react/no-this-in-sfc */ import React, { useEffect, useState } from "react"; +import { useIntl } from "react-intl"; import { Button, Popover, OverlayTrigger, Table } from "react-bootstrap"; import * as GQL from "src/core/generated-graphql"; import { @@ -83,6 +84,8 @@ export const PerformerDetailsPanel: React.FC = ({ // Network state const [isLoading, setIsLoading] = useState(false); + const intl = useIntl(); + const Scrapers = useListPerformerScrapers(); const [queryableScrapers, setQueryableScrapers] = useState([]); @@ -459,6 +462,20 @@ export const PerformerDetailsPanel: React.FC = ({ }); } + const formatHeight = () => { + if (isEditing) { + return height; + } + if (!height) { + return ""; + } + return intl.formatNumber(Number.parseInt(height, 10), { + style: "unit", + unit: "centimeter", + unitDisplay: "narrow", + }); + }; + return ( <> {renderDeleteAlert()} @@ -471,7 +488,9 @@ export const PerformerDetailsPanel: React.FC = ({ {renderGender()} {TableUtils.renderInputGroup({ title: "Birthdate", - value: birthdate, + value: isEditing + ? birthdate + : intl.formatDate(birthdate, { format: "long" }), isEditing: !!isEditing, onChange: setBirthdate, })} @@ -489,8 +508,8 @@ export const PerformerDetailsPanel: React.FC = ({ onChange: setCountry, })} {TableUtils.renderInputGroup({ - title: "Height (cm)", - value: height, + title: `Height ${isEditing ? "(cm)" : ""}`, + value: formatHeight(), isEditing: !!isEditing, onChange: setHeight, })} diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneDetailPanel.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneDetailPanel.tsx index f40b9d69e..4106cbc59 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneDetailPanel.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneDetailPanel.tsx @@ -1,5 +1,6 @@ import React from "react"; import { Link } from "react-router-dom"; +import { FormattedDate } from "react-intl"; import * as GQL from "src/core/generated-graphql"; import { TextUtils } from "src/utils"; import { TagLink } from "src/components/Shared"; @@ -38,7 +39,9 @@ export const SceneDetailPanel: React.FC = (props) => { {props.scene.title ?? TextUtils.fileNameFromPath(props.scene.path)}
-

{props.scene.date ?? ""}

+

+ +

{props.scene.rating ?
Rating: {props.scene.rating}
: ""} {props.scene.file.height && (
Resolution: {TextUtils.resolution(props.scene.file.height)}
diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneFileInfoPanel.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneFileInfoPanel.tsx index 675b81545..fc1ce90cb 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneFileInfoPanel.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneFileInfoPanel.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { FormattedNumber } from "react-intl"; import * as GQL from "src/core/generated-graphql"; import { TextUtils } from "src/utils"; @@ -49,11 +50,23 @@ export const SceneFileInfoPanel: React.FC = ( if (props.scene.file.size === undefined) { return; } + + const { size, unit } = TextUtils.fileSize( + Number.parseInt(props.scene.file.size ?? "0", 10) + ); + return (
File Size - {TextUtils.fileSize(parseInt(props.scene.file.size ?? "0", 10))} +
); @@ -95,13 +108,15 @@ export const SceneFileInfoPanel: React.FC = (
Frame Rate - {props.scene.file.framerate} frames per second + frames per + second
); } function renderbitrate() { + // TODO: An upcoming react-intl version will support compound units, megabits-per-second if (props.scene.file.bitrate === undefined) { return; } @@ -109,7 +124,11 @@ export const SceneFileInfoPanel: React.FC = (
Bit Rate - {TextUtils.bitRate(props.scene.file.bitrate ?? 0)} + +  megabits per second
); diff --git a/ui/v2.5/src/components/Studios/StudioCard.tsx b/ui/v2.5/src/components/Studios/StudioCard.tsx index 2b5c76203..4b1f8f86e 100644 --- a/ui/v2.5/src/components/Studios/StudioCard.tsx +++ b/ui/v2.5/src/components/Studios/StudioCard.tsx @@ -2,6 +2,7 @@ import { Card } from "react-bootstrap"; import React from "react"; import { Link } from "react-router-dom"; import * as GQL from "src/core/generated-graphql"; +import { FormattedPlural } from "react-intl"; interface IProps { studio: GQL.StudioDataFragment; @@ -19,7 +20,15 @@ export const StudioCard: React.FC = ({ studio }) => {
{studio.name}
- {studio.scene_count} scenes. + + {studio.scene_count}  + + . +
); diff --git a/ui/v2.5/src/components/Tags/TagList.tsx b/ui/v2.5/src/components/Tags/TagList.tsx index f2bf519c7..e7d3fa218 100644 --- a/ui/v2.5/src/components/Tags/TagList.tsx +++ b/ui/v2.5/src/components/Tags/TagList.tsx @@ -1,6 +1,7 @@ import React, { useState } from "react"; import { Button, Form } from "react-bootstrap"; import { Link } from "react-router-dom"; +import { FormattedNumber } from "react-intl"; import * as GQL from "src/core/generated-graphql"; import { mutateMetadataAutoTag, @@ -115,7 +116,7 @@ export const TagList: React.FC = () => { to={NavUtils.makeTagScenesUrl(tag)} className="tag-list-anchor" > - Scenes: {tag.scene_count} + Scenes: - Total: {(tag.scene_count || 0) + (tag.scene_marker_count || 0)} + Total:{" "} +