mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 12:54:38 +03:00
Make directors and photographers clickable in detail view (#4621)
* Make directors and photographers clickable * Make director clickable on movie details page --------- Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
This commit is contained in:
@@ -9,6 +9,7 @@ import { PerformerCard } from "src/components/Performers/PerformerCard";
|
|||||||
import { RatingSystem } from "src/components/Shared/Rating/RatingSystem";
|
import { RatingSystem } from "src/components/Shared/Rating/RatingSystem";
|
||||||
import { sortPerformers } from "src/core/performers";
|
import { sortPerformers } from "src/core/performers";
|
||||||
import { galleryTitle } from "src/core/galleries";
|
import { galleryTitle } from "src/core/galleries";
|
||||||
|
import { PhotographerLink } from "src/components/Shared/Link";
|
||||||
|
|
||||||
interface IGalleryDetailProps {
|
interface IGalleryDetailProps {
|
||||||
gallery: GQL.GalleryDataFragment;
|
gallery: GQL.GalleryDataFragment;
|
||||||
@@ -118,7 +119,11 @@ export const GalleryDetailPanel: React.FC<IGalleryDetailProps> = ({
|
|||||||
)}
|
)}
|
||||||
{gallery.photographer && (
|
{gallery.photographer && (
|
||||||
<h6>
|
<h6>
|
||||||
<FormattedMessage id="photographer" />: {gallery.photographer}{" "}
|
<FormattedMessage id="photographer" />:{" "}
|
||||||
|
<PhotographerLink
|
||||||
|
photographer={gallery.photographer}
|
||||||
|
linkType="gallery"
|
||||||
|
/>
|
||||||
</h6>
|
</h6>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { RatingSystem } from "src/components/Shared/Rating/RatingSystem";
|
|||||||
import { sortPerformers } from "src/core/performers";
|
import { sortPerformers } from "src/core/performers";
|
||||||
import { FormattedDate, FormattedMessage, useIntl } from "react-intl";
|
import { FormattedDate, FormattedMessage, useIntl } from "react-intl";
|
||||||
import { objectTitle } from "src/core/files";
|
import { objectTitle } from "src/core/files";
|
||||||
|
import { PhotographerLink } from "src/components/Shared/Link";
|
||||||
interface IImageDetailProps {
|
interface IImageDetailProps {
|
||||||
image: GQL.ImageDataFragment;
|
image: GQL.ImageDataFragment;
|
||||||
}
|
}
|
||||||
@@ -154,7 +155,11 @@ export const ImageDetailPanel: React.FC<IImageDetailProps> = (props) => {
|
|||||||
)}
|
)}
|
||||||
{props.image.photographer && (
|
{props.image.photographer && (
|
||||||
<h6>
|
<h6>
|
||||||
<FormattedMessage id="photographer" />: {props.image.photographer}{" "}
|
<FormattedMessage id="photographer" />:{" "}
|
||||||
|
<PhotographerLink
|
||||||
|
photographer={props.image.photographer}
|
||||||
|
linkType="image"
|
||||||
|
/>
|
||||||
</h6>
|
</h6>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import * as GQL from "src/core/generated-graphql";
|
|||||||
import TextUtils from "src/utils/text";
|
import TextUtils from "src/utils/text";
|
||||||
import { DetailItem } from "src/components/Shared/DetailItem";
|
import { DetailItem } from "src/components/Shared/DetailItem";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
import { DirectorLink } from "src/components/Shared/Link";
|
||||||
|
|
||||||
interface IMovieDetailsPanel {
|
interface IMovieDetailsPanel {
|
||||||
movie: GQL.MovieDataFragment;
|
movie: GQL.MovieDataFragment;
|
||||||
@@ -45,7 +46,17 @@ export const MovieDetailsPanel: React.FC<IMovieDetailsPanel> = ({
|
|||||||
fullWidth={fullWidth}
|
fullWidth={fullWidth}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DetailItem id="director" value={movie.director} fullWidth={fullWidth} />
|
<DetailItem
|
||||||
|
id="director"
|
||||||
|
value={
|
||||||
|
movie.director ? (
|
||||||
|
<DirectorLink director={movie.director} linkType="movie" />
|
||||||
|
) : (
|
||||||
|
""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fullWidth={fullWidth}
|
||||||
|
/>
|
||||||
<DetailItem id="synopsis" value={movie.synopsis} fullWidth={fullWidth} />
|
<DetailItem id="synopsis" value={movie.synopsis} fullWidth={fullWidth} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { PerformerCard } from "src/components/Performers/PerformerCard";
|
|||||||
import { sortPerformers } from "src/core/performers";
|
import { sortPerformers } from "src/core/performers";
|
||||||
import { RatingSystem } from "src/components/Shared/Rating/RatingSystem";
|
import { RatingSystem } from "src/components/Shared/Rating/RatingSystem";
|
||||||
import { objectTitle } from "src/core/files";
|
import { objectTitle } from "src/core/files";
|
||||||
|
import { DirectorLink } from "src/components/Shared/Link";
|
||||||
|
|
||||||
interface ISceneDetailProps {
|
interface ISceneDetailProps {
|
||||||
scene: GQL.SceneDataFragment;
|
scene: GQL.SceneDataFragment;
|
||||||
@@ -128,7 +129,8 @@ export const SceneDetailPanel: React.FC<ISceneDetailProps> = (props) => {
|
|||||||
)}
|
)}
|
||||||
{props.scene.director && (
|
{props.scene.director && (
|
||||||
<h6>
|
<h6>
|
||||||
<FormattedMessage id="director" />: {props.scene.director}{" "}
|
<FormattedMessage id="director" />:{" "}
|
||||||
|
<DirectorLink director={props.scene.director} linkType="scene" />
|
||||||
</h6>
|
</h6>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
37
ui/v2.5/src/components/Shared/Link.tsx
Normal file
37
ui/v2.5/src/components/Shared/Link.tsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { useMemo } from "react";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import NavUtils from "src/utils/navigation";
|
||||||
|
|
||||||
|
// common link components
|
||||||
|
|
||||||
|
export const DirectorLink: React.FC<{
|
||||||
|
director: string;
|
||||||
|
linkType: "scene" | "movie";
|
||||||
|
}> = ({ director: director, linkType = "scene" }) => {
|
||||||
|
const link = useMemo(() => {
|
||||||
|
switch (linkType) {
|
||||||
|
case "scene":
|
||||||
|
return NavUtils.makeDirectorScenesUrl(director);
|
||||||
|
case "movie":
|
||||||
|
return NavUtils.makeDirectorMoviesUrl(director);
|
||||||
|
}
|
||||||
|
}, [director, linkType]);
|
||||||
|
|
||||||
|
return <Link to={link}>{director}</Link>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PhotographerLink: React.FC<{
|
||||||
|
photographer: string;
|
||||||
|
linkType: "gallery" | "image";
|
||||||
|
}> = ({ photographer, linkType = "image" }) => {
|
||||||
|
const link = useMemo(() => {
|
||||||
|
switch (linkType) {
|
||||||
|
case "gallery":
|
||||||
|
return NavUtils.makePhotographerGalleriesUrl(photographer);
|
||||||
|
case "image":
|
||||||
|
return NavUtils.makePhotographerImagesUrl(photographer);
|
||||||
|
}
|
||||||
|
}, [photographer, linkType]);
|
||||||
|
|
||||||
|
return <Link to={link}>{photographer}</Link>;
|
||||||
|
};
|
||||||
@@ -15,7 +15,10 @@ import { ListFilterModel } from "src/models/list-filter/filter";
|
|||||||
import { MoviesCriterion } from "src/models/list-filter/criteria/movies";
|
import { MoviesCriterion } from "src/models/list-filter/criteria/movies";
|
||||||
import {
|
import {
|
||||||
Criterion,
|
Criterion,
|
||||||
|
CriterionOption,
|
||||||
CriterionValue,
|
CriterionValue,
|
||||||
|
StringCriterion,
|
||||||
|
createStringCriterionOption,
|
||||||
} from "src/models/list-filter/criteria/criterion";
|
} from "src/models/list-filter/criteria/criterion";
|
||||||
import { GalleriesCriterion } from "src/models/list-filter/criteria/galleries";
|
import { GalleriesCriterion } from "src/models/list-filter/criteria/galleries";
|
||||||
import { PhashCriterion } from "src/models/list-filter/criteria/phash";
|
import { PhashCriterion } from "src/models/list-filter/criteria/phash";
|
||||||
@@ -355,6 +358,55 @@ const makeGalleryImagesUrl = (
|
|||||||
return `/images?${filter.makeQueryParameters()}`;
|
return `/images?${filter.makeQueryParameters()}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function stringEqualsCriterion(option: CriterionOption, value: string) {
|
||||||
|
const criterion = new StringCriterion(option);
|
||||||
|
criterion.modifier = GQL.CriterionModifier.Equals;
|
||||||
|
criterion.value = value;
|
||||||
|
return criterion;
|
||||||
|
}
|
||||||
|
|
||||||
|
const makeDirectorScenesUrl = (director: string) => {
|
||||||
|
if (director.length == 0) return "#";
|
||||||
|
const filter = new ListFilterModel(GQL.FilterMode.Scenes, undefined);
|
||||||
|
filter.criteria.push(
|
||||||
|
stringEqualsCriterion(createStringCriterionOption("director"), director)
|
||||||
|
);
|
||||||
|
return `/scenes?${filter.makeQueryParameters()}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const makeDirectorMoviesUrl = (director: string) => {
|
||||||
|
if (director.length == 0) return "#";
|
||||||
|
const filter = new ListFilterModel(GQL.FilterMode.Movies, undefined);
|
||||||
|
filter.criteria.push(
|
||||||
|
stringEqualsCriterion(createStringCriterionOption("director"), director)
|
||||||
|
);
|
||||||
|
return `/movies?${filter.makeQueryParameters()}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const makePhotographerGalleriesUrl = (photographer: string) => {
|
||||||
|
if (photographer.length == 0) return "#";
|
||||||
|
const filter = new ListFilterModel(GQL.FilterMode.Galleries, undefined);
|
||||||
|
filter.criteria.push(
|
||||||
|
stringEqualsCriterion(
|
||||||
|
createStringCriterionOption("photographer"),
|
||||||
|
photographer
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return `/galleries?${filter.makeQueryParameters()}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const makePhotographerImagesUrl = (photographer: string) => {
|
||||||
|
if (photographer.length == 0) return "#";
|
||||||
|
const filter = new ListFilterModel(GQL.FilterMode.Images, undefined);
|
||||||
|
filter.criteria.push(
|
||||||
|
stringEqualsCriterion(
|
||||||
|
createStringCriterionOption("photographer"),
|
||||||
|
photographer
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return `/images?${filter.makeQueryParameters()}`;
|
||||||
|
};
|
||||||
|
|
||||||
export function handleUnsavedChanges(
|
export function handleUnsavedChanges(
|
||||||
intl: IntlShape,
|
intl: IntlShape,
|
||||||
basepath: string,
|
basepath: string,
|
||||||
@@ -394,6 +446,10 @@ const NavUtils = {
|
|||||||
makeMovieScenesUrl,
|
makeMovieScenesUrl,
|
||||||
makeChildStudiosUrl,
|
makeChildStudiosUrl,
|
||||||
makeGalleryImagesUrl,
|
makeGalleryImagesUrl,
|
||||||
|
makeDirectorScenesUrl,
|
||||||
|
makePhotographerGalleriesUrl,
|
||||||
|
makePhotographerImagesUrl,
|
||||||
|
makeDirectorMoviesUrl,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default NavUtils;
|
export default NavUtils;
|
||||||
|
|||||||
Reference in New Issue
Block a user