Skip to content

Commit

Permalink
Add gallery wall view, and new lightbox (#1008)
Browse files Browse the repository at this point in the history
  • Loading branch information
InfiniteTF authored Dec 24, 2020
1 parent c8bcaaf commit 232a69c
Show file tree
Hide file tree
Showing 24 changed files with 979 additions and 216 deletions.
3 changes: 0 additions & 3 deletions ui/v2.5/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
"flag-icon-css": "^3.5.0",
"flexbin": "^0.2.0",
"formik": "^2.2.1",
"fslightbox-react": "^1.5.0",
"graphql": "^15.4.0",
"graphql-tag": "^2.11.0",
"i18n-iso-countries": "^6.0.0",
Expand All @@ -52,11 +51,9 @@
"react": "17.0.1",
"react-bootstrap": "1.4.0",
"react-dom": "17.0.1",
"react-images": "0.5.19",
"react-intl": "^5.8.8",
"react-jw-player": "1.19.1",
"react-markdown": "^5.0.2",
"react-photo-gallery": "^8.0.0",
"react-router-bootstrap": "^0.25.0",
"react-router-dom": "^5.2.0",
"react-router-hash-link": "^2.2.2",
Expand Down
41 changes: 22 additions & 19 deletions ui/v2.5/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from "react";
import { Route, Switch } from "react-router-dom";
import { IntlProvider } from "react-intl";
import { ToastProvider } from "src/hooks/Toast";
import LightboxProvider from "src/hooks/Lightbox/context";
import { library } from "@fortawesome/fontawesome-svg-core";
import { fas } from "@fortawesome/free-solid-svg-icons";
import "@formatjs/intl-numberformat/polyfill";
Expand Down Expand Up @@ -53,25 +54,27 @@ export const App: React.FC = () => {
<ErrorBoundary>
<IntlProvider locale={language} messages={messages} formats={intlFormats}>
<ToastProvider>
<MainNavbar />
<div className="main container-fluid">
<Switch>
<Route exact path="/" component={Stats} />
<Route path="/scenes" component={Scenes} />
<Route path="/images" component={Images} />
<Route path="/galleries" component={Galleries} />
<Route path="/performers" component={Performers} />
<Route path="/tags" component={Tags} />
<Route path="/studios" component={Studios} />
<Route path="/movies" component={Movies} />
<Route path="/settings" component={Settings} />
<Route
path="/sceneFilenameParser"
component={SceneFilenameParser}
/>
<Route component={PageNotFound} />
</Switch>
</div>
<LightboxProvider>
<MainNavbar />
<div className="main container-fluid">
<Switch>
<Route exact path="/" component={Stats} />
<Route path="/scenes" component={Scenes} />
<Route path="/images" component={Images} />
<Route path="/galleries" component={Galleries} />
<Route path="/performers" component={Performers} />
<Route path="/tags" component={Tags} />
<Route path="/studios" component={Studios} />
<Route path="/movies" component={Movies} />
<Route path="/settings" component={Settings} />
<Route
path="/sceneFilenameParser"
component={SceneFilenameParser}
/>
<Route component={PageNotFound} />
</Switch>
</div>
</LightboxProvider>
</ToastProvider>
</IntlProvider>
</ErrorBoundary>
Expand Down
4 changes: 3 additions & 1 deletion ui/v2.5/src/components/Changelog/versions/v050.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#### 💥 **Note: After upgrading, all scene file sizes will be 0B until a new [scan](/settings?tab=tasks) is run.
#### 💥 Note: After upgrading, all scene file sizes will be 0B until a new [scan](/settings?tab=tasks) is run.

### ✨ New Features
* Add gallery wall view.
* Add organized flag for scenes, galleries and images.
* Allow configuration of visible navbar items.

### 🎨 Improvements
* Pagination support and general improvements for image lightbox.
* Add mouse click support for CDP scrapers.
* Add gallery tabs to performer and studio pages.
* Add gallery scrapers to scraper page.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,6 @@ export const Gallery: React.FC = () => {

<Tab.Content>
<Tab.Pane eventKey="images">
{/* <GalleryViewer gallery={gallery} /> */}
<GalleryImagesPanel gallery={gallery} />
</Tab.Pane>
<Tab.Pane eventKey="add">
Expand Down
11 changes: 10 additions & 1 deletion ui/v2.5/src/components/Galleries/GalleryList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ListFilterModel } from "src/models/list-filter/filter";
import { DisplayMode } from "src/models/list-filter/types";
import { queryFindGalleries } from "src/core/StashService";
import { GalleryCard } from "./GalleryCard";
import GalleryWallCard from "./GalleryWallCard";
import { EditGalleriesDialog } from "./EditGalleriesDialog";
import { DeleteGalleriesDialog } from "./DeleteGalleriesDialog";
import { ExportDialog } from "../Shared/ExportDialog";
Expand Down Expand Up @@ -212,7 +213,15 @@ export const GalleryList: React.FC<IGalleryList> = ({
);
}
if (filter.displayMode === DisplayMode.Wall) {
return <h1>TODO</h1>;
return (
<div className="row">
<div className="GalleryWall">
{result.data.findGalleries.galleries.map((gallery) => (
<GalleryWallCard key={gallery.id} gallery={gallery} />
))}
</div>
</div>
);
}
}

Expand Down
58 changes: 21 additions & 37 deletions ui/v2.5/src/components/Galleries/GalleryViewer.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,36 @@
import React, { useState } from "react";
import React from "react";
import * as GQL from "src/core/generated-graphql";
import FsLightbox from "fslightbox-react";
import { useLightbox } from "src/hooks";
import "flexbin/flexbin.css";

interface IProps {
gallery: Partial<GQL.GalleryDataFragment>;
gallery: GQL.GalleryDataFragment;
}

export const GalleryViewer: React.FC<IProps> = ({ gallery }) => {
const [lightboxToggle, setLightboxToggle] = useState(false);
const [currentIndex, setCurrentIndex] = useState(0);
const images = gallery?.images ?? [];
const showLightbox = useLightbox({ images, showNavigation: false });

const openImage = (index: number) => {
setCurrentIndex(index);
setLightboxToggle(!lightboxToggle);
};

const photos = !gallery.images
? []
: gallery.images.map((file) => file.paths.image ?? "");
const thumbs = !gallery.images
? []
: gallery.images.map((file, index) => (
<div
role="link"
tabIndex={index}
key={file.checksum ?? index}
onClick={() => openImage(index)}
onKeyPress={() => openImage(index)}
>
<img
src={file.paths.thumbnail ?? ""}
loading="lazy"
className="gallery-image"
alt={file.title ?? index.toString()}
/>
</div>
));
const thumbs = images.map((file, index) => (
<div
role="link"
tabIndex={index}
key={file.checksum ?? index}
onClick={() => showLightbox(index)}
onKeyPress={() => showLightbox(index)}
>
<img
src={file.paths.thumbnail ?? ""}
loading="lazy"
className="gallery-image"
alt={file.title ?? index.toString()}
/>
</div>
));

return (
<div className="gallery">
<div className="flexbin">{thumbs}</div>
<FsLightbox
sourceIndex={currentIndex}
toggler={lightboxToggle}
sources={photos}
key={gallery.id!}
/>
</div>
);
};
68 changes: 68 additions & 0 deletions ui/v2.5/src/components/Galleries/GalleryWallCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from "react";
import { useIntl } from "react-intl";
import { Link } from "react-router-dom";
import * as GQL from "src/core/generated-graphql";
import { RatingStars, TruncatedText } from "src/components/Shared";
import { TextUtils } from "src/utils";
import { useGalleryLightbox } from "src/hooks";

const CLASSNAME = "GalleryWallCard";
const CLASSNAME_FOOTER = `${CLASSNAME}-footer`;
const CLASSNAME_IMG = `${CLASSNAME}-img`;
const CLASSNAME_TITLE = `${CLASSNAME}-title`;

interface IProps {
gallery: GQL.GallerySlimDataFragment;
}

const GalleryWallCard: React.FC<IProps> = ({ gallery }) => {
const intl = useIntl();
const showLightbox = useGalleryLightbox(gallery.id);

const orientation =
(gallery?.cover?.file.width ?? 0) > (gallery.cover?.file.height ?? 0)
? "landscape"
: "portrait";
const cover = gallery?.cover?.paths.thumbnail ?? "";
const title = gallery.title ?? gallery.path;
const performerNames = gallery.performers.map((p) => p.name);
const performers =
performerNames.length >= 2
? [...performerNames.slice(0, -2), performerNames.slice(-2).join(" & ")]
: performerNames;

return (
<>
<section
className={`${CLASSNAME} ${CLASSNAME}-${orientation}`}
onClick={showLightbox}
onKeyPress={showLightbox}
role="button"
tabIndex={0}
>
<RatingStars rating={gallery.rating} />
<img src={cover} alt="" className={CLASSNAME_IMG} />
<footer className={CLASSNAME_FOOTER}>
<Link
to={`/galleries/${gallery.id}`}
onClick={(e) => e.stopPropagation()}
>
{title && (
<TruncatedText
text={title}
lineCount={1}
className={CLASSNAME_TITLE}
/>
)}
<TruncatedText text={performers.join(", ")} />
<div>
{gallery.date && TextUtils.formatDate(intl, gallery.date)}
</div>
</Link>
</footer>
</section>
</>
);
};

export default GalleryWallCard;
113 changes: 113 additions & 0 deletions ui/v2.5/src/components/Galleries/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,116 @@ $galleryTabWidth: 450px;
height: calc(1.5em + 0.75rem + 2px);
}
}

