From 0737ca953d74770284ba19f44a15227960494417 Mon Sep 17 00:00:00 2001 From: WithoutPants <53250216+WithoutPants@users.noreply.github.com> Date: Thu, 3 Mar 2022 11:39:03 +1100 Subject: [PATCH] Add title, rating, o-counter to image lightbox (#2274) --- .../components/Changelog/versions/v0130.md | 1 + .../components/Images/ImageDetails/Image.tsx | 11 - .../Scenes/SceneDetails/OCounterButton.tsx | 49 ++- .../components/Scenes/SceneDetails/Scene.tsx | 11 - ui/v2.5/src/core/StashService.ts | 53 ++- ui/v2.5/src/hooks/Lightbox/Lightbox.tsx | 403 +++++++++++------- ui/v2.5/src/hooks/Lightbox/context.tsx | 7 +- ui/v2.5/src/hooks/Lightbox/lightbox.scss | 64 ++- 8 files changed, 380 insertions(+), 219 deletions(-) diff --git a/ui/v2.5/src/components/Changelog/versions/v0130.md b/ui/v2.5/src/components/Changelog/versions/v0130.md index 9320e0e49..5ac334da3 100644 --- a/ui/v2.5/src/components/Changelog/versions/v0130.md +++ b/ui/v2.5/src/components/Changelog/versions/v0130.md @@ -1,4 +1,5 @@ ### ✨ New Features +* Added title, rating and o-counter in image lightbox. ([#2274](https://github.com/stashapp/stash/pull/2274)) * Added option to hide scene scrubber by default. ([#2325](https://github.com/stashapp/stash/pull/2325)) * Added support for bulk-editing movies. ([#2283](https://github.com/stashapp/stash/pull/2283)) * Added support for filtering scenes, images and galleries featuring favourite performers and performer age at time of production. ([#2257](https://github.com/stashapp/stash/pull/2257)) diff --git a/ui/v2.5/src/components/Images/ImageDetails/Image.tsx b/ui/v2.5/src/components/Images/ImageDetails/Image.tsx index cfa800fa5..b4144c8f9 100644 --- a/ui/v2.5/src/components/Images/ImageDetails/Image.tsx +++ b/ui/v2.5/src/components/Images/ImageDetails/Image.tsx @@ -34,7 +34,6 @@ export const Image: React.FC = () => { const { data, error, loading } = useFindImage(id); const image = data?.findImage; - const [oLoading, setOLoading] = useState(false); const [incrementO] = useImageIncrementO(image?.id ?? "0"); const [decrementO] = useImageDecrementO(image?.id ?? "0"); const [resetO] = useImageResetO(image?.id ?? "0"); @@ -87,34 +86,25 @@ export const Image: React.FC = () => { const onIncrementClick = async () => { try { - setOLoading(true); await incrementO(); } catch (e) { Toast.error(e); - } finally { - setOLoading(false); } }; const onDecrementClick = async () => { try { - setOLoading(true); await decrementO(); } catch (e) { Toast.error(e); - } finally { - setOLoading(false); } }; const onResetClick = async () => { try { - setOLoading(true); await resetO(); } catch (e) { Toast.error(e); - } finally { - setOLoading(false); } }; @@ -196,7 +186,6 @@ export const Image: React.FC = () => { void; - onDecrement: () => void; - onReset: () => void; - onMenuOpened?: () => void; - onMenuClosed?: () => void; + onIncrement: () => Promise; + onDecrement: () => Promise; + onReset: () => Promise; } export const OCounterButton: React.FC = ( props: IOCounterButtonProps ) => { const intl = useIntl(); - if (props.loading) return ; + const [loading, setLoading] = useState(false); + + async function increment() { + setLoading(true); + await props.onIncrement(); + setLoading(false); + } + + async function decrement() { + setLoading(true); + await props.onDecrement(); + setLoading(false); + } + + async function reset() { + setLoading(true); + await props.onReset(); + setLoading(false); + } + + if (loading) return ; const renderButton = () => ( - setShowOptions(false)} - > - {({ placement, arrowProps, show: _show, ...props }) => ( -
- {optionsPopover} -
- )} -
- - - - - - {slideshowEnabled && ( - - )} - {zoom !== 1 && ( - - )} - {document.fullscreenEnabled && ( - - )} +
+
+
+ {pageHeader} + {images.length > 1 ? ( + {`${currentIndex + 1} / ${images.length}`} + ) : undefined} +
+
+
+
+ setShowOptions(false)} + > + {({ placement, arrowProps, show: _show, ...props }) => ( +
+ {optionsPopover} +
+ )} +
+ + +
-
- {allowNavigation && ( - - )} - -
- {images.map((image, i) => ( -
- {i >= currentIndex - 1 && i <= currentIndex + 1 ? ( - setZoom(v)} - resetPosition={resetPosition} - /> - ) : undefined} -
- ))} -
- - {allowNavigation && ( - - )} -
- {showNavigation && !isFullscreen && images.length > 1 && ( -
- - {navItems} - -
+ + )} - - ) : ( - - )} -
- ) : ( - <> - ); + {zoom !== 1 && ( + + )} + {document.fullscreenEnabled && ( + + )} + +
+
+
+ {allowNavigation && ( + + )} - return element; +
+ {images.map((image, i) => ( +
+ {i >= currentIndex - 1 && i <= currentIndex + 1 ? ( + setZoom(v)} + resetPosition={resetPosition} + /> + ) : undefined} +
+ ))} +
+ + {allowNavigation && ( + + )} +
+ {showNavigation && !isFullscreen && images.length > 1 && ( +
+ + {navItems} + +
+ )} +
+
+ {currentImage.id !== undefined && ( + <> +
+ +
+ { + setRating(v ?? null); + }} + /> + + )} +
+
+ {currentImage.title && ( + hide()}> + {currentImage.title ?? ""} + + )} +
+
+
+ + ); }; diff --git a/ui/v2.5/src/hooks/Lightbox/context.tsx b/ui/v2.5/src/hooks/Lightbox/context.tsx index 8980415af..c8e9bc106 100644 --- a/ui/v2.5/src/hooks/Lightbox/context.tsx +++ b/ui/v2.5/src/hooks/Lightbox/context.tsx @@ -1,11 +1,8 @@ import React, { useCallback, useState } from "react"; -import * as GQL from "src/core/generated-graphql"; -import { LightboxComponent } from "./Lightbox"; - -type Image = Pick; +import { ILightboxImage, LightboxComponent } from "./Lightbox"; export interface IState { - images: Image[]; + images: ILightboxImage[]; isVisible: boolean; isLoading: boolean; showNavigation: boolean; diff --git a/ui/v2.5/src/hooks/Lightbox/lightbox.scss b/ui/v2.5/src/hooks/Lightbox/lightbox.scss index d759646c4..ab59bfb3e 100644 --- a/ui/v2.5/src/hooks/Lightbox/lightbox.scss +++ b/ui/v2.5/src/hooks/Lightbox/lightbox.scss @@ -9,23 +9,23 @@ top: 0; z-index: 1040; - .fa-icon { - path { - fill: white; - } - opacity: 0.4; - - &:hover { - opacity: 1; - } - } - &-header { align-items: center; display: flex; flex-shrink: 0; height: 4rem; + .fa-icon { + path { + fill: white; + } + opacity: 0.4; + + &:hover { + opacity: 1; + } + } + &-left-spacer { display: flex; flex: 1; @@ -72,11 +72,42 @@ } } + &-footer { + align-items: center; + display: flex; + flex-shrink: 0; + height: 4.5rem; + + & > div { + flex: 1; + + &:nth-child(2) { + text-align: center; + } + } + + .rating-stars { + padding-left: 0.38rem; + } + + &-left { + display: flex; + flex-direction: column; + justify-content: start; + padding-left: 1em; + } + + a { + color: $text-color; + font-weight: bold; + text-decoration: none; + } + } + &-display { display: flex; height: 100%; justify-content: space-between; - margin-bottom: 2rem; position: relative; } @@ -125,7 +156,16 @@ .fa-icon { height: 4rem; + opacity: 0.4; width: 4rem; + + path { + fill: white; + } + + &:hover { + opacity: 1; + } } &:focus {