Add FileSize component and refactor file size rendering in various components (#5695)

This commit is contained in:
WithoutPants 2025-03-03 18:38:19 +11:00 committed by GitHub
parent a391fa4345
commit ce2d779dbc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 53 additions and 125 deletions

View file

@ -1,6 +1,6 @@
import React, { useState } from "react";
import { Accordion, Button, Card } from "react-bootstrap";
import { FormattedMessage, FormattedNumber, FormattedTime } from "react-intl";
import { FormattedMessage, FormattedTime } from "react-intl";
import { TruncatedText } from "src/components/Shared/TruncatedText";
import { DeleteFilesDialog } from "src/components/Shared/DeleteFilesDialog";
import * as GQL from "src/core/generated-graphql";
@ -8,6 +8,7 @@ import { mutateImageSetPrimaryFile } from "src/core/StashService";
import { useToast } from "src/hooks/Toast";
import TextUtils from "src/utils/text";
import { TextField, URLField, URLsField } from "src/utils/field";
import { FileSize } from "src/components/Shared/FileSize";
interface IFileInfoPanelProps {
file: GQL.ImageFileDataFragment | GQL.VideoFileDataFragment;
@ -21,29 +22,6 @@ interface IFileInfoPanelProps {
const FileInfoPanel: React.FC<IFileInfoPanelProps> = (
props: IFileInfoPanelProps
) => {
function renderFileSize() {
if (props.file.size === undefined) {
return;
}
const { size, unit } = TextUtils.fileSize(props.file.size ?? 0);
return (
<TextField id="filesize">
<span className="text-truncate">
<FormattedNumber
value={size}
// eslint-disable-next-line react/style-prop-object
style="unit"
unit={unit}
unitDisplay="narrow"
maximumFractionDigits={2}
/>
</span>
</TextField>
);
}
const checksum = props.file.fingerprints.find((f) => f.type === "md5");
return (
@ -64,7 +42,11 @@ const FileInfoPanel: React.FC<IFileInfoPanelProps> = (
value={`file://${props.file.path}`}
truncate
/>
{renderFileSize()}
<TextField id="filesize">
<span className="text-truncate">
<FileSize size={props.file.size} />
</span>
</TextField>
<TextField id="file_mod_time">
<FormattedTime
dateStyle="medium"

View file

@ -23,11 +23,11 @@ import "flexbin/flexbin.css";
import Gallery from "react-photo-gallery";
import { ExportDialog } from "../Shared/ExportDialog";
import { objectTitle } from "src/core/files";
import TextUtils from "src/utils/text";
import { ConfigurationContext } from "src/hooks/Config";
import { ImageGridCard } from "./ImageGridCard";
import { View } from "../List/views";
import { IItemListOperation } from "../List/FilteredListToolbar";
import { FileSize } from "../Shared/FileSize";
interface IImageWallProps {
images: GQL.SlimImageDataFragment[];
@ -230,7 +230,6 @@ function getCount(result: GQL.FindImagesQueryResult) {
function renderMetadataByline(result: GQL.FindImagesQueryResult) {
const megapixels = result?.data?.findImages?.megapixels;
const size = result?.data?.findImages?.filesize;
const filesize = size ? TextUtils.fileSize(size) : undefined;
if (!megapixels && !size) {
return;
@ -247,15 +246,9 @@ function renderMetadataByline(result: GQL.FindImagesQueryResult) {
</span>
) : undefined}
{separator}
{size && filesize ? (
{size ? (
<span className="images-size">
<FormattedNumber
value={filesize.size}
maximumFractionDigits={TextUtils.fileSizeFractionalDigits(
filesize.unit
)}
/>
{` ${TextUtils.formatFileSizeUnit(filesize.unit)}`}
<FileSize size={size} />
</span>
) : undefined}
)

View file

@ -44,6 +44,7 @@ import {
} from "@fortawesome/free-solid-svg-icons";
import { SceneMergeModal } from "../Scenes/SceneMergeDialog";
import { objectTitle } from "src/core/files";
import { FileSize } from "../Shared/FileSize";
const CLASSNAME = "duplicate-checker";
@ -326,19 +327,6 @@ export const SceneDuplicateChecker: React.FC = () => {
resetCheckboxSelection();
}
const renderFilesize = (filesize: number | null | undefined) => {
const { size: parsedSize, unit } = TextUtils.fileSize(filesize ?? 0);
return (
<FormattedNumber
value={parsedSize}
style="unit"
unit={unit}
unitDisplay="narrow"
maximumFractionDigits={2}
/>
);
};
function maybeRenderMissingPhashWarning() {
const missingPhashes = missingPhash?.findScenes.count ?? 0;
if (missingPhashes > 0) {
@ -917,7 +905,9 @@ export const SceneDuplicateChecker: React.FC = () => {
{file?.duration &&
TextUtils.secondsToTimestamp(file.duration)}
</td>
<td>{renderFilesize(file?.size ?? 0)}</td>
<td>
<FileSize size={file?.size ?? 0} />
</td>
<td>{`${file?.width ?? 0}x${file?.height ?? 0}`}</td>
<td>
<FormattedNumber

View file

@ -15,7 +15,7 @@ import { ConfigurationContext } from "src/hooks/Config";
import { PerformerPopoverButton } from "../Shared/PerformerPopoverButton";
import { GridCard, calculateCardWidth } from "../Shared/GridCard/GridCard";
import { RatingBanner } from "../Shared/RatingBanner";
import { FormattedMessage, FormattedNumber } from "react-intl";
import { FormattedMessage } from "react-intl";
import {
faBox,
faCopy,
@ -30,6 +30,7 @@ import { PatchComponent } from "src/patch";
import ScreenUtils from "src/utils/screen";
import { StudioOverlay } from "../Shared/GridCard/StudioOverlay";
import { GroupTag } from "../Groups/GroupTag";
import { FileSize } from "../Shared/FileSize";
interface IScenePreviewProps {
isPortrait: boolean;
@ -362,21 +363,11 @@ const SceneCardImage = PatchComponent(
);
function maybeRenderSceneSpecsOverlay() {
let sizeObj = null;
if (file?.size) {
sizeObj = TextUtils.fileSize(file.size);
}
return (
<div className="scene-specs-overlay">
{sizeObj != null ? (
{file?.size !== undefined ? (
<span className="overlay-filesize extra-scene-info">
<FormattedNumber
value={sizeObj.size}
maximumFractionDigits={TextUtils.fileSizeFractionalDigits(
sizeObj.unit
)}
/>
{TextUtils.formatFileSizeUnit(sizeObj.unit)}
<FileSize size={file.size} />
</span>
) : (
""

View file

@ -18,6 +18,7 @@ import TextUtils from "src/utils/text";
import { TextField, URLField, URLsField } from "src/utils/field";
import { StashIDPill } from "src/components/Shared/StashID";
import { PatchComponent } from "../../../patch";
import { FileSize } from "src/components/Shared/FileSize";
interface IFileInfoPanelProps {
sceneID: string;
@ -36,25 +37,6 @@ const FileInfoPanel: React.FC<IFileInfoPanelProps> = (
const intl = useIntl();
const history = useHistory();
function renderFileSize() {
const { size, unit } = TextUtils.fileSize(props.file.size);
return (
<TextField id="filesize">
<span className="text-truncate">
<FormattedNumber
value={size}
// eslint-disable-next-line react/style-prop-object
style="unit"
unit={unit}
unitDisplay="narrow"
maximumFractionDigits={2}
/>
</span>
</TextField>
);
}
// TODO - generalise fingerprints
const oshash = props.file.fingerprints.find((f) => f.type === "oshash");
const phash = props.file.fingerprints.find((f) => f.type === "phash");
@ -94,7 +76,11 @@ const FileInfoPanel: React.FC<IFileInfoPanelProps> = (
value={`file://${props.file.path}`}
truncate
/>
{renderFileSize()}
<TextField id="filesize">
<span className="text-truncate">
<FileSize size={props.file.size} />
</span>
</TextField>
<TextField id="file_mod_time">
<FormattedTime
dateStyle="medium"

View file

@ -1,6 +1,6 @@
import React, { useState } from "react";
import cloneDeep from "lodash-es/cloneDeep";
import { FormattedNumber, useIntl } from "react-intl";
import { useIntl } from "react-intl";
import { useHistory } from "react-router-dom";
import Mousetrap from "mousetrap";
import * as GQL from "src/core/generated-graphql";
@ -25,6 +25,7 @@ import { SceneMergeModal } from "./SceneMergeDialog";
import { objectTitle } from "src/core/files";
import TextUtils from "src/utils/text";
import { View } from "../List/views";
import { FileSize } from "../Shared/FileSize";
function getItems(result: GQL.FindScenesQueryResult) {
return result?.data?.findScenes?.scenes ?? [];
@ -37,7 +38,6 @@ function getCount(result: GQL.FindScenesQueryResult) {
function renderMetadataByline(result: GQL.FindScenesQueryResult) {
const duration = result?.data?.findScenes?.duration;
const size = result?.data?.findScenes?.filesize;
const filesize = size ? TextUtils.fileSize(size) : undefined;
if (!duration && !size) {
return;
@ -54,15 +54,9 @@ function renderMetadataByline(result: GQL.FindScenesQueryResult) {
</span>
) : undefined}
{separator}
{size && filesize ? (
{size ? (
<span className="scenes-size">
<FormattedNumber
value={filesize.size}
maximumFractionDigits={TextUtils.fileSizeFractionalDigits(
filesize.unit
)}
/>
{` ${TextUtils.formatFileSizeUnit(filesize.unit)}`}
<FileSize size={size} />
</span>
) : undefined}
)

View file

@ -3,7 +3,7 @@ import { Link } from "react-router-dom";
import * as GQL from "src/core/generated-graphql";
import NavUtils from "src/utils/navigation";
import TextUtils from "src/utils/text";
import { FormattedMessage, FormattedNumber, useIntl } from "react-intl";
import { FormattedMessage, useIntl } from "react-intl";
import { objectTitle } from "src/core/files";
import { galleryTitle } from "src/core/galleries";
import SceneQueue from "src/models/sceneQueue";
@ -11,6 +11,7 @@ import { RatingSystem } from "../Shared/Rating/RatingSystem";
import { useSceneUpdate } from "src/core/StashService";
import { IColumn, ListTable } from "../List/ListTable";
import { useTableColumns } from "src/hooks/useTableColumns";
import { FileSize } from "../Shared/FileSize";
interface ISceneListTableProps {
scenes: GQL.SlimSceneDataFragment[];
@ -169,24 +170,12 @@ export const SceneListTable: React.FC<ISceneListTableProps> = (
</ul>
);
function renderFileSize(file: { size: number | undefined }) {
const { size, unit } = TextUtils.fileSize(file.size);
return (
<FormattedNumber
value={size}
style="unit"
unit={unit}
unitDisplay="narrow"
maximumFractionDigits={2}
/>
);
}
const FileSizeCell = (scene: GQL.SlimSceneDataFragment) => (
<ul className="comma-list">
{scene.files.map((file) => (
<li key={file.id}>{renderFileSize(file)}</li>
<li key={file.id}>
<FileSize size={file.size} />
</li>
))}
</ul>
);

View file

@ -0,0 +1,17 @@
import React from "react";
import { FormattedNumber } from "react-intl";
import TextUtils from "src/utils/text";
export const FileSize: React.FC<{ size: number }> = ({ size: fileSize }) => {
const { size, unit } = TextUtils.fileSize(fileSize);
return (
<>
<FormattedNumber
value={size}
maximumFractionDigits={TextUtils.fileSizeFractionalDigits(unit)}
/>
{` ${TextUtils.formatFileSizeUnit(unit)}`}
</>
);
};

View file

@ -3,6 +3,7 @@ import { useStats } from "src/core/StashService";
import { FormattedMessage, FormattedNumber } from "react-intl";
import { LoadingIndicator } from "src/components/Shared/LoadingIndicator";
import TextUtils from "src/utils/text";
import { FileSize } from "./Shared/FileSize";
export const Stats: React.FC = () => {
const { data, error, loading } = useStats();
@ -10,9 +11,6 @@ export const Stats: React.FC = () => {
if (error) return <span>{error.message}</span>;
if (loading || !data) return <LoadingIndicator />;
const scenesSize = TextUtils.fileSize(data.stats.scenes_size);
const imagesSize = TextUtils.fileSize(data.stats.images_size);
const scenesDuration = TextUtils.secondsAsTimeString(
data.stats.scenes_duration,
3
@ -28,13 +26,7 @@ export const Stats: React.FC = () => {
<div className="col col-sm-8 m-sm-auto row stats">
<div className="stats-element">
<p className="title">
<FormattedNumber
value={scenesSize.size}
maximumFractionDigits={TextUtils.fileSizeFractionalDigits(
scenesSize.unit
)}
/>
{` ${TextUtils.formatFileSizeUnit(scenesSize.unit)}`}
<FileSize size={data.stats.scenes_size} />
</p>
<p className="heading">
<FormattedMessage id="stats.scenes_size" />
@ -74,13 +66,7 @@ export const Stats: React.FC = () => {
<div className="col col-sm-8 m-sm-auto row stats">
<div className="stats-element">
<p className="title">
<FormattedNumber
value={imagesSize.size}
maximumFractionDigits={TextUtils.fileSizeFractionalDigits(
imagesSize.unit
)}
/>
{` ${TextUtils.formatFileSizeUnit(imagesSize.unit)}`}
<FileSize size={data.stats.images_size} />
</p>
<p className="heading">
<FormattedMessage id="stats.image_size" />