.GalleryWall {
display: flex;
flex-wrap: wrap;
margin: 0 auto;
width: 96vw;

/* Prevents last row from consuming all space and stretching images to oblivion */
&::after {
content: "";
flex: auto;
flex-grow: 9999;
}
}

.GalleryWallCard {
height: auto;
padding: 2px;
position: relative;

$width: 96vw;

&-landscape {
flex-grow: 2;
width: 96vw;
}

&-portrait {
flex-grow: 1;
width: 96vw;
}

@mixin galleryWidth($width) {
height: ($width / 3) * 2;

&-landscape {
width: $width;
}

&-portrait {
width: $width / 2;
}
}

@media (min-width: 576px) {
@include galleryWidth(96vw);
}
@media (min-width: 768px) {
@include galleryWidth(48vw);
}
@media (min-width: 1200px) {
@include galleryWidth(32vw);
}

&-img {
height: 100%;
object-fit: cover;
object-position: center 20%;
width: 100%;
}

&-title {
font-weight: bold;
}

&-footer {
background-image: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.3));
bottom: 0;
padding: 1rem;
position: absolute;
text-shadow: 1px 1px 3px black;
transition: 0s opacity;
width: 100%;

@media (min-width: 768px) {
opacity: 0;
}

&:hover {
.GalleryWallCard-title {
text-decoration: underline;
}
}

a {
color: white;
}
}

&:hover &-footer {
opacity: 1;
transition: 1s opacity;
transition-delay: 500ms;

a {
text-decoration: none;
}
}

.RatingStars {
position: absolute;
right: 1rem;
top: 1rem;

&-unfilled {
display: none;
}

&-filled {
filter: drop-shadow(1px 1px 1px #222);
}
}
}
Loading

0 comments on commit 232a69c

Please sign in to comment.