diff --git a/ui/v2.5/graphql/data/file.graphql b/ui/v2.5/graphql/data/file.graphql index 52a4c50f89b..fe7f8fa0ecd 100644 --- a/ui/v2.5/graphql/data/file.graphql +++ b/ui/v2.5/graphql/data/file.graphql @@ -6,6 +6,7 @@ fragment FolderData on Folder { fragment VideoFileData on VideoFile { id path + basename size mod_time duration @@ -24,6 +25,7 @@ fragment VideoFileData on VideoFile { fragment ImageFileData on ImageFile { id path + basename size mod_time width @@ -37,6 +39,7 @@ fragment ImageFileData on ImageFile { fragment GalleryFileData on GalleryFile { id path + basename size mod_time fingerprints { @@ -49,6 +52,7 @@ fragment VisualFileData on VisualFile { ... on BaseFile { id path + basename size mod_time fingerprints { @@ -59,6 +63,7 @@ fragment VisualFileData on VisualFile { ... on ImageFile { id path + basename size mod_time width @@ -71,6 +76,7 @@ fragment VisualFileData on VisualFile { ... on VideoFile { id path + basename size mod_time duration diff --git a/ui/v2.5/src/components/Performers/PerformerCard.tsx b/ui/v2.5/src/components/Performers/PerformerCard.tsx index 4792e452cd3..32ed0c4106e 100644 --- a/ui/v2.5/src/components/Performers/PerformerCard.tsx +++ b/ui/v2.5/src/components/Performers/PerformerCard.tsx @@ -35,7 +35,7 @@ export interface IPerformerCardExtraCriteria { interface IPerformerCardProps { performer: GQL.PerformerDataFragment; containerWidth?: number; - ageFromDate?: string; + ageFromDate?: string | null; selecting?: boolean; selected?: boolean; onSelectedChanged?: (selected: boolean, shiftKey: boolean) => void; @@ -52,10 +52,10 @@ export const PerformerCard: React.FC = ({ extraCriteria, }) => { const intl = useIntl(); - const age = TextUtils.age( - performer.birthdate, - ageFromDate ?? performer.death_date - ); + const age = + ageFromDate === null + ? 0 + : TextUtils.age(performer.birthdate, ageFromDate ?? performer.death_date); const ageL10nId = ageFromDate ? "media_info.performer_card.age_context" : "media_info.performer_card.age"; @@ -253,13 +253,20 @@ export const PerformerCard: React.FC = ({ } title={ -
- {performer.name} - {performer.disambiguation && ( - - {` (${performer.disambiguation})`} - - )} +
+ + {performer.name} + {performer.disambiguation && ( + + {` (${performer.disambiguation})`} + + )} + {ageFromDate && ( + + {age} + + )} +
} image={ @@ -284,7 +291,7 @@ export const PerformerCard: React.FC = ({ } details={ <> - {age !== 0 ? ( + {!ageFromDate && age !== 0 ? (
{ageString}
) : ( "" diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/Scene.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/Scene.tsx index f4cbbe0e38b..ee53e8c13e1 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/Scene.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/Scene.tsx @@ -6,8 +6,9 @@ import React, { useContext, useRef, useLayoutEffect, + PropsWithChildren, } from "react"; -import { FormattedMessage, useIntl } from "react-intl"; +import { FormattedDate, FormattedMessage, useIntl } from "react-intl"; import { Link, RouteComponentProps } from "react-router-dom"; import { Helmet } from "react-helmet"; import * as GQL from "src/core/generated-graphql"; @@ -15,24 +16,21 @@ import { mutateMetadataScan, useFindScene, useSceneIncrementO, - useSceneDecrementO, - useSceneResetO, useSceneGenerateScreenshot, useSceneUpdate, queryFindScenes, queryFindScenesByID, + useSceneIncrementPlayCount, } from "src/core/StashService"; import { SceneEditPanel } from "./SceneEditPanel"; import { ErrorMessage } from "src/components/Shared/ErrorMessage"; import { LoadingIndicator } from "src/components/Shared/LoadingIndicator"; import { Icon } from "src/components/Shared/Icon"; -import { Counter } from "src/components/Shared/Counter"; import { useToast } from "src/hooks/Toast"; import SceneQueue, { QueuedScene } from "src/models/sceneQueue"; import { ListFilterModel } from "src/models/list-filter/filter"; import Mousetrap from "mousetrap"; -import { OCounterButton } from "./OCounterButton"; import { OrganizedButton } from "./OrganizedButton"; import { ConfigurationContext } from "src/hooks/Config"; import { getPlayerPosition } from "src/components/ScenePlayer/util"; @@ -40,8 +38,18 @@ import { faEllipsisV, faChevronRight, faChevronLeft, + faTimes, } from "@fortawesome/free-solid-svg-icons"; import { lazyComponent } from "src/utils/lazyComponent"; +import { objectPath, objectTitle } from "src/core/files"; +import { RatingSystem } from "src/components/Shared/Rating/RatingSystem"; +import TextUtils from "src/utils/text"; +import { + OCounterButton, + ViewCountButton, +} from "src/components/Shared/CountButton"; +import { useRatingKeybinds } from "src/hooks/keybinds"; +import cx from "classnames"; const SubmitStashBoxDraft = lazyComponent( () => import("src/components/Dialogs/SubmitDraft") @@ -62,7 +70,6 @@ const SceneMarkersPanel = lazyComponent(() => import("./SceneMarkersPanel")); const SceneFileInfoPanel = lazyComponent(() => import("./SceneFileInfoPanel")); const SceneDetailPanel = lazyComponent(() => import("./SceneDetailPanel")); const SceneHistoryPanel = lazyComponent(() => import("./SceneHistoryPanel")); -const SceneMoviePanel = lazyComponent(() => import("./SceneMoviePanel")); const SceneGalleriesPanel = lazyComponent( () => import("./SceneGalleriesPanel") ); @@ -73,7 +80,6 @@ const GenerateDialog = lazyComponent( const SceneVideoFilterPanel = lazyComponent( () => import("./SceneVideoFilterPanel") ); -import { objectPath, objectTitle } from "src/core/files"; interface IProps { scene: GQL.SceneDataFragment; @@ -126,17 +132,26 @@ const ScenePage: React.FC = ({ const boxes = configuration?.general?.stashBoxes ?? []; const [incrementO] = useSceneIncrementO(scene.id); - const [decrementO] = useSceneDecrementO(scene.id); - const [resetO] = useSceneResetO(scene.id); + + const [incrementPlay] = useSceneIncrementPlayCount(); + + function incrementPlayCount() { + incrementPlay({ + variables: { + id: scene.id, + }, + }); + } const [organizedLoading, setOrganizedLoading] = useState(false); const [activeTabKey, setActiveTabKey] = useState("scene-details-panel"); + const [activePane, setActivePane] = useState(); const [isDeleteAlertOpen, setIsDeleteAlertOpen] = useState(false); const [isGenerateDialogOpen, setIsGenerateDialogOpen] = useState(false); - const onIncrementClick = async () => { + const onIncrementOClick = async () => { try { await incrementO(); } catch (e) { @@ -144,24 +159,38 @@ const ScenePage: React.FC = ({ } }; - const onDecrementClick = async () => { - try { - await decrementO(); - } catch (e) { - Toast.error(e); - } - }; + function setRating(v: number | null) { + updateScene({ + variables: { + input: { + id: scene.id, + rating100: v, + }, + }, + }); + } + + useRatingKeybinds( + true, + configuration?.ui.ratingSystemOptions?.type, + setRating + ); + + function onSetActiveTabKey(key: string) { + setActiveTabKey(key); + setActivePane(undefined); + } // set up hotkeys useEffect(() => { - Mousetrap.bind("a", () => setActiveTabKey("scene-details-panel")); - Mousetrap.bind("q", () => setActiveTabKey("scene-queue-panel")); - Mousetrap.bind("e", () => setActiveTabKey("scene-edit-panel")); - Mousetrap.bind("k", () => setActiveTabKey("scene-markers-panel")); - Mousetrap.bind("i", () => setActiveTabKey("scene-file-info-panel")); - Mousetrap.bind("h", () => setActiveTabKey("scene-history-panel")); + Mousetrap.bind("a", () => onSetActiveTabKey("scene-details-panel")); + Mousetrap.bind("q", () => onSetActiveTabKey("scene-queue-panel")); + Mousetrap.bind("e", () => onSetActiveTabKey("scene-edit-panel")); + Mousetrap.bind("k", () => onSetActiveTabKey("scene-markers-panel")); + Mousetrap.bind("i", () => setActivePane("scene-file-info-panel")); + Mousetrap.bind("h", () => setActivePane("scene-history-panel")); Mousetrap.bind("o", () => { - onIncrementClick(); + onIncrementOClick(); }); Mousetrap.bind("p n", () => onQueueNext()); Mousetrap.bind("p p", () => onQueuePrevious()); @@ -218,14 +247,6 @@ const ScenePage: React.FC = ({ } }; - const onResetClick = async () => { - try { - await resetO(); - } catch (e) { - Toast.error(e); - } - }; - function onClickMarker(marker: GQL.SceneMarkerDataFragment) { setTimestamp(marker.seconds); } @@ -351,102 +372,89 @@ const ScenePage: React.FC = ({ ); + function TabLink( + props: PropsWithChildren<{ + className?: string; + eventKey: string; + pane?: boolean; + messageId?: string; + }> + ) { + if ( + (activePane !== undefined && activePane !== props.eventKey) || + (props.pane && activePane === undefined) + ) { + return null; + } + + return ( + + + {props.messageId ? ( + + ) : undefined} + {props.children} + + + ); + } + const renderTabs = () => ( k && setActiveTabKey(k)} >
- + setActivePane("scene-file-info-panel")} + /> = ({ isVisible={activeTabKey === "scene-markers-panel"} /> - - - {scene.galleries.length >= 1 && ( @@ -509,6 +514,11 @@ const ScenePage: React.FC = ({ const title = objectTitle(scene); + const file = useMemo( + () => (scene.files.length > 0 ? scene.files[0] : undefined), + [scene] + ); + return ( <> @@ -521,19 +531,73 @@ const ScenePage: React.FC = ({ collapsed ? "collapsed" : "" }`} > -
- {scene.studio && ( -

- - {`${scene.studio.name} +
+ {scene.studio && ( +

+ + {`${scene.studio.name} + +

+ )} +

+ {title} +

+
+ +
+ + {!!scene.date && ( + - -

- )} -

{title}

+ )} + + {file?.width && file.height ? ( + + {TextUtils.resolution(file.width, file.height)} + + ) : undefined} +
+ +
+ + + + + + + + + incrementPlayCount()} + onValueClicked={() => setActivePane("scene-history-panel")} + /> + + + onIncrementOClick()} + onValueClicked={() => setActivePane("scene-history-panel")} + /> + + + + + {renderOperations()} + +
{renderTabs()} diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneDetailPanel.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneDetailPanel.tsx index 729be465a72..63e11b89ec7 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneDetailPanel.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneDetailPanel.tsx @@ -1,156 +1,144 @@ import React, { useMemo } from "react"; -import { Link } from "react-router-dom"; -import { FormattedDate, FormattedMessage, useIntl } from "react-intl"; +import { FormattedMessage, useIntl } from "react-intl"; import * as GQL from "src/core/generated-graphql"; import TextUtils from "src/utils/text"; import { TagLink } from "src/components/Shared/TagLink"; import { TruncatedText } from "src/components/Shared/TruncatedText"; import { PerformerCard } from "src/components/Performers/PerformerCard"; import { sortPerformers } from "src/core/performers"; -import { RatingSystem } from "src/components/Shared/Rating/RatingSystem"; -import { objectTitle } from "src/core/files"; import { DirectorLink } from "src/components/Shared/Link"; +import { DetailItem } from "src/components/Shared/DetailItem"; +import { Button } from "react-bootstrap"; +import { MovieCard } from "src/components/Movies/MovieCard"; interface ISceneDetailProps { scene: GQL.SceneDataFragment; + onClickFileDetails?: (fileID?: string) => void; } -export const SceneDetailPanel: React.FC = (props) => { +export const SceneDetailPanel: React.FC = ({ + scene, + onClickFileDetails, +}) => { const intl = useIntl(); - const file = useMemo( - () => (props.scene.files.length > 0 ? props.scene.files[0] : undefined), - [props.scene] + const tags = useMemo( + () => scene.tags.map((tag) => ), + [scene.tags] ); - function renderDetails() { - if (!props.scene.details || props.scene.details === "") return; - return ( - <> -
- :{" "} -
-

{props.scene.details}

- - ); - } - - function renderTags() { - if (props.scene.tags.length === 0) return; - const tags = props.scene.tags.map((tag) => ( - - )); - return ( - <> -
- -
- {tags} - - ); - } + const movies = useMemo( + () => + scene.movies.map((sceneMovie) => ( + + )), + [scene.movies] + ); - function renderPerformers() { - if (props.scene.performers.length === 0) return; - const performers = sortPerformers(props.scene.performers); - const cards = performers.map((performer) => ( + const performers = useMemo(() => { + const sorted = sortPerformers(scene.performers); + return sorted.map((performer) => ( )); + }, [scene.performers, scene.date]); + const details = useMemo(() => { + return scene.details?.length ? ( +

{scene.details}

+ ) : undefined; + }, [scene.details]); + + const files = useMemo(() => { return ( - <> -
- -
-
- {cards} -
- +
    + {scene.files.map((file) => ( +
  • + +
  • + ))} +
); - } - - // filename should use entire row if there is no studio - const sceneDetailsWidth = props.scene.studio ? "col-9" : "col-12"; + }, [scene.files, onClickFileDetails]); return ( <>
-
-
-

- -

+
+
+ + + + {/* */} + + } + value={tags.length ? tags : undefined} + /> + + + {/* */} + + } + value={performers.length ? performers : undefined} + /> + + + + ) : undefined + } + fullWidth + /> + + +
- {props.scene.date ? ( -
- -
- ) : undefined} - {props.scene.rating100 ? ( -
- :{" "} - -
- ) : ( - "" - )} - {file?.width && file?.height && ( -
- :{" "} - {TextUtils.resolution(file.width, file.height)} -
- )} -
- :{" "} - {TextUtils.formatDateTime(intl, props.scene.created_at)}{" "} -
-
- :{" "} - {TextUtils.formatDateTime(intl, props.scene.updated_at)}{" "} -
- {props.scene.code && ( -
- : {props.scene.code}{" "} -
- )} - {props.scene.director && ( -
- :{" "} - -
- )} -
- {props.scene.studio && ( -
- - {`${props.scene.studio.name} - -
- )} -
-
-
- {renderDetails()} - {renderTags()} - {renderPerformers()}
diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneEditPanel.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneEditPanel.tsx index b4c3048c380..bee912c599f 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneEditPanel.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneEditPanel.tsx @@ -33,7 +33,6 @@ import { IMovieEntry, SceneMovieTable } from "./SceneMovieTable"; import { faSearch, faSyncAlt } from "@fortawesome/free-solid-svg-icons"; import { objectTitle } from "src/core/files"; import { galleryTitle } from "src/core/galleries"; -import { useRatingKeybinds } from "src/hooks/keybinds"; import { lazyComponent } from "src/utils/lazyComponent"; import isEqual from "lodash-es/isEqual"; import { @@ -128,7 +127,6 @@ export const SceneEditPanel: React.FC = ({ urls: yupUniqueStringList(intl), date: yupDateString(intl), director: yup.string().ensure(), - rating100: yup.number().integer().nullable().defined(), gallery_ids: yup.array(yup.string().required()).defined(), studio_id: yup.string().required().nullable(), performer_ids: yup.array(yup.string().required()).defined(), @@ -153,7 +151,6 @@ export const SceneEditPanel: React.FC = ({ urls: scene.urls ?? [], date: scene.date ?? "", director: scene.director ?? "", - rating100: scene.rating100 ?? null, gallery_ids: (scene.galleries ?? []).map((g) => g.id), studio_id: scene.studio?.id ?? null, performer_ids: (scene.performers ?? []).map((p) => p.id), @@ -201,10 +198,6 @@ export const SceneEditPanel: React.FC = ({ .filter((m) => m.movie !== undefined) as IMovieEntry[]; }, [formik.values.movies, movies]); - function setRating(v: number) { - formik.setFieldValue("rating100", v); - } - function onSetGalleries(items: Gallery[]) { setGalleries(items); formik.setFieldValue( @@ -234,12 +227,6 @@ export const SceneEditPanel: React.FC = ({ formik.setFieldValue("studio_id", item ? item.id : null); } - useRatingKeybinds( - isVisible, - stashConfig?.ui.ratingSystemOptions?.type, - setRating - ); - useEffect(() => { if (isVisible) { Mousetrap.bind("s s", () => { @@ -726,7 +713,6 @@ export const SceneEditPanel: React.FC = ({ renderField, renderInputField, renderDateField, - renderRatingField, renderURLListField, renderStashIDsField, } = formikUtils(intl, formik, splitProps); @@ -865,7 +851,6 @@ export const SceneEditPanel: React.FC = ({ {renderDateField("date")} {renderInputField("director")} - {renderRatingField("rating100", "rating")} {renderGalleriesField()} {renderStudioField()} diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneMoviePanel.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneMoviePanel.tsx deleted file mode 100644 index b63fd195d8d..00000000000 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneMoviePanel.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React from "react"; -import * as GQL from "src/core/generated-graphql"; -import { MovieCard } from "src/components/Movies/MovieCard"; - -interface ISceneMoviePanelProps { - scene: GQL.SceneDataFragment; -} - -export const SceneMoviePanel: React.FC = ( - props: ISceneMoviePanelProps -) => { - const cards = props.scene.movies.map((sceneMovie) => ( - - )); - - return ( - <> -
{cards}
- - ); -}; - -export default SceneMoviePanel; diff --git a/ui/v2.5/src/components/Scenes/styles.scss b/ui/v2.5/src/components/Scenes/styles.scss index 2f66a54e92a..2d9e87f6466 100644 --- a/ui/v2.5/src/components/Scenes/styles.scss +++ b/ui/v2.5/src/components/Scenes/styles.scss @@ -63,9 +63,54 @@ max-width: 100%; } +@include media-breakpoint-only(lg) { + .scene-header-container { + align-items: center; + display: flex; + justify-content: space-between; + + .scene-header { + flex: 0 0 75%; + order: 1; + } + + .scene-studio-image { + flex: 0 0 25%; + order: 2; + } + } +} + .scene-header { flex-basis: auto; + font-size: 1.5rem; margin-top: 30px; + + @include media-breakpoint-down(xl) { + font-size: 1.75rem; + } +} + +.scene-subheader { + display: flex; + justify-content: space-between; + margin-top: 0.5rem; + + .date { + color: $text-muted; + } + + .resolution { + font-weight: bold; + } +} + +.scene-toolbar { + display: flex; + justify-content: space-between; + margin-bottom: 0.25rem; + margin-top: 0.5rem; + width: 100%; } #scene-details-container { @@ -784,3 +829,103 @@ input[type="range"].blue-slider { } } } + +.scene-details { + .detail-group { + padding-top: 0; + } + + .detail-item { + font-size: 1.25em; + padding-right: 1rem; + + &.studio-code, + &.director, + &.files, + &.created-at, + &.updated-at, + &.details .detail-item-value { + font-size: 1rem; + } + + &.files ul { + font-size: 0.9em; + list-style-type: none; + padding-inline-start: 0; + + .file-info-button { + padding-left: 0; + padding-right: 0; + text-align: left; + } + } + + &.rating, + &.tags, + &.performers, + &.details { + flex-basis: 100%; + } + + &:not(.full-width) .detail-item-title { + margin-bottom: 0.25em; + } + } + + .detail-item-value.performers { + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + } + + .performer-card { + max-width: 12rem; + padding-bottom: 0; + + .rating-banner, + .favorite-button, + .performer-card__country-flag { + // .card-popovers { + display: none; + } + + .card-popovers { + margin-bottom: 0.25rem; + } + + .card-section-title { + font-size: 1rem; + justify-content: center; + } + + .performer-age { + color: $text-muted; + font-size: 0.9em; + margin-bottom: 0; + margin-left: 0.5rem; + } + + hr { + margin-bottom: 0; + } + } + + .movie-card { + padding-bottom: 0; + width: 10rem; + + .card-section-title, + .movie-scene-number { + font-size: 1rem; + } + + .movie-scene-number { + color: $text-muted; + } + + .movie-card__details, + .card-popovers { + display: none; + } + } +} diff --git a/ui/v2.5/src/components/Shared/CountButton.tsx b/ui/v2.5/src/components/Shared/CountButton.tsx new file mode 100644 index 00000000000..1519c104bde --- /dev/null +++ b/ui/v2.5/src/components/Shared/CountButton.tsx @@ -0,0 +1,74 @@ +import { faEye } from "@fortawesome/free-solid-svg-icons"; +import React from "react"; +import { Button, ButtonGroup } from "react-bootstrap"; +import { Icon } from "src/components/Shared/Icon"; +import { SweatDrops } from "./SweatDrops"; +import cx from "classnames"; +import { useIntl } from "react-intl"; + +interface ICountButtonProps { + value: number; + icon: React.ReactNode; + onIncrement?: () => void; + onValueClicked?: () => void; + title?: string; + countTitle?: string; +} + +export const CountButton: React.FC = ({ + value, + icon, + onIncrement, + onValueClicked, + title, + countTitle, +}) => { + return ( + + + + + ); +}; + +type CountButtonPropsNoIcon = Omit; + +export const ViewCountButton: React.FC = (props) => { + const intl = useIntl(); + return ( + } + title={intl.formatMessage({ id: "media_info.play_count" })} + countTitle={intl.formatMessage({ id: "actions.view_history" })} + /> + ); +}; + +export const OCounterButton: React.FC = (props) => { + const intl = useIntl(); + return ( + } + title={intl.formatMessage({ id: "o_count" })} + countTitle={intl.formatMessage({ id: "actions.view_history" })} + /> + ); +}; diff --git a/ui/v2.5/src/components/Shared/DetailItem.tsx b/ui/v2.5/src/components/Shared/DetailItem.tsx index 304655a4c69..83a728d7d1a 100644 --- a/ui/v2.5/src/components/Shared/DetailItem.tsx +++ b/ui/v2.5/src/components/Shared/DetailItem.tsx @@ -1,8 +1,11 @@ import React from "react"; import { FormattedMessage } from "react-intl"; +import cx from "classnames"; interface IDetailItem { id?: string | null; + messageId?: string; + heading?: React.ReactNode; value?: React.ReactNode; title?: string; fullWidth?: boolean; @@ -10,6 +13,8 @@ interface IDetailItem { export const DetailItem: React.FC = ({ id, + messageId, + heading, value, title, fullWidth, @@ -18,19 +23,24 @@ export const DetailItem: React.FC = ({ return <>; } - const message = ; + const message = ; + + // according to linter rule CSS classes shouldn't use underscores + const cssId = id?.replace("_", "-"); return ( - // according to linter rule CSS classes shouldn't use underscores -
- - {message} - {fullWidth ? ":" : ""} +
+ + {heading ? ( + heading + ) : ( + <> + {message} + {fullWidth ? ":" : ""} + + )} - + {value}
diff --git a/ui/v2.5/src/components/Shared/styles.scss b/ui/v2.5/src/components/Shared/styles.scss index 2ac707f066d..066a92da34a 100644 --- a/ui/v2.5/src/components/Shared/styles.scss +++ b/ui/v2.5/src/components/Shared/styles.scss @@ -552,3 +552,47 @@ button.btn.favorite-button { box-shadow: none; } } + +.count-button { + border-radius: 5px; + + &:hover { + background: rgba(138, 155, 168, 0.15); + color: #f5f8fa; + } + + .count-icon { + padding-left: 0.5rem; + padding-right: 0.25rem; + } + + .count-value { + padding-left: 0.25rem; + padding-right: 0.5rem; + } + + button.count-icon, + &.increment-only button.count-value { + &:hover { + background: none; + color: #f5f8fa; + } + } + + button.btn-secondary.count-icon, + button.btn-secondary.count-value { + &:focus { + border: none; + box-shadow: none; + color: #f5f8fa; + + &:not(:hover) { + background: none; + } + } + } +} + +.detail-item-title { + text-transform: capitalize; +} diff --git a/ui/v2.5/src/components/Studios/StudioDetails/StudioDetailsPanel.tsx b/ui/v2.5/src/components/Studios/StudioDetails/StudioDetailsPanel.tsx index a6c5126cb6b..38983dbbcd7 100644 --- a/ui/v2.5/src/components/Studios/StudioDetails/StudioDetailsPanel.tsx +++ b/ui/v2.5/src/components/Studios/StudioDetails/StudioDetailsPanel.tsx @@ -47,7 +47,7 @@ export const StudioDetailsPanel: React.FC = ({ return (
- + .row { - width: 100%; + > .row { + width: 100%; + } } } diff --git a/ui/v2.5/src/hooks/Lightbox/Lightbox.tsx b/ui/v2.5/src/hooks/Lightbox/Lightbox.tsx index dcdbc4b33e9..abcb9bddd4d 100644 --- a/ui/v2.5/src/hooks/Lightbox/Lightbox.tsx +++ b/ui/v2.5/src/hooks/Lightbox/Lightbox.tsx @@ -21,13 +21,7 @@ import { FormattedMessage, useIntl } from "react-intl"; import { LightboxImage } from "./LightboxImage"; import { ConfigurationContext } from "../Config"; import { Link } from "react-router-dom"; -import { OCounterButton } from "src/components/Scenes/SceneDetails/OCounterButton"; -import { - mutateImageIncrementO, - mutateImageDecrementO, - mutateImageResetO, - useImageUpdate, -} from "src/core/StashService"; +import { mutateImageIncrementO, useImageUpdate } from "src/core/StashService"; import * as GQL from "src/core/generated-graphql"; import { useInterfaceLocalForage } from "../LocalForage"; import { imageLightboxDisplayModeIntlMap } from "src/core/enums"; @@ -48,6 +42,7 @@ import { import { RatingSystem } from "src/components/Shared/Rating/RatingSystem"; import { useDebounce } from "../debounce"; import { isVideo } from "src/utils/visualFile"; +import { OCounterButton } from "src/components/Shared/CountButton"; const CLASSNAME = "Lightbox"; const CLASSNAME_HEADER = `${CLASSNAME}-header`; @@ -712,24 +707,6 @@ export const LightboxComponent: React.FC = ({ } } - async function onDecrementClick() { - if (currentImage?.id === undefined) return; - try { - await mutateImageDecrementO(currentImage.id); - } catch (e) { - Toast.error(e); - } - } - - async function onResetClick() { - if (currentImage?.id === undefined) return; - try { - await mutateImageResetO(currentImage?.id); - } catch (e) { - Toast.error(e); - } - } - const pageHeader = page && pages ? intl.formatMessage( @@ -916,9 +893,7 @@ export const LightboxComponent: React.FC = ({ <>
diff --git a/ui/v2.5/src/index.scss b/ui/v2.5/src/index.scss index 9f49d76a486..8e8897ca16e 100755 --- a/ui/v2.5/src/index.scss +++ b/ui/v2.5/src/index.scss @@ -283,22 +283,6 @@ dd { } } - .detail-item { - display: table; - padding-right: 0; - width: 100%; - - .detail-item-title { - display: table-cell; - width: 130px; - } - - .detail-item-value.age { - border-bottom: unset; - width: fit-content; - } - } - .detail-item-title.tags, .detail-item-title.parent-tags, .detail-item-title.sub-tags { @@ -306,6 +290,22 @@ dd { } } +.detail-item.full-width { + display: table; + padding-right: 0; + width: 100%; + + .detail-item-title { + display: table-cell; + width: 130px; + } + + .detail-item-value.age { + border-bottom: unset; + width: fit-content; + } +} + .detail-header-image { display: flex; float: left; diff --git a/ui/v2.5/src/locales/en-GB.json b/ui/v2.5/src/locales/en-GB.json index eabbfe1ef7e..a1e06009436 100644 --- a/ui/v2.5/src/locales/en-GB.json +++ b/ui/v2.5/src/locales/en-GB.json @@ -134,6 +134,7 @@ "temp_enable": "Enable temporarily…", "unset": "Unset", "use_default": "Use default", + "view_history": "View history", "view_random": "View Random" }, "actions_name": "Actions",