mirror of https://github.com/stashapp/stash.git
Rename Movie to Group in UI (#4963)
* Replace movies with groups in the UI * Massage menu items * Change view names * Rename Movie components to Group * Refactor movie to group variable names * Rename movie class names to group
This commit is contained in:
parent
d986a9eb4f
commit
af6841be49
|
@ -66,7 +66,7 @@ const Galleries = lazyComponent(
|
|||
() => import("./components/Galleries/Galleries")
|
||||
);
|
||||
|
||||
const Movies = lazyComponent(() => import("./components/Movies/Movies"));
|
||||
const Groups = lazyComponent(() => import("./components/Movies/Movies"));
|
||||
const Tags = lazyComponent(() => import("./components/Tags/Tags"));
|
||||
const Images = lazyComponent(() => import("./components/Images/Images"));
|
||||
const Setup = lazyComponent(() => import("./components/Setup/Setup"));
|
||||
|
@ -312,7 +312,7 @@ export const App: React.FC = () => {
|
|||
<Route path="/performers" component={Performers} />
|
||||
<Route path="/tags" component={Tags} />
|
||||
<Route path="/studios" component={Studios} />
|
||||
<Route path="/movies" component={Movies} />
|
||||
<Route path="/groups" component={Groups} />
|
||||
<Route path="/stats" component={Stats} />
|
||||
<Route path="/settings" component={Settings} />
|
||||
<Route
|
||||
|
|
|
@ -7,7 +7,7 @@ import { ConfigurationContext } from "src/hooks/Config";
|
|||
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||
import { GalleryRecommendationRow } from "../Galleries/GalleryRecommendationRow";
|
||||
import { ImageRecommendationRow } from "../Images/ImageRecommendationRow";
|
||||
import { MovieRecommendationRow } from "../Movies/MovieRecommendationRow";
|
||||
import { GroupRecommendationRow } from "../Movies/MovieRecommendationRow";
|
||||
import { PerformerRecommendationRow } from "../Performers/PerformerRecommendationRow";
|
||||
import { SceneRecommendationRow } from "../Scenes/SceneRecommendationRow";
|
||||
import { StudioRecommendationRow } from "../Studios/StudioRecommendationRow";
|
||||
|
@ -45,7 +45,7 @@ const RecommendationRow: React.FC<IFilter> = ({ mode, filter, header }) => {
|
|||
);
|
||||
case GQL.FilterMode.Movies:
|
||||
return (
|
||||
<MovieRecommendationRow
|
||||
<GroupRecommendationRow
|
||||
isTouch={isTouch}
|
||||
filter={filter}
|
||||
header={header}
|
||||
|
|
|
@ -22,7 +22,7 @@ interface IAddSavedFilterModalProps {
|
|||
const FilterModeToMessageID = {
|
||||
[GQL.FilterMode.Galleries]: "galleries",
|
||||
[GQL.FilterMode.Images]: "images",
|
||||
[GQL.FilterMode.Movies]: "movies",
|
||||
[GQL.FilterMode.Movies]: "groups",
|
||||
[GQL.FilterMode.Performers]: "performers",
|
||||
[GQL.FilterMode.SceneMarkers]: "markers",
|
||||
[GQL.FilterMode.Scenes]: "scenes",
|
||||
|
|
|
@ -111,7 +111,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.movie-skeleton {
|
||||
.group-skeleton {
|
||||
max-width: 240px;
|
||||
min-height: 540px;
|
||||
min-width: 240px;
|
||||
|
@ -313,7 +313,7 @@
|
|||
width: 20rem;
|
||||
}
|
||||
|
||||
.slick-list .movie-card.card {
|
||||
.slick-list .group-card.card {
|
||||
width: 16rem;
|
||||
}
|
||||
|
||||
|
|
|
@ -193,7 +193,7 @@ const CriterionOptionList: React.FC<ICriterionList> = ({
|
|||
const FilterModeToConfigKey = {
|
||||
[FilterMode.Galleries]: "galleries",
|
||||
[FilterMode.Images]: "images",
|
||||
[FilterMode.Movies]: "movies",
|
||||
[FilterMode.Movies]: "groups",
|
||||
[FilterMode.Performers]: "performers",
|
||||
[FilterMode.SceneMarkers]: "sceneMarkers",
|
||||
[FilterMode.Scenes]: "scenes",
|
||||
|
|
|
@ -24,7 +24,7 @@ export const LabeledIdFilter: React.FC<ILabeledIdFilterProps> = ({
|
|||
inputType !== "performer_tags" &&
|
||||
inputType !== "tags" &&
|
||||
inputType !== "scenes" &&
|
||||
inputType !== "movies" &&
|
||||
inputType !== "groups" &&
|
||||
inputType !== "galleries"
|
||||
) {
|
||||
return null;
|
||||
|
|
|
@ -2,7 +2,7 @@ export enum View {
|
|||
Galleries = "galleries",
|
||||
Images = "images",
|
||||
Scenes = "scenes",
|
||||
Movies = "movies",
|
||||
Groups = "groups",
|
||||
Performers = "performers",
|
||||
Tags = "tags",
|
||||
SceneMarkers = "scene_markers",
|
||||
|
@ -17,7 +17,7 @@ export enum View {
|
|||
PerformerScenes = "performer_scenes",
|
||||
PerformerGalleries = "performer_galleries",
|
||||
PerformerImages = "performer_images",
|
||||
PerformerMovies = "performer_movies",
|
||||
PerformerGroups = "performer_groups",
|
||||
PerformerAppearsWith = "performer_appears_with",
|
||||
|
||||
StudioGalleries = "studio_galleries",
|
||||
|
@ -26,9 +26,9 @@ export enum View {
|
|||
GalleryImages = "gallery_images",
|
||||
|
||||
StudioScenes = "studio_scenes",
|
||||
StudioMovies = "studio_movies",
|
||||
StudioGroups = "studio_groups",
|
||||
StudioPerformers = "studio_performers",
|
||||
StudioChildren = "studio_children",
|
||||
|
||||
MovieScenes = "movie_scenes",
|
||||
GroupScenes = "group_scenes",
|
||||
}
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
import React, { useEffect, useRef, useState, useCallback } from "react";
|
||||
import React, {
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
useCallback,
|
||||
useMemo,
|
||||
} from "react";
|
||||
import {
|
||||
defineMessages,
|
||||
FormattedMessage,
|
||||
|
@ -52,9 +58,9 @@ const messages = defineMessages({
|
|||
id: "images",
|
||||
defaultMessage: "Images",
|
||||
},
|
||||
movies: {
|
||||
id: "movies",
|
||||
defaultMessage: "Movies",
|
||||
groups: {
|
||||
id: "groups",
|
||||
defaultMessage: "Groups",
|
||||
},
|
||||
markers: {
|
||||
id: "markers",
|
||||
|
@ -107,9 +113,9 @@ const allMenuItems: IMenuItem[] = [
|
|||
hotkey: "g i",
|
||||
},
|
||||
{
|
||||
name: "movies",
|
||||
message: messages.movies,
|
||||
href: "/movies",
|
||||
name: "groups",
|
||||
message: messages.groups,
|
||||
href: "/groups",
|
||||
icon: faFilm,
|
||||
hotkey: "g v",
|
||||
userCreatable: true,
|
||||
|
@ -179,20 +185,26 @@ export const MainNavbar: React.FC = () => {
|
|||
const { configuration, loading } = React.useContext(ConfigurationContext);
|
||||
const { openManual } = React.useContext(ManualStateContext);
|
||||
|
||||
// Show all menu items by default, unless config says otherwise
|
||||
const [menuItems, setMenuItems] = useState<IMenuItem[]>(allMenuItems);
|
||||
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const iCfg = configuration?.interface;
|
||||
if (iCfg?.menuItems) {
|
||||
setMenuItems(
|
||||
allMenuItems.filter((menuItem) =>
|
||||
iCfg.menuItems!.includes(menuItem.name)
|
||||
)
|
||||
);
|
||||
// Show all menu items by default, unless config says otherwise
|
||||
const menuItems = useMemo(() => {
|
||||
let cfgMenuItems = configuration?.interface.menuItems;
|
||||
if (!cfgMenuItems) {
|
||||
return allMenuItems;
|
||||
}
|
||||
|
||||
// translate old movies menu item to groups
|
||||
cfgMenuItems = cfgMenuItems.map((item) => {
|
||||
if (item === "movies") {
|
||||
return "groups";
|
||||
}
|
||||
return item;
|
||||
});
|
||||
|
||||
return allMenuItems.filter((menuItem) =>
|
||||
cfgMenuItems!.includes(menuItem.name)
|
||||
);
|
||||
}, [configuration]);
|
||||
|
||||
// react-bootstrap typing bug
|
||||
|
|
|
@ -24,7 +24,7 @@ interface IListOperationProps {
|
|||
onClose: (applied: boolean) => void;
|
||||
}
|
||||
|
||||
export const EditMoviesDialog: React.FC<IListOperationProps> = (
|
||||
export const EditGroupsDialog: React.FC<IListOperationProps> = (
|
||||
props: IListOperationProps
|
||||
) => {
|
||||
const intl = useIntl();
|
||||
|
@ -69,7 +69,7 @@ export const EditMoviesDialog: React.FC<IListOperationProps> = (
|
|||
intl.formatMessage(
|
||||
{ id: "toast.updated_entity" },
|
||||
{
|
||||
entity: intl.formatMessage({ id: "movies" }).toLocaleLowerCase(),
|
||||
entity: intl.formatMessage({ id: "groups" }).toLocaleLowerCase(),
|
||||
}
|
||||
)
|
||||
);
|
||||
|
@ -126,7 +126,7 @@ export const EditMoviesDialog: React.FC<IListOperationProps> = (
|
|||
icon={faPencilAlt}
|
||||
header={intl.formatMessage(
|
||||
{ id: "actions.edit_entity" },
|
||||
{ entityType: intl.formatMessage({ id: "movies" }) }
|
||||
{ entityType: intl.formatMessage({ id: "groups" }) }
|
||||
)}
|
||||
accept={{
|
||||
onClick: onSave,
|
||||
|
|
|
@ -12,7 +12,7 @@ import { faPlayCircle, faTag } from "@fortawesome/free-solid-svg-icons";
|
|||
import ScreenUtils from "src/utils/screen";
|
||||
|
||||
interface IProps {
|
||||
movie: GQL.MovieDataFragment;
|
||||
group: GQL.MovieDataFragment;
|
||||
containerWidth?: number;
|
||||
sceneIndex?: number;
|
||||
selecting?: boolean;
|
||||
|
@ -20,8 +20,8 @@ interface IProps {
|
|||
onSelectedChanged?: (selected: boolean, shiftKey: boolean) => void;
|
||||
}
|
||||
|
||||
export const MovieCard: React.FC<IProps> = ({
|
||||
movie,
|
||||
export const GroupCard: React.FC<IProps> = ({
|
||||
group,
|
||||
sceneIndex,
|
||||
containerWidth,
|
||||
selecting,
|
||||
|
@ -47,7 +47,7 @@ export const MovieCard: React.FC<IProps> = ({
|
|||
return (
|
||||
<>
|
||||
<hr />
|
||||
<span className="movie-scene-number">
|
||||
<span className="group-scene-number">
|
||||
<FormattedMessage id="scene" /> #{sceneIndex}
|
||||
</span>
|
||||
</>
|
||||
|
@ -55,9 +55,9 @@ export const MovieCard: React.FC<IProps> = ({
|
|||
}
|
||||
|
||||
function maybeRenderScenesPopoverButton() {
|
||||
if (movie.scenes.length === 0) return;
|
||||
if (group.scenes.length === 0) return;
|
||||
|
||||
const popoverContent = movie.scenes.map((scene) => (
|
||||
const popoverContent = group.scenes.map((scene) => (
|
||||
<SceneLink key={scene.id} scene={scene} />
|
||||
));
|
||||
|
||||
|
@ -69,31 +69,31 @@ export const MovieCard: React.FC<IProps> = ({
|
|||
>
|
||||
<Button className="minimal">
|
||||
<Icon icon={faPlayCircle} />
|
||||
<span>{movie.scenes.length}</span>
|
||||
<span>{group.scenes.length}</span>
|
||||
</Button>
|
||||
</HoverPopover>
|
||||
);
|
||||
}
|
||||
|
||||
function maybeRenderTagPopoverButton() {
|
||||
if (movie.tags.length <= 0) return;
|
||||
if (group.tags.length <= 0) return;
|
||||
|
||||
const popoverContent = movie.tags.map((tag) => (
|
||||
<TagLink key={tag.id} linkType="movie" tag={tag} />
|
||||
const popoverContent = group.tags.map((tag) => (
|
||||
<TagLink key={tag.id} linkType="group" tag={tag} />
|
||||
));
|
||||
|
||||
return (
|
||||
<HoverPopover placement="bottom" content={popoverContent}>
|
||||
<Button className="minimal tag-count">
|
||||
<Icon icon={faTag} />
|
||||
<span>{movie.tags.length}</span>
|
||||
<span>{group.tags.length}</span>
|
||||
</Button>
|
||||
</HoverPopover>
|
||||
);
|
||||
}
|
||||
|
||||
function maybeRenderPopoverButtonGroup() {
|
||||
if (sceneIndex || movie.scenes.length > 0 || movie.tags.length > 0) {
|
||||
if (sceneIndex || group.scenes.length > 0 || group.tags.length > 0) {
|
||||
return (
|
||||
<>
|
||||
{maybeRenderSceneNumber()}
|
||||
|
@ -109,28 +109,28 @@ export const MovieCard: React.FC<IProps> = ({
|
|||
|
||||
return (
|
||||
<GridCard
|
||||
className="movie-card"
|
||||
url={`/movies/${movie.id}`}
|
||||
className="group-card"
|
||||
url={`/groups/${group.id}`}
|
||||
width={cardWidth}
|
||||
title={movie.name}
|
||||
linkClassName="movie-card-header"
|
||||
title={group.name}
|
||||
linkClassName="group-card-header"
|
||||
image={
|
||||
<>
|
||||
<img
|
||||
loading="lazy"
|
||||
className="movie-card-image"
|
||||
alt={movie.name ?? ""}
|
||||
src={movie.front_image_path ?? ""}
|
||||
className="group-card-image"
|
||||
alt={group.name ?? ""}
|
||||
src={group.front_image_path ?? ""}
|
||||
/>
|
||||
<RatingBanner rating={movie.rating100} />
|
||||
<RatingBanner rating={group.rating100} />
|
||||
</>
|
||||
}
|
||||
details={
|
||||
<div className="movie-card__details">
|
||||
<span className="movie-card__date">{movie.date}</span>
|
||||
<div className="group-card__details">
|
||||
<span className="group-card__date">{group.date}</span>
|
||||
<TruncatedText
|
||||
className="movie-card__description"
|
||||
text={movie.synopsis}
|
||||
className="group-card__description"
|
||||
text={group.synopsis}
|
||||
lineCount={3}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
import React from "react";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { MovieCard } from "./MovieCard";
|
||||
import { GroupCard } from "./MovieCard";
|
||||
import { useContainerDimensions } from "../Shared/GridCard/GridCard";
|
||||
|
||||
interface IMovieCardGrid {
|
||||
movies: GQL.MovieDataFragment[];
|
||||
interface IGroupCardGrid {
|
||||
groups: GQL.MovieDataFragment[];
|
||||
selectedIds: Set<string>;
|
||||
onSelectChange: (id: string, selected: boolean, shiftKey: boolean) => void;
|
||||
}
|
||||
|
||||
export const MovieCardGrid: React.FC<IMovieCardGrid> = ({
|
||||
movies,
|
||||
export const GroupCardGrid: React.FC<IGroupCardGrid> = ({
|
||||
groups,
|
||||
selectedIds,
|
||||
onSelectChange,
|
||||
}) => {
|
||||
const [componentRef, { width }] = useContainerDimensions();
|
||||
return (
|
||||
<div className="row justify-content-center" ref={componentRef}>
|
||||
{movies.map((p) => (
|
||||
<MovieCard
|
||||
{groups.map((p) => (
|
||||
<GroupCard
|
||||
key={p.id}
|
||||
containerWidth={width}
|
||||
movie={p}
|
||||
group={p}
|
||||
selecting={selectedIds.size > 0}
|
||||
selected={selectedIds.has(p.id)}
|
||||
onSelectedChanged={(selected: boolean, shiftKey: boolean) =>
|
||||
|
|
|
@ -17,12 +17,12 @@ import { LoadingIndicator } from "src/components/Shared/LoadingIndicator";
|
|||
import { useLightbox } from "src/hooks/Lightbox/hooks";
|
||||
import { ModalComponent } from "src/components/Shared/Modal";
|
||||
import { useToast } from "src/hooks/Toast";
|
||||
import { MovieScenesPanel } from "./MovieScenesPanel";
|
||||
import { GroupScenesPanel } from "./MovieScenesPanel";
|
||||
import {
|
||||
CompressedMovieDetailsPanel,
|
||||
MovieDetailsPanel,
|
||||
GroupDetailsPanel,
|
||||
} from "./MovieDetailsPanel";
|
||||
import { MovieEditPanel } from "./MovieEditPanel";
|
||||
import { GroupEditPanel } from "./MovieEditPanel";
|
||||
import {
|
||||
faChevronDown,
|
||||
faChevronUp,
|
||||
|
@ -38,14 +38,14 @@ import { useScrollToTopOnMount } from "src/hooks/scrollToTop";
|
|||
import { ExternalLinksButton } from "src/components/Shared/ExternalLinksButton";
|
||||
|
||||
interface IProps {
|
||||
movie: GQL.MovieDataFragment;
|
||||
group: GQL.MovieDataFragment;
|
||||
}
|
||||
|
||||
interface IMovieParams {
|
||||
interface IGroupParams {
|
||||
id: string;
|
||||
}
|
||||
|
||||
const MoviePage: React.FC<IProps> = ({ movie }) => {
|
||||
const GroupPage: React.FC<IProps> = ({ group }) => {
|
||||
const intl = useIntl();
|
||||
const history = useHistory();
|
||||
const Toast = useToast();
|
||||
|
@ -70,35 +70,35 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
|
|||
const [encodingImage, setEncodingImage] = useState<boolean>(false);
|
||||
|
||||
const defaultImage =
|
||||
movie.front_image_path && movie.front_image_path.includes("default=true")
|
||||
group.front_image_path && group.front_image_path.includes("default=true")
|
||||
? true
|
||||
: false;
|
||||
|
||||
const lightboxImages = useMemo(() => {
|
||||
const covers = [
|
||||
...(movie.front_image_path && !defaultImage
|
||||
...(group.front_image_path && !defaultImage
|
||||
? [
|
||||
{
|
||||
paths: {
|
||||
thumbnail: movie.front_image_path,
|
||||
image: movie.front_image_path,
|
||||
thumbnail: group.front_image_path,
|
||||
image: group.front_image_path,
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(movie.back_image_path
|
||||
...(group.back_image_path
|
||||
? [
|
||||
{
|
||||
paths: {
|
||||
thumbnail: movie.back_image_path,
|
||||
image: movie.back_image_path,
|
||||
thumbnail: group.back_image_path,
|
||||
image: group.back_image_path,
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
return covers;
|
||||
}, [movie.front_image_path, movie.back_image_path, defaultImage]);
|
||||
}, [group.front_image_path, group.back_image_path, defaultImage]);
|
||||
|
||||
const index = lightboxImages.length;
|
||||
|
||||
|
@ -108,7 +108,7 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
|
|||
|
||||
const [updateMovie, { loading: updating }] = useMovieUpdate();
|
||||
const [deleteMovie, { loading: deleting }] = useMovieDestroy({
|
||||
id: movie.id,
|
||||
id: group.id,
|
||||
});
|
||||
|
||||
// set up hotkeys
|
||||
|
@ -135,7 +135,7 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
|
|||
await updateMovie({
|
||||
variables: {
|
||||
input: {
|
||||
id: movie.id,
|
||||
id: group.id,
|
||||
...input,
|
||||
},
|
||||
},
|
||||
|
@ -144,7 +144,7 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
|
|||
Toast.success(
|
||||
intl.formatMessage(
|
||||
{ id: "toast.updated_entity" },
|
||||
{ entity: intl.formatMessage({ id: "movie" }).toLocaleLowerCase() }
|
||||
{ entity: intl.formatMessage({ id: "group" }).toLocaleLowerCase() }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -157,7 +157,7 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
|
|||
}
|
||||
|
||||
// redirect to movies page
|
||||
history.push(`/movies`);
|
||||
history.push(`/groups`);
|
||||
}
|
||||
|
||||
function toggleEditing(value?: boolean) {
|
||||
|
@ -187,8 +187,8 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
|
|||
id="dialogs.delete_confirm"
|
||||
values={{
|
||||
entityName:
|
||||
movie.name ??
|
||||
intl.formatMessage({ id: "movie" }).toLocaleLowerCase(),
|
||||
group.name ??
|
||||
intl.formatMessage({ id: "group" }).toLocaleLowerCase(),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
|
@ -216,7 +216,7 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
|
|||
}
|
||||
|
||||
function renderFrontImage() {
|
||||
let image = movie.front_image_path;
|
||||
let image = group.front_image_path;
|
||||
if (isEditing) {
|
||||
if (frontImage === null && image) {
|
||||
const imageURL = new URL(image);
|
||||
|
@ -229,14 +229,14 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
|
|||
|
||||
if (image && defaultImage) {
|
||||
return (
|
||||
<div className="movie-image-container">
|
||||
<div className="group-image-container">
|
||||
<DetailImage alt="Front Cover" src={image} />
|
||||
</div>
|
||||
);
|
||||
} else if (image) {
|
||||
return (
|
||||
<Button
|
||||
className="movie-image-container"
|
||||
className="group-image-container"
|
||||
variant="link"
|
||||
onClick={() => showLightbox()}
|
||||
>
|
||||
|
@ -247,7 +247,7 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
|
|||
}
|
||||
|
||||
function renderBackImage() {
|
||||
let image = movie.back_image_path;
|
||||
let image = group.back_image_path;
|
||||
if (isEditing) {
|
||||
if (backImage === null) {
|
||||
image = undefined;
|
||||
|
@ -259,7 +259,7 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
|
|||
if (image) {
|
||||
return (
|
||||
<Button
|
||||
className="movie-image-container"
|
||||
className="group-image-container"
|
||||
variant="link"
|
||||
onClick={() => showLightbox(index - 1)}
|
||||
>
|
||||
|
@ -271,26 +271,26 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
|
|||
|
||||
const renderClickableIcons = () => (
|
||||
<span className="name-icons">
|
||||
{movie.urls.length > 0 && <ExternalLinksButton urls={movie.urls} />}
|
||||
{group.urls.length > 0 && <ExternalLinksButton urls={group.urls} />}
|
||||
</span>
|
||||
);
|
||||
|
||||
function maybeRenderAliases() {
|
||||
if (movie?.aliases) {
|
||||
if (group?.aliases) {
|
||||
return (
|
||||
<div>
|
||||
<span className="alias-head">{movie?.aliases}</span>
|
||||
<span className="alias-head">{group?.aliases}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function setRating(v: number | null) {
|
||||
if (movie.id) {
|
||||
if (group.id) {
|
||||
updateMovie({
|
||||
variables: {
|
||||
input: {
|
||||
id: movie.id,
|
||||
id: group.id,
|
||||
rating100: v,
|
||||
},
|
||||
},
|
||||
|
@ -298,13 +298,13 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
|
|||
}
|
||||
}
|
||||
|
||||
const renderTabs = () => <MovieScenesPanel active={true} movie={movie} />;
|
||||
const renderTabs = () => <GroupScenesPanel active={true} group={group} />;
|
||||
|
||||
function maybeRenderDetails() {
|
||||
if (!isEditing) {
|
||||
return (
|
||||
<MovieDetailsPanel
|
||||
movie={movie}
|
||||
<GroupDetailsPanel
|
||||
group={group}
|
||||
collapsed={collapsed}
|
||||
fullWidth={!collapsed && !compactExpandedDetails}
|
||||
/>
|
||||
|
@ -315,8 +315,8 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
|
|||
function maybeRenderEditPanel() {
|
||||
if (isEditing) {
|
||||
return (
|
||||
<MovieEditPanel
|
||||
movie={movie}
|
||||
<GroupEditPanel
|
||||
group={group}
|
||||
onSubmit={onSave}
|
||||
onCancel={() => toggleEditing()}
|
||||
onDelete={onDelete}
|
||||
|
@ -329,7 +329,7 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
|
|||
{
|
||||
return (
|
||||
<DetailsEditNavbar
|
||||
objectName={movie.name}
|
||||
objectName={group.name}
|
||||
isNew={false}
|
||||
isEditing={isEditing}
|
||||
onToggleEdit={() => toggleEditing()}
|
||||
|
@ -343,12 +343,12 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
|
|||
|
||||
function maybeRenderCompressedDetails() {
|
||||
if (!isEditing && loadStickyHeader) {
|
||||
return <CompressedMovieDetailsPanel movie={movie} />;
|
||||
return <CompressedMovieDetailsPanel group={group} />;
|
||||
}
|
||||
}
|
||||
|
||||
function maybeRenderHeaderBackgroundImage() {
|
||||
let image = movie.front_image_path;
|
||||
let image = group.front_image_path;
|
||||
if (enableBackgroundImage && !isEditing && image) {
|
||||
const imageURL = new URL(image);
|
||||
let isDefaultImage = imageURL.searchParams.get("default");
|
||||
|
@ -360,7 +360,7 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
|
|||
<img
|
||||
className="background-image"
|
||||
src={image}
|
||||
alt={`${movie.name} background`}
|
||||
alt={`${group.name} background`}
|
||||
/>
|
||||
</picture>
|
||||
</div>
|
||||
|
@ -384,9 +384,9 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
|
|||
});
|
||||
|
||||
return (
|
||||
<div id="movie-page" className="row">
|
||||
<div id="group-page" className="row">
|
||||
<Helmet>
|
||||
<title>{movie?.name}</title>
|
||||
<title>{group?.name}</title>
|
||||
</Helmet>
|
||||
|
||||
<div className={headerClassName}>
|
||||
|
@ -399,7 +399,7 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
|
|||
message={intl.formatMessage({ id: "actions.encoding_image" })}
|
||||
/>
|
||||
) : (
|
||||
<div className="movie-images">
|
||||
<div className="group-images">
|
||||
{renderFrontImage()}
|
||||
{renderBackImage()}
|
||||
</div>
|
||||
|
@ -407,15 +407,15 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
|
|||
</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="movie-head col">
|
||||
<div className="group-head col">
|
||||
<h2>
|
||||
<span className="movie-name">{movie.name}</span>
|
||||
<span className="group-name">{group.name}</span>
|
||||
{maybeRenderShowCollapseButton()}
|
||||
{renderClickableIcons()}
|
||||
</h2>
|
||||
{maybeRenderAliases()}
|
||||
<RatingSystem
|
||||
value={movie.rating100}
|
||||
value={group.rating100}
|
||||
onSetRating={(value) => setRating(value)}
|
||||
clickToRate
|
||||
withoutContext
|
||||
|
@ -428,8 +428,8 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
|
|||
</div>
|
||||
{maybeRenderCompressedDetails()}
|
||||
<div className="detail-body">
|
||||
<div className="movie-body">
|
||||
<div className="movie-tabs">{maybeRenderTab()}</div>
|
||||
<div className="group-body">
|
||||
<div className="group-tabs">{maybeRenderTab()}</div>
|
||||
</div>
|
||||
</div>
|
||||
{renderDeleteAlert()}
|
||||
|
@ -437,7 +437,7 @@ const MoviePage: React.FC<IProps> = ({ movie }) => {
|
|||
);
|
||||
};
|
||||
|
||||
const MovieLoader: React.FC<RouteComponentProps<IMovieParams>> = ({
|
||||
const GroupLoader: React.FC<RouteComponentProps<IGroupParams>> = ({
|
||||
match,
|
||||
}) => {
|
||||
const { id } = match.params;
|
||||
|
@ -450,7 +450,7 @@ const MovieLoader: React.FC<RouteComponentProps<IMovieParams>> = ({
|
|||
if (!data?.findMovie)
|
||||
return <ErrorMessage error={`No movie found with id ${id}.`} />;
|
||||
|
||||
return <MoviePage movie={data.findMovie} />;
|
||||
return <GroupPage group={data.findMovie} />;
|
||||
};
|
||||
|
||||
export default MovieLoader;
|
||||
export default GroupLoader;
|
||||
|
|
|
@ -5,16 +5,16 @@ import { useHistory, useLocation } from "react-router-dom";
|
|||
import { useIntl } from "react-intl";
|
||||
import { LoadingIndicator } from "src/components/Shared/LoadingIndicator";
|
||||
import { useToast } from "src/hooks/Toast";
|
||||
import { MovieEditPanel } from "./MovieEditPanel";
|
||||
import { GroupEditPanel } from "./MovieEditPanel";
|
||||
|
||||
const MovieCreate: React.FC = () => {
|
||||
const GroupCreate: React.FC = () => {
|
||||
const history = useHistory();
|
||||
const intl = useIntl();
|
||||
const Toast = useToast();
|
||||
|
||||
const location = useLocation();
|
||||
const query = useMemo(() => new URLSearchParams(location.search), [location]);
|
||||
const movie = {
|
||||
const group = {
|
||||
name: query.get("q") ?? undefined,
|
||||
};
|
||||
|
||||
|
@ -30,7 +30,7 @@ const MovieCreate: React.FC = () => {
|
|||
variables: { input },
|
||||
});
|
||||
if (result.data?.movieCreate?.id) {
|
||||
history.push(`/movies/${result.data.movieCreate.id}`);
|
||||
history.push(`/groups/${result.data.movieCreate.id}`);
|
||||
Toast.success(
|
||||
intl.formatMessage(
|
||||
{ id: "toast.created_entity" },
|
||||
|
@ -43,7 +43,7 @@ const MovieCreate: React.FC = () => {
|
|||
function renderFrontImage() {
|
||||
if (frontImage) {
|
||||
return (
|
||||
<div className="movie-image-container">
|
||||
<div className="group-image-container">
|
||||
<img alt="Front Cover" src={frontImage} />
|
||||
</div>
|
||||
);
|
||||
|
@ -53,7 +53,7 @@ const MovieCreate: React.FC = () => {
|
|||
function renderBackImage() {
|
||||
if (backImage) {
|
||||
return (
|
||||
<div className="movie-image-container">
|
||||
<div className="group-image-container">
|
||||
<img alt="Back Cover" src={backImage} />
|
||||
</div>
|
||||
);
|
||||
|
@ -63,24 +63,24 @@ const MovieCreate: React.FC = () => {
|
|||
// TODO: CSS class
|
||||
return (
|
||||
<div className="row">
|
||||
<div className="movie-details mb-3 col">
|
||||
<div className="group-details mb-3 col">
|
||||
<div className="logo w-100">
|
||||
{encodingImage ? (
|
||||
<LoadingIndicator
|
||||
message={intl.formatMessage({ id: "actions.encoding_image" })}
|
||||
/>
|
||||
) : (
|
||||
<div className="movie-images">
|
||||
<div className="group-images">
|
||||
{renderFrontImage()}
|
||||
{renderBackImage()}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<MovieEditPanel
|
||||
movie={movie}
|
||||
<GroupEditPanel
|
||||
group={group}
|
||||
onSubmit={onSave}
|
||||
onCancel={() => history.push("/movies")}
|
||||
onCancel={() => history.push("/groups")}
|
||||
onDelete={() => {}}
|
||||
setFrontImage={setFrontImage}
|
||||
setBackImage={setBackImage}
|
||||
|
@ -91,4 +91,4 @@ const MovieCreate: React.FC = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export default MovieCreate;
|
||||
export default GroupCreate;
|
||||
|
|
|
@ -7,14 +7,14 @@ import { Link } from "react-router-dom";
|
|||
import { DirectorLink } from "src/components/Shared/Link";
|
||||
import { TagLink } from "src/components/Shared/TagLink";
|
||||
|
||||
interface IMovieDetailsPanel {
|
||||
movie: GQL.MovieDataFragment;
|
||||
interface IGroupDetailsPanel {
|
||||
group: GQL.MovieDataFragment;
|
||||
collapsed?: boolean;
|
||||
fullWidth?: boolean;
|
||||
}
|
||||
|
||||
export const MovieDetailsPanel: React.FC<IMovieDetailsPanel> = ({
|
||||
movie,
|
||||
export const GroupDetailsPanel: React.FC<IGroupDetailsPanel> = ({
|
||||
group,
|
||||
collapsed,
|
||||
fullWidth,
|
||||
}) => {
|
||||
|
@ -22,13 +22,13 @@ export const MovieDetailsPanel: React.FC<IMovieDetailsPanel> = ({
|
|||
const intl = useIntl();
|
||||
|
||||
function renderTagsField() {
|
||||
if (!movie.tags.length) {
|
||||
if (!group.tags.length) {
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<ul className="pl-0">
|
||||
{(movie.tags ?? []).map((tag) => (
|
||||
<TagLink key={tag.id} linkType="movie" tag={tag} />
|
||||
{(group.tags ?? []).map((tag) => (
|
||||
<TagLink key={tag.id} linkType="group" tag={tag} />
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
|
@ -40,7 +40,7 @@ export const MovieDetailsPanel: React.FC<IMovieDetailsPanel> = ({
|
|||
<>
|
||||
<DetailItem
|
||||
id="synopsis"
|
||||
value={movie.synopsis}
|
||||
value={group.synopsis}
|
||||
fullWidth={fullWidth}
|
||||
/>
|
||||
<DetailItem
|
||||
|
@ -58,21 +58,21 @@ export const MovieDetailsPanel: React.FC<IMovieDetailsPanel> = ({
|
|||
<DetailItem
|
||||
id="duration"
|
||||
value={
|
||||
movie.duration ? TextUtils.secondsToTimestamp(movie.duration) : ""
|
||||
group.duration ? TextUtils.secondsToTimestamp(group.duration) : ""
|
||||
}
|
||||
fullWidth={fullWidth}
|
||||
/>
|
||||
<DetailItem
|
||||
id="date"
|
||||
value={movie.date ? TextUtils.formatDate(intl, movie.date) : ""}
|
||||
value={group.date ? TextUtils.formatDate(intl, group.date) : ""}
|
||||
fullWidth={fullWidth}
|
||||
/>
|
||||
<DetailItem
|
||||
id="studio"
|
||||
value={
|
||||
movie.studio?.id ? (
|
||||
<Link to={`/studios/${movie.studio?.id}`}>
|
||||
{movie.studio?.name}
|
||||
group.studio?.id ? (
|
||||
<Link to={`/studios/${group.studio?.id}`}>
|
||||
{group.studio?.name}
|
||||
</Link>
|
||||
) : (
|
||||
""
|
||||
|
@ -84,8 +84,8 @@ export const MovieDetailsPanel: React.FC<IMovieDetailsPanel> = ({
|
|||
<DetailItem
|
||||
id="director"
|
||||
value={
|
||||
movie.director ? (
|
||||
<DirectorLink director={movie.director} linkType="movie" />
|
||||
group.director ? (
|
||||
<DirectorLink director={group.director} linkType="group" />
|
||||
) : (
|
||||
""
|
||||
)
|
||||
|
@ -97,8 +97,8 @@ export const MovieDetailsPanel: React.FC<IMovieDetailsPanel> = ({
|
|||
);
|
||||
};
|
||||
|
||||
export const CompressedMovieDetailsPanel: React.FC<IMovieDetailsPanel> = ({
|
||||
movie,
|
||||
export const CompressedMovieDetailsPanel: React.FC<IGroupDetailsPanel> = ({
|
||||
group,
|
||||
}) => {
|
||||
function scrollToTop() {
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
|
@ -107,13 +107,13 @@ export const CompressedMovieDetailsPanel: React.FC<IMovieDetailsPanel> = ({
|
|||
return (
|
||||
<div className="sticky detail-header">
|
||||
<div className="sticky detail-header-group">
|
||||
<a className="movie-name" onClick={() => scrollToTop()}>
|
||||
{movie.name}
|
||||
<a className="group-name" onClick={() => scrollToTop()}>
|
||||
{group.name}
|
||||
</a>
|
||||
{movie?.studio?.name ? (
|
||||
{group?.studio?.name ? (
|
||||
<>
|
||||
<span className="detail-divider">/</span>
|
||||
<span className="movie-studio">{movie?.studio?.name}</span>
|
||||
<span className="group-studio">{group?.studio?.name}</span>
|
||||
</>
|
||||
) : (
|
||||
""
|
||||
|
|
|
@ -15,7 +15,7 @@ import TextUtils from "src/utils/text";
|
|||
import ImageUtils from "src/utils/image";
|
||||
import { useFormik } from "formik";
|
||||
import { Prompt } from "react-router-dom";
|
||||
import { MovieScrapeDialog } from "./MovieScrapeDialog";
|
||||
import { GroupScrapeDialog } from "./MovieScrapeDialog";
|
||||
import isEqual from "lodash-es/isEqual";
|
||||
import { handleUnsavedChanges } from "src/utils/navigation";
|
||||
import { formikUtils } from "src/utils/form";
|
||||
|
@ -27,8 +27,8 @@ import {
|
|||
import { Studio, StudioSelect } from "src/components/Studios/StudioSelect";
|
||||
import { useTagsEdit } from "src/hooks/tagsEdit";
|
||||
|
||||
interface IMovieEditPanel {
|
||||
movie: Partial<GQL.MovieDataFragment>;
|
||||
interface IGroupEditPanel {
|
||||
group: Partial<GQL.MovieDataFragment>;
|
||||
onSubmit: (movie: GQL.MovieCreateInput) => Promise<void>;
|
||||
onCancel: () => void;
|
||||
onDelete: () => void;
|
||||
|
@ -37,8 +37,8 @@ interface IMovieEditPanel {
|
|||
setEncodingImage: (loading: boolean) => void;
|
||||
}
|
||||
|
||||
export const MovieEditPanel: React.FC<IMovieEditPanel> = ({
|
||||
movie,
|
||||
export const GroupEditPanel: React.FC<IGroupEditPanel> = ({
|
||||
group,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
onDelete,
|
||||
|
@ -49,7 +49,7 @@ export const MovieEditPanel: React.FC<IMovieEditPanel> = ({
|
|||
const intl = useIntl();
|
||||
const Toast = useToast();
|
||||
|
||||
const isNew = movie.id === undefined;
|
||||
const isNew = group.id === undefined;
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isImageAlertOpen, setIsImageAlertOpen] = useState<boolean>(false);
|
||||
|
@ -57,7 +57,7 @@ export const MovieEditPanel: React.FC<IMovieEditPanel> = ({
|
|||
const [imageClipboard, setImageClipboard] = useState<string>();
|
||||
|
||||
const Scrapers = useListMovieScrapers();
|
||||
const [scrapedMovie, setScrapedMovie] = useState<GQL.ScrapedMovie>();
|
||||
const [scrapedGroup, setScrapedGroup] = useState<GQL.ScrapedMovie>();
|
||||
|
||||
const [studio, setStudio] = useState<Studio | null>(null);
|
||||
|
||||
|
@ -76,15 +76,15 @@ export const MovieEditPanel: React.FC<IMovieEditPanel> = ({
|
|||
});
|
||||
|
||||
const initialValues = {
|
||||
name: movie?.name ?? "",
|
||||
aliases: movie?.aliases ?? "",
|
||||
duration: movie?.duration ?? null,
|
||||
date: movie?.date ?? "",
|
||||
studio_id: movie?.studio?.id ?? null,
|
||||
tag_ids: (movie?.tags ?? []).map((t) => t.id),
|
||||
director: movie?.director ?? "",
|
||||
urls: movie?.urls ?? [],
|
||||
synopsis: movie?.synopsis ?? "",
|
||||
name: group?.name ?? "",
|
||||
aliases: group?.aliases ?? "",
|
||||
duration: group?.duration ?? null,
|
||||
date: group?.date ?? "",
|
||||
studio_id: group?.studio?.id ?? null,
|
||||
tag_ids: (group?.tags ?? []).map((t) => t.id),
|
||||
director: group?.director ?? "",
|
||||
urls: group?.urls ?? [],
|
||||
synopsis: group?.synopsis ?? "",
|
||||
};
|
||||
|
||||
type InputValues = yup.InferType<typeof schema>;
|
||||
|
@ -97,7 +97,7 @@ export const MovieEditPanel: React.FC<IMovieEditPanel> = ({
|
|||
});
|
||||
|
||||
const { tags, updateTagsStateFromScraper, tagsControl } = useTagsEdit(
|
||||
movie.tags,
|
||||
group.tags,
|
||||
(ids) => formik.setFieldValue("tag_ids", ids)
|
||||
);
|
||||
|
||||
|
@ -107,8 +107,8 @@ export const MovieEditPanel: React.FC<IMovieEditPanel> = ({
|
|||
}
|
||||
|
||||
useEffect(() => {
|
||||
setStudio(movie.studio ?? null);
|
||||
}, [movie.studio]);
|
||||
setStudio(group.studio ?? null);
|
||||
}, [group.studio]);
|
||||
|
||||
// set up hotkeys
|
||||
useEffect(() => {
|
||||
|
@ -128,7 +128,7 @@ export const MovieEditPanel: React.FC<IMovieEditPanel> = ({
|
|||
};
|
||||
});
|
||||
|
||||
function updateMovieEditStateFromScraper(
|
||||
function updateGroupEditStateFromScraper(
|
||||
state: Partial<GQL.ScrapedMovieDataFragment>
|
||||
) {
|
||||
if (state.name) {
|
||||
|
@ -200,11 +200,11 @@ export const MovieEditPanel: React.FC<IMovieEditPanel> = ({
|
|||
return;
|
||||
}
|
||||
|
||||
// if this is a new movie, just dump the data
|
||||
// if this is a new group, just dump the data
|
||||
if (isNew) {
|
||||
updateMovieEditStateFromScraper(result.data.scrapeMovieURL);
|
||||
updateGroupEditStateFromScraper(result.data.scrapeMovieURL);
|
||||
} else {
|
||||
setScrapedMovie(result.data.scrapeMovieURL);
|
||||
setScrapedGroup(result.data.scrapeMovieURL);
|
||||
}
|
||||
} catch (e) {
|
||||
Toast.error(e);
|
||||
|
@ -223,25 +223,25 @@ export const MovieEditPanel: React.FC<IMovieEditPanel> = ({
|
|||
}
|
||||
|
||||
function maybeRenderScrapeDialog() {
|
||||
if (!scrapedMovie) {
|
||||
if (!scrapedGroup) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentMovie = {
|
||||
id: movie.id!,
|
||||
const currentGroup = {
|
||||
id: group.id!,
|
||||
...formik.values,
|
||||
};
|
||||
|
||||
// Get image paths for scrape gui
|
||||
currentMovie.front_image = movie?.front_image_path;
|
||||
currentMovie.back_image = movie?.back_image_path;
|
||||
currentGroup.front_image = group?.front_image_path;
|
||||
currentGroup.back_image = group?.back_image_path;
|
||||
|
||||
return (
|
||||
<MovieScrapeDialog
|
||||
movie={currentMovie}
|
||||
movieStudio={studio}
|
||||
movieTags={tags}
|
||||
scraped={scrapedMovie}
|
||||
<GroupScrapeDialog
|
||||
group={currentGroup}
|
||||
groupStudio={studio}
|
||||
groupTags={tags}
|
||||
scraped={scrapedGroup}
|
||||
onClose={(m) => {
|
||||
onScrapeDialogClosed(m);
|
||||
}}
|
||||
|
@ -251,9 +251,9 @@ export const MovieEditPanel: React.FC<IMovieEditPanel> = ({
|
|||
|
||||
function onScrapeDialogClosed(p?: GQL.ScrapedMovieDataFragment) {
|
||||
if (p) {
|
||||
updateMovieEditStateFromScraper(p);
|
||||
updateGroupEditStateFromScraper(p);
|
||||
}
|
||||
setScrapedMovie(undefined);
|
||||
setScrapedGroup(undefined);
|
||||
}
|
||||
|
||||
const encodingImage = ImageUtils.usePasteImage(showImageAlert);
|
||||
|
@ -373,7 +373,7 @@ export const MovieEditPanel: React.FC<IMovieEditPanel> = ({
|
|||
<h2>
|
||||
{intl.formatMessage(
|
||||
{ id: "actions.add_entity" },
|
||||
{ entityType: intl.formatMessage({ id: "movie" }) }
|
||||
{ entityType: intl.formatMessage({ id: "group" }) }
|
||||
)}
|
||||
</h2>
|
||||
)}
|
||||
|
@ -382,14 +382,14 @@ export const MovieEditPanel: React.FC<IMovieEditPanel> = ({
|
|||
when={formik.dirty}
|
||||
message={(location, action) => {
|
||||
// Check if it's a redirect after movie creation
|
||||
if (action === "PUSH" && location.pathname.startsWith("/movies/"))
|
||||
if (action === "PUSH" && location.pathname.startsWith("/groups/"))
|
||||
return true;
|
||||
|
||||
return handleUnsavedChanges(intl, "movies", movie.id)(location);
|
||||
return handleUnsavedChanges(intl, "groups", group.id)(location);
|
||||
}}
|
||||
/>
|
||||
|
||||
<Form noValidate onSubmit={formik.handleSubmit} id="movie-edit">
|
||||
<Form noValidate onSubmit={formik.handleSubmit} id="group-edit">
|
||||
{renderInputField("name")}
|
||||
{renderInputField("aliases")}
|
||||
{renderDurationField("duration")}
|
||||
|
@ -402,7 +402,7 @@ export const MovieEditPanel: React.FC<IMovieEditPanel> = ({
|
|||
</Form>
|
||||
|
||||
<DetailsEditNavbar
|
||||
objectName={movie?.name ?? intl.formatMessage({ id: "movie" })}
|
||||
objectName={group?.name ?? intl.formatMessage({ id: "group" })}
|
||||
isNew={isNew}
|
||||
classNames="col-xl-9 mt-3"
|
||||
isEditing
|
||||
|
|
|
@ -5,17 +5,17 @@ import { ListFilterModel } from "src/models/list-filter/filter";
|
|||
import { SceneList } from "src/components/Scenes/SceneList";
|
||||
import { View } from "src/components/List/views";
|
||||
|
||||
interface IMovieScenesPanel {
|
||||
interface IGroupScenesPanel {
|
||||
active: boolean;
|
||||
movie: GQL.MovieDataFragment;
|
||||
group: GQL.MovieDataFragment;
|
||||
}
|
||||
|
||||
export const MovieScenesPanel: React.FC<IMovieScenesPanel> = ({
|
||||
export const GroupScenesPanel: React.FC<IGroupScenesPanel> = ({
|
||||
active,
|
||||
movie,
|
||||
group,
|
||||
}) => {
|
||||
function filterHook(filter: ListFilterModel) {
|
||||
const movieValue = { id: movie.id, label: movie.name };
|
||||
const movieValue = { id: group.id, label: group.name };
|
||||
// if movie is already present, then we modify it, otherwise add
|
||||
let movieCriterion = filter.criteria.find((c) => {
|
||||
return c.criterionOption.type === "movies";
|
||||
|
@ -29,7 +29,7 @@ export const MovieScenesPanel: React.FC<IMovieScenesPanel> = ({
|
|||
// add the movie if not present
|
||||
if (
|
||||
!movieCriterion.value.find((p) => {
|
||||
return p.id === movie.id;
|
||||
return p.id === group.id;
|
||||
})
|
||||
) {
|
||||
movieCriterion.value.push(movieValue);
|
||||
|
@ -46,13 +46,13 @@ export const MovieScenesPanel: React.FC<IMovieScenesPanel> = ({
|
|||
return filter;
|
||||
}
|
||||
|
||||
if (movie && movie.id) {
|
||||
if (group && group.id) {
|
||||
return (
|
||||
<SceneList
|
||||
filterHook={filterHook}
|
||||
defaultSort="movie_scene_number"
|
||||
alterQuery={active}
|
||||
view={View.MovieScenes}
|
||||
view={View.GroupScenes}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -20,33 +20,33 @@ import { uniq } from "lodash-es";
|
|||
import { Tag } from "src/components/Tags/TagSelect";
|
||||
import { useScrapedTags } from "src/components/Shared/ScrapeDialog/scrapedTags";
|
||||
|
||||
interface IMovieScrapeDialogProps {
|
||||
movie: Partial<GQL.MovieUpdateInput>;
|
||||
movieStudio: Studio | null;
|
||||
movieTags: Tag[];
|
||||
interface IGroupScrapeDialogProps {
|
||||
group: Partial<GQL.MovieUpdateInput>;
|
||||
groupStudio: Studio | null;
|
||||
groupTags: Tag[];
|
||||
scraped: GQL.ScrapedMovie;
|
||||
|
||||
onClose: (scrapedMovie?: GQL.ScrapedMovie) => void;
|
||||
}
|
||||
|
||||
export const MovieScrapeDialog: React.FC<IMovieScrapeDialogProps> = ({
|
||||
movie,
|
||||
movieStudio,
|
||||
movieTags,
|
||||
export const GroupScrapeDialog: React.FC<IGroupScrapeDialogProps> = ({
|
||||
group,
|
||||
groupStudio: groupStudio,
|
||||
groupTags: groupTags,
|
||||
scraped,
|
||||
onClose,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const [name, setName] = useState<ScrapeResult<string>>(
|
||||
new ScrapeResult<string>(movie.name, scraped.name)
|
||||
new ScrapeResult<string>(group.name, scraped.name)
|
||||
);
|
||||
const [aliases, setAliases] = useState<ScrapeResult<string>>(
|
||||
new ScrapeResult<string>(movie.aliases, scraped.aliases)
|
||||
new ScrapeResult<string>(group.aliases, scraped.aliases)
|
||||
);
|
||||
const [duration, setDuration] = useState<ScrapeResult<string>>(
|
||||
new ScrapeResult<string>(
|
||||
TextUtils.secondsToTimestamp(movie.duration || 0),
|
||||
TextUtils.secondsToTimestamp(group.duration || 0),
|
||||
// convert seconds to string if it's a number
|
||||
scraped.duration && !isNaN(+scraped.duration)
|
||||
? TextUtils.secondsToTimestamp(parseInt(scraped.duration, 10))
|
||||
|
@ -54,20 +54,20 @@ export const MovieScrapeDialog: React.FC<IMovieScrapeDialogProps> = ({
|
|||
)
|
||||
);
|
||||
const [date, setDate] = useState<ScrapeResult<string>>(
|
||||
new ScrapeResult<string>(movie.date, scraped.date)
|
||||
new ScrapeResult<string>(group.date, scraped.date)
|
||||
);
|
||||
const [director, setDirector] = useState<ScrapeResult<string>>(
|
||||
new ScrapeResult<string>(movie.director, scraped.director)
|
||||
new ScrapeResult<string>(group.director, scraped.director)
|
||||
);
|
||||
const [synopsis, setSynopsis] = useState<ScrapeResult<string>>(
|
||||
new ScrapeResult<string>(movie.synopsis, scraped.synopsis)
|
||||
new ScrapeResult<string>(group.synopsis, scraped.synopsis)
|
||||
);
|
||||
const [studio, setStudio] = useState<ObjectScrapeResult<GQL.ScrapedStudio>>(
|
||||
new ObjectScrapeResult<GQL.ScrapedStudio>(
|
||||
movieStudio
|
||||
groupStudio
|
||||
? {
|
||||
stored_id: movieStudio.id,
|
||||
name: movieStudio.name,
|
||||
stored_id: groupStudio.id,
|
||||
name: groupStudio.name,
|
||||
}
|
||||
: undefined,
|
||||
scraped.studio?.stored_id ? scraped.studio : undefined
|
||||
|
@ -75,17 +75,17 @@ export const MovieScrapeDialog: React.FC<IMovieScrapeDialogProps> = ({
|
|||
);
|
||||
const [urls, setURLs] = useState<ScrapeResult<string[]>>(
|
||||
new ScrapeResult<string[]>(
|
||||
movie.urls,
|
||||
group.urls,
|
||||
scraped.urls
|
||||
? uniq((movie.urls ?? []).concat(scraped.urls ?? []))
|
||||
? uniq((group.urls ?? []).concat(scraped.urls ?? []))
|
||||
: undefined
|
||||
)
|
||||
);
|
||||
const [frontImage, setFrontImage] = useState<ScrapeResult<string>>(
|
||||
new ScrapeResult<string>(movie.front_image, scraped.front_image)
|
||||
new ScrapeResult<string>(group.front_image, scraped.front_image)
|
||||
);
|
||||
const [backImage, setBackImage] = useState<ScrapeResult<string>>(
|
||||
new ScrapeResult<string>(movie.back_image, scraped.back_image)
|
||||
new ScrapeResult<string>(group.back_image, scraped.back_image)
|
||||
);
|
||||
|
||||
const [newStudio, setNewStudio] = useState<GQL.ScrapedStudio | undefined>(
|
||||
|
@ -99,7 +99,7 @@ export const MovieScrapeDialog: React.FC<IMovieScrapeDialogProps> = ({
|
|||
});
|
||||
|
||||
const { tags, newTags, scrapedTagsRow } = useScrapedTags(
|
||||
movieTags,
|
||||
groupTags,
|
||||
scraped.tags
|
||||
);
|
||||
|
||||
|
@ -194,13 +194,13 @@ export const MovieScrapeDialog: React.FC<IMovieScrapeDialogProps> = ({
|
|||
{scrapedTagsRow}
|
||||
<ScrapedImageRow
|
||||
title="Front Image"
|
||||
className="movie-image"
|
||||
className="group-image"
|
||||
result={frontImage}
|
||||
onChange={(value) => setFrontImage(value)}
|
||||
/>
|
||||
<ScrapedImageRow
|
||||
title="Back Image"
|
||||
className="movie-image"
|
||||
className="group-image"
|
||||
result={backImage}
|
||||
onChange={(value) => setBackImage(value)}
|
||||
/>
|
||||
|
@ -212,7 +212,7 @@ export const MovieScrapeDialog: React.FC<IMovieScrapeDialogProps> = ({
|
|||
<ScrapeDialog
|
||||
title={intl.formatMessage(
|
||||
{ id: "dialogs.scrape_entity_title" },
|
||||
{ entity_type: intl.formatMessage({ id: "movie" }) }
|
||||
{ entity_type: intl.formatMessage({ id: "group" }) }
|
||||
)}
|
||||
renderScrapeRows={renderScrapeRows}
|
||||
onClose={(apply) => {
|
||||
|
|
|
@ -14,11 +14,11 @@ import {
|
|||
import { makeItemList, showWhenSelected } from "../List/ItemList";
|
||||
import { ExportDialog } from "../Shared/ExportDialog";
|
||||
import { DeleteEntityDialog } from "../Shared/DeleteEntityDialog";
|
||||
import { MovieCardGrid } from "./MovieCardGrid";
|
||||
import { EditMoviesDialog } from "./EditMoviesDialog";
|
||||
import { GroupCardGrid } from "./MovieCardGrid";
|
||||
import { EditGroupsDialog } from "./EditMoviesDialog";
|
||||
import { View } from "../List/views";
|
||||
|
||||
const MovieItemList = makeItemList({
|
||||
const GroupItemList = makeItemList({
|
||||
filterMode: GQL.FilterMode.Movies,
|
||||
useResult: useFindMovies,
|
||||
getItems(result: GQL.FindMoviesQueryResult) {
|
||||
|
@ -29,13 +29,13 @@ const MovieItemList = makeItemList({
|
|||
},
|
||||
});
|
||||
|
||||
interface IMovieList {
|
||||
interface IGroupList {
|
||||
filterHook?: (filter: ListFilterModel) => ListFilterModel;
|
||||
view?: View;
|
||||
alterQuery?: boolean;
|
||||
}
|
||||
|
||||
export const MovieList: React.FC<IMovieList> = ({
|
||||
export const GroupList: React.FC<IGroupList> = ({
|
||||
filterHook,
|
||||
alterQuery,
|
||||
view,
|
||||
|
@ -90,7 +90,7 @@ export const MovieList: React.FC<IMovieList> = ({
|
|||
if (singleResult.data.findMovies.movies.length === 1) {
|
||||
const { id } = singleResult.data.findMovies.movies[0];
|
||||
// navigate to the movie page
|
||||
history.push(`/movies/${id}`);
|
||||
history.push(`/groups/${id}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ export const MovieList: React.FC<IMovieList> = ({
|
|||
selectedIds: Set<string>,
|
||||
onSelectChange: (id: string, selected: boolean, shiftKey: boolean) => void
|
||||
) {
|
||||
function maybeRenderMovieExportDialog() {
|
||||
function maybeRenderGroupExportDialog() {
|
||||
if (isExportDialogOpen) {
|
||||
return (
|
||||
<ExportDialog
|
||||
|
@ -127,13 +127,13 @@ export const MovieList: React.FC<IMovieList> = ({
|
|||
}
|
||||
}
|
||||
|
||||
function renderMovies() {
|
||||
function renderGroups() {
|
||||
if (!result.data?.findMovies) return;
|
||||
|
||||
if (filter.displayMode === DisplayMode.Grid) {
|
||||
return (
|
||||
<MovieCardGrid
|
||||
movies={result.data.findMovies.movies}
|
||||
<GroupCardGrid
|
||||
groups={result.data.findMovies.movies}
|
||||
selectedIds={selectedIds}
|
||||
onSelectChange={onSelectChange}
|
||||
/>
|
||||
|
@ -145,36 +145,36 @@ export const MovieList: React.FC<IMovieList> = ({
|
|||
}
|
||||
return (
|
||||
<>
|
||||
{maybeRenderMovieExportDialog()}
|
||||
{renderMovies()}
|
||||
{maybeRenderGroupExportDialog()}
|
||||
{renderGroups()}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function renderEditDialog(
|
||||
selectedMovies: GQL.MovieDataFragment[],
|
||||
selectedGroups: GQL.MovieDataFragment[],
|
||||
onClose: (applied: boolean) => void
|
||||
) {
|
||||
return <EditMoviesDialog selected={selectedMovies} onClose={onClose} />;
|
||||
return <EditGroupsDialog selected={selectedGroups} onClose={onClose} />;
|
||||
}
|
||||
|
||||
function renderDeleteDialog(
|
||||
selectedMovies: GQL.SlimMovieDataFragment[],
|
||||
selectedGroups: GQL.SlimMovieDataFragment[],
|
||||
onClose: (confirmed: boolean) => void
|
||||
) {
|
||||
return (
|
||||
<DeleteEntityDialog
|
||||
selected={selectedMovies}
|
||||
selected={selectedGroups}
|
||||
onClose={onClose}
|
||||
singularEntity={intl.formatMessage({ id: "movie" })}
|
||||
pluralEntity={intl.formatMessage({ id: "movies" })}
|
||||
singularEntity={intl.formatMessage({ id: "group" })}
|
||||
pluralEntity={intl.formatMessage({ id: "groups" })}
|
||||
destroyMutation={useMoviesDestroy}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<MovieItemList
|
||||
<GroupItemList
|
||||
selectable
|
||||
filterHook={filterHook}
|
||||
view={view}
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from "react";
|
|||
import { Link } from "react-router-dom";
|
||||
import { useFindMovies } from "src/core/StashService";
|
||||
import Slider from "@ant-design/react-slick";
|
||||
import { MovieCard } from "./MovieCard";
|
||||
import { GroupCard } from "./MovieCard";
|
||||
import { ListFilterModel } from "src/models/list-filter/filter";
|
||||
import { getSlickSliderSettings } from "src/core/recommendations";
|
||||
import { RecommendationRow } from "../FrontPage/RecommendationRow";
|
||||
|
@ -14,7 +14,7 @@ interface IProps {
|
|||
header: string;
|
||||
}
|
||||
|
||||
export const MovieRecommendationRow: React.FC<IProps> = (props: IProps) => {
|
||||
export const GroupRecommendationRow: React.FC<IProps> = (props: IProps) => {
|
||||
const result = useFindMovies(props.filter);
|
||||
const cardCount = result.data?.findMovies.count;
|
||||
|
||||
|
@ -24,10 +24,10 @@ export const MovieRecommendationRow: React.FC<IProps> = (props: IProps) => {
|
|||
|
||||
return (
|
||||
<RecommendationRow
|
||||
className="movie-recommendations"
|
||||
className="group-recommendations"
|
||||
header={props.header}
|
||||
link={
|
||||
<Link to={`/movies?${props.filter.makeQueryParameters()}`}>
|
||||
<Link to={`/groups?${props.filter.makeQueryParameters()}`}>
|
||||
<FormattedMessage id="view_all" />
|
||||
</Link>
|
||||
}
|
||||
|
@ -40,10 +40,10 @@ export const MovieRecommendationRow: React.FC<IProps> = (props: IProps) => {
|
|||
>
|
||||
{result.loading
|
||||
? [...Array(props.filter.itemsPerPage)].map((i) => (
|
||||
<div key={`_${i}`} className="movie-skeleton skeleton-card"></div>
|
||||
<div key={`_${i}`} className="group-skeleton skeleton-card"></div>
|
||||
))
|
||||
: result.data?.findMovies.movies.map((m) => (
|
||||
<MovieCard key={m.id} movie={m} />
|
||||
<GroupCard key={m.id} group={m} />
|
||||
))}
|
||||
</Slider>
|
||||
</RecommendationRow>
|
||||
|
|
|
@ -30,13 +30,13 @@ import { sortByRelevance } from "src/utils/query";
|
|||
import { PatchComponent, PatchFunction } from "src/patch";
|
||||
import { TruncatedText } from "../Shared/TruncatedText";
|
||||
|
||||
export type Movie = Pick<
|
||||
export type Group = Pick<
|
||||
GQL.Movie,
|
||||
"id" | "name" | "date" | "front_image_path" | "aliases"
|
||||
> & {
|
||||
studio?: Pick<GQL.Studio, "name"> | null;
|
||||
};
|
||||
type Option = SelectOption<Movie>;
|
||||
type Option = SelectOption<Group>;
|
||||
|
||||
type FindMoviesResult = Awaited<
|
||||
ReturnType<typeof queryFindMoviesForSelect>
|
||||
|
@ -56,9 +56,9 @@ const movieSelectSort = PatchFunction(
|
|||
sortMoviesByRelevance
|
||||
);
|
||||
|
||||
const _MovieSelect: React.FC<
|
||||
const _GroupSelect: React.FC<
|
||||
IFilterProps &
|
||||
IFilterValueProps<Movie> & {
|
||||
IFilterValueProps<Group> & {
|
||||
hoverPlacement?: Placement;
|
||||
excludeIds?: string[];
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ const _MovieSelect: React.FC<
|
|||
}));
|
||||
}
|
||||
|
||||
const MovieOption: React.FC<OptionProps<Option, boolean>> = (optionProps) => {
|
||||
const GroupOption: React.FC<OptionProps<Option, boolean>> = (optionProps) => {
|
||||
let thisOptionProps = optionProps;
|
||||
|
||||
const { object } = optionProps.data;
|
||||
|
@ -111,24 +111,24 @@ const _MovieSelect: React.FC<
|
|||
thisOptionProps = {
|
||||
...optionProps,
|
||||
children: (
|
||||
<span className="movie-select-option">
|
||||
<span className="movie-select-row">
|
||||
<span className="group-select-option">
|
||||
<span className="group-select-row">
|
||||
{object.front_image_path && (
|
||||
<img
|
||||
className="movie-select-image"
|
||||
className="group-select-image"
|
||||
src={object.front_image_path}
|
||||
loading="lazy"
|
||||
/>
|
||||
)}
|
||||
|
||||
<span className="movie-select-details">
|
||||
<span className="group-select-details">
|
||||
<TruncatedText
|
||||
className="movie-select-title"
|
||||
className="group-select-title"
|
||||
text={
|
||||
<span>
|
||||
{title}
|
||||
{alias && (
|
||||
<span className="movie-select-alias">{` (${alias})`}</span>
|
||||
<span className="group-select-alias">{` (${alias})`}</span>
|
||||
)}
|
||||
</span>
|
||||
}
|
||||
|
@ -136,13 +136,13 @@ const _MovieSelect: React.FC<
|
|||
/>
|
||||
|
||||
{object.studio?.name && (
|
||||
<span className="movie-select-studio">
|
||||
<span className="group-select-studio">
|
||||
{object.studio?.name}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{object.date && (
|
||||
<span className="movie-select-date">{object.date}</span>
|
||||
<span className="group-select-date">{object.date}</span>
|
||||
)}
|
||||
</span>
|
||||
</span>
|
||||
|
@ -153,7 +153,7 @@ const _MovieSelect: React.FC<
|
|||
return <reactSelectComponents.Option {...thisOptionProps} />;
|
||||
};
|
||||
|
||||
const MovieMultiValueLabel: React.FC<
|
||||
const GroupMultiValueLabel: React.FC<
|
||||
MultiValueGenericProps<Option, boolean>
|
||||
> = (optionProps) => {
|
||||
let thisOptionProps = optionProps;
|
||||
|
@ -168,7 +168,7 @@ const _MovieSelect: React.FC<
|
|||
return <reactSelectComponents.MultiValueLabel {...thisOptionProps} />;
|
||||
};
|
||||
|
||||
const MovieValueLabel: React.FC<SingleValueProps<Option, boolean>> = (
|
||||
const GroupValueLabel: React.FC<SingleValueProps<Option, boolean>> = (
|
||||
optionProps
|
||||
) => {
|
||||
let thisOptionProps = optionProps;
|
||||
|
@ -190,7 +190,7 @@ const _MovieSelect: React.FC<
|
|||
return {
|
||||
value: result.data!.movieCreate!.id,
|
||||
item: result.data!.movieCreate!,
|
||||
message: "Created movie",
|
||||
message: "Created group",
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -201,7 +201,7 @@ const _MovieSelect: React.FC<
|
|||
};
|
||||
};
|
||||
|
||||
const isValidNewOption = (inputValue: string, options: Movie[]) => {
|
||||
const isValidNewOption = (inputValue: string, options: Group[]) => {
|
||||
if (!inputValue) {
|
||||
return false;
|
||||
}
|
||||
|
@ -221,12 +221,12 @@ const _MovieSelect: React.FC<
|
|||
};
|
||||
|
||||
return (
|
||||
<FilterSelectComponent<Movie, boolean>
|
||||
<FilterSelectComponent<Group, boolean>
|
||||
{...props}
|
||||
className={cx(
|
||||
"movie-select",
|
||||
"group-select",
|
||||
{
|
||||
"movie-select-active": props.active,
|
||||
"group-select-active": props.active,
|
||||
},
|
||||
props.className
|
||||
)}
|
||||
|
@ -234,9 +234,9 @@ const _MovieSelect: React.FC<
|
|||
getNamedObject={getNamedObject}
|
||||
isValidNewOption={isValidNewOption}
|
||||
components={{
|
||||
Option: MovieOption,
|
||||
MultiValueLabel: MovieMultiValueLabel,
|
||||
SingleValue: MovieValueLabel,
|
||||
Option: GroupOption,
|
||||
MultiValueLabel: GroupMultiValueLabel,
|
||||
SingleValue: GroupValueLabel,
|
||||
}}
|
||||
isMulti={props.isMulti ?? false}
|
||||
creatable={props.creatable ?? defaultCreatable}
|
||||
|
@ -247,7 +247,7 @@ const _MovieSelect: React.FC<
|
|||
{ id: "actions.select_entity" },
|
||||
{
|
||||
entityType: intl.formatMessage({
|
||||
id: props.isMulti ? "movies" : "movie",
|
||||
id: props.isMulti ? "groups" : "group",
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
@ -257,22 +257,22 @@ const _MovieSelect: React.FC<
|
|||
);
|
||||
};
|
||||
|
||||
export const MovieSelect = PatchComponent("MovieSelect", _MovieSelect);
|
||||
export const GroupSelect = PatchComponent("GroupSelect", _GroupSelect);
|
||||
|
||||
const _MovieIDSelect: React.FC<IFilterProps & IFilterIDProps<Movie>> = (
|
||||
const _GroupIDSelect: React.FC<IFilterProps & IFilterIDProps<Group>> = (
|
||||
props
|
||||
) => {
|
||||
const { ids, onSelect: onSelectValues } = props;
|
||||
|
||||
const [values, setValues] = useState<Movie[]>([]);
|
||||
const [values, setValues] = useState<Group[]>([]);
|
||||
const idsChanged = useCompare(ids);
|
||||
|
||||
function onSelect(items: Movie[]) {
|
||||
function onSelect(items: Group[]) {
|
||||
setValues(items);
|
||||
onSelectValues?.(items);
|
||||
}
|
||||
|
||||
async function loadObjectsByID(idsToLoad: string[]): Promise<Movie[]> {
|
||||
async function loadObjectsByID(idsToLoad: string[]): Promise<Group[]> {
|
||||
const query = await queryFindMoviesByIDForSelect(idsToLoad);
|
||||
const { movies: loadedMovies } = query.data.findMovies;
|
||||
|
||||
|
@ -303,7 +303,7 @@ const _MovieIDSelect: React.FC<IFilterProps & IFilterIDProps<Movie>> = (
|
|||
load();
|
||||
}, [ids, idsChanged, values]);
|
||||
|
||||
return <MovieSelect {...props} values={values} onSelect={onSelect} />;
|
||||
return <GroupSelect {...props} values={values} onSelect={onSelect} />;
|
||||
};
|
||||
|
||||
export const MovieIDSelect = PatchComponent("MovieIDSelect", _MovieIDSelect);
|
||||
export const GroupIDSelect = PatchComponent("GroupIDSelect", _GroupIDSelect);
|
||||
|
|
|
@ -2,30 +2,30 @@ import React from "react";
|
|||
import { Route, Switch } from "react-router-dom";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useTitleProps } from "src/hooks/title";
|
||||
import Movie from "./MovieDetails/Movie";
|
||||
import MovieCreate from "./MovieDetails/MovieCreate";
|
||||
import { MovieList } from "./MovieList";
|
||||
import Group from "./MovieDetails/Movie";
|
||||
import GroupCreate from "./MovieDetails/MovieCreate";
|
||||
import { GroupList } from "./MovieList";
|
||||
import { useScrollToTopOnMount } from "src/hooks/scrollToTop";
|
||||
import { View } from "../List/views";
|
||||
|
||||
const Movies: React.FC = () => {
|
||||
const Groups: React.FC = () => {
|
||||
useScrollToTopOnMount();
|
||||
|
||||
return <MovieList view={View.Movies} />;
|
||||
return <GroupList view={View.Groups} />;
|
||||
};
|
||||
|
||||
const MovieRoutes: React.FC = () => {
|
||||
const titleProps = useTitleProps({ id: "movies" });
|
||||
const GroupRoutes: React.FC = () => {
|
||||
const titleProps = useTitleProps({ id: "groups" });
|
||||
return (
|
||||
<>
|
||||
<Helmet {...titleProps} />
|
||||
<Switch>
|
||||
<Route exact path="/movies" component={Movies} />
|
||||
<Route exact path="/movies/new" component={MovieCreate} />
|
||||
<Route path="/movies/:id/:tab?" component={Movie} />
|
||||
<Route exact path="/groups" component={Groups} />
|
||||
<Route exact path="/groups/new" component={GroupCreate} />
|
||||
<Route path="/groups/:id/:tab?" component={Group} />
|
||||
</Switch>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MovieRoutes;
|
||||
export default GroupRoutes;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.movie-card {
|
||||
.group-card {
|
||||
width: 240px;
|
||||
|
||||
@media (max-width: 576px) {
|
||||
|
@ -14,7 +14,7 @@
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.movie-scene-number {
|
||||
.group-scene-number {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
@ -23,14 +23,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
.movie-images {
|
||||
.group-images {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
max-width: 100%;
|
||||
|
||||
.movie-image-container {
|
||||
.group-image-container {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
|
@ -40,17 +40,17 @@
|
|||
}
|
||||
}
|
||||
|
||||
#movie-page .rating-number .text-input {
|
||||
#group-page .rating-number .text-input {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.movie-select-option {
|
||||
.movie-select-row {
|
||||
.group-select-option {
|
||||
.group-select-row {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
||||
.movie-select-image {
|
||||
.group-select-image {
|
||||
background-color: $body-bg;
|
||||
margin-right: 0.4em;
|
||||
max-height: 50px;
|
||||
|
@ -59,26 +59,26 @@
|
|||
object-position: center;
|
||||
}
|
||||
|
||||
.movie-select-details {
|
||||
.group-select-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
max-height: 4.1rem;
|
||||
overflow: hidden;
|
||||
|
||||
.movie-select-title {
|
||||
.group-select-title {
|
||||
flex-shrink: 0;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
|
||||
.movie-select-alias {
|
||||
.group-select-alias {
|
||||
font-size: 0.8rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.movie-select-date,
|
||||
.movie-select-studio {
|
||||
.group-select-date,
|
||||
.group-select-studio {
|
||||
color: $text-muted;
|
||||
flex-shrink: 0;
|
||||
font-size: 0.9rem;
|
||||
|
|
|
@ -178,15 +178,15 @@ export const PerformerCard: React.FC<IPerformerCardProps> = ({
|
|||
);
|
||||
}
|
||||
|
||||
function maybeRenderMoviesPopoverButton() {
|
||||
function maybeRenderGroupsPopoverButton() {
|
||||
if (!performer.movie_count) return;
|
||||
|
||||
return (
|
||||
<PopoverCountButton
|
||||
className="movie-count"
|
||||
type="movie"
|
||||
className="group-count"
|
||||
type="group"
|
||||
count={performer.movie_count}
|
||||
url={NavUtils.makePerformerMoviesUrl(
|
||||
url={NavUtils.makePerformerGroupsUrl(
|
||||
performer,
|
||||
extraCriteria?.performer,
|
||||
extraCriteria?.movies
|
||||
|
@ -209,7 +209,7 @@ export const PerformerCard: React.FC<IPerformerCardProps> = ({
|
|||
<hr />
|
||||
<ButtonGroup className="card-popovers">
|
||||
{maybeRenderScenesPopoverButton()}
|
||||
{maybeRenderMoviesPopoverButton()}
|
||||
{maybeRenderGroupsPopoverButton()}
|
||||
{maybeRenderImagesPopoverButton()}
|
||||
{maybeRenderGalleriesPopoverButton()}
|
||||
{maybeRenderTagPopoverButton()}
|
||||
|
|
|
@ -27,7 +27,7 @@ import {
|
|||
} from "./PerformerDetailsPanel";
|
||||
import { PerformerScenesPanel } from "./PerformerScenesPanel";
|
||||
import { PerformerGalleriesPanel } from "./PerformerGalleriesPanel";
|
||||
import { PerformerMoviesPanel } from "./PerformerMoviesPanel";
|
||||
import { PerformerGroupsPanel } from "./PerformerMoviesPanel";
|
||||
import { PerformerImagesPanel } from "./PerformerImagesPanel";
|
||||
import { PerformerAppearsWithPanel } from "./performerAppearsWithPanel";
|
||||
import { PerformerEditPanel } from "./PerformerEditPanel";
|
||||
|
@ -60,7 +60,7 @@ const validTabs = [
|
|||
"scenes",
|
||||
"galleries",
|
||||
"images",
|
||||
"movies",
|
||||
"groups",
|
||||
"appearswith",
|
||||
] as const;
|
||||
type TabKey = (typeof validTabs)[number];
|
||||
|
@ -146,7 +146,7 @@ const PerformerPage: React.FC<IProps> = ({ performer, tabKey }) => {
|
|||
} else if (performer.image_count != 0) {
|
||||
ret = "images";
|
||||
} else if (performer.movie_count != 0) {
|
||||
ret = "movies";
|
||||
ret = "groups";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,7 +191,7 @@ const PerformerPage: React.FC<IProps> = ({ performer, tabKey }) => {
|
|||
Mousetrap.bind("e", () => toggleEditing());
|
||||
Mousetrap.bind("c", () => setTabKey("scenes"));
|
||||
Mousetrap.bind("g", () => setTabKey("galleries"));
|
||||
Mousetrap.bind("m", () => setTabKey("movies"));
|
||||
Mousetrap.bind("m", () => setTabKey("groups"));
|
||||
Mousetrap.bind("f", () => setFavorite(!performer.favorite));
|
||||
Mousetrap.bind(",", () => setCollapsed(!collapsed));
|
||||
|
||||
|
@ -319,10 +319,10 @@ const PerformerPage: React.FC<IProps> = ({ performer, tabKey }) => {
|
|||
/>
|
||||
</Tab>
|
||||
<Tab
|
||||
eventKey="movies"
|
||||
eventKey="groups"
|
||||
title={
|
||||
<>
|
||||
{intl.formatMessage({ id: "movies" })}
|
||||
{intl.formatMessage({ id: "groups" })}
|
||||
<Counter
|
||||
abbreviateCounter={abbreviateCounter}
|
||||
count={performer.movie_count}
|
||||
|
@ -331,8 +331,8 @@ const PerformerPage: React.FC<IProps> = ({ performer, tabKey }) => {
|
|||
</>
|
||||
}
|
||||
>
|
||||
<PerformerMoviesPanel
|
||||
active={tabKey === "movies"}
|
||||
<PerformerGroupsPanel
|
||||
active={tabKey === "groups"}
|
||||
performer={performer}
|
||||
/>
|
||||
</Tab>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { MovieList } from "src/components/Movies/MovieList";
|
||||
import { GroupList } from "src/components/Movies/MovieList";
|
||||
import { usePerformerFilterHook } from "src/core/performers";
|
||||
import { View } from "src/components/List/views";
|
||||
|
||||
|
@ -9,16 +9,16 @@ interface IPerformerDetailsProps {
|
|||
performer: GQL.PerformerDataFragment;
|
||||
}
|
||||
|
||||
export const PerformerMoviesPanel: React.FC<IPerformerDetailsProps> = ({
|
||||
export const PerformerGroupsPanel: React.FC<IPerformerDetailsProps> = ({
|
||||
active,
|
||||
performer,
|
||||
}) => {
|
||||
const filterHook = usePerformerFilterHook(performer);
|
||||
return (
|
||||
<MovieList
|
||||
<GroupList
|
||||
filterHook={filterHook}
|
||||
alterQuery={active}
|
||||
view={View.PerformerMovies}
|
||||
view={View.PerformerGroups}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -21,7 +21,7 @@ import { HoverPopover } from "../Shared/HoverPopover";
|
|||
import { Icon } from "../Shared/Icon";
|
||||
import {
|
||||
GalleryLink,
|
||||
MovieLink,
|
||||
GroupLink,
|
||||
SceneMarkerLink,
|
||||
TagLink,
|
||||
} from "../Shared/TagLink";
|
||||
|
@ -386,14 +386,14 @@ export const SceneDuplicateChecker: React.FC = () => {
|
|||
return <PerformerPopoverButton performers={scene.performers} />;
|
||||
}
|
||||
|
||||
function maybeRenderMoviePopoverButton(scene: GQL.SlimSceneDataFragment) {
|
||||
function maybeRenderGroupPopoverButton(scene: GQL.SlimSceneDataFragment) {
|
||||
if (scene.movies.length <= 0) return;
|
||||
|
||||
const popoverContent = scene.movies.map((sceneMovie) => (
|
||||
<div className="movie-tag-container row" key="movie">
|
||||
<div className="group-tag-container row" key={sceneMovie.movie.id}>
|
||||
<Link
|
||||
to={`/movies/${sceneMovie.movie.id}`}
|
||||
className="movie-tag col m-auto zoom-2"
|
||||
to={`/groups/${sceneMovie.movie.id}`}
|
||||
className="group-tag col m-auto zoom-2"
|
||||
>
|
||||
<img
|
||||
className="image-thumbnail"
|
||||
|
@ -401,9 +401,9 @@ export const SceneDuplicateChecker: React.FC = () => {
|
|||
src={sceneMovie.movie.front_image_path ?? ""}
|
||||
/>
|
||||
</Link>
|
||||
<MovieLink
|
||||
<GroupLink
|
||||
key={sceneMovie.movie.id}
|
||||
movie={sceneMovie.movie}
|
||||
group={sceneMovie.movie}
|
||||
className="d-block"
|
||||
/>
|
||||
</div>
|
||||
|
@ -523,7 +523,7 @@ export const SceneDuplicateChecker: React.FC = () => {
|
|||
<ButtonGroup className="flex-wrap">
|
||||
{maybeRenderTagPopoverButton(scene)}
|
||||
{maybeRenderPerformerPopoverButton(scene)}
|
||||
{maybeRenderMoviePopoverButton(scene)}
|
||||
{maybeRenderGroupPopoverButton(scene)}
|
||||
{maybeRenderSceneMarkerPopoverButton(scene)}
|
||||
{maybeRenderOCounter(scene)}
|
||||
{maybeRenderGallery(scene)}
|
||||
|
|
|
@ -13,7 +13,7 @@ import { RatingSystem } from "../Shared/Rating/RatingSystem";
|
|||
import {
|
||||
getAggregateInputIDs,
|
||||
getAggregateInputValue,
|
||||
getAggregateMovieIds,
|
||||
getAggregateGroupIds,
|
||||
getAggregatePerformerIds,
|
||||
getAggregateRating,
|
||||
getAggregateStudioId,
|
||||
|
@ -42,11 +42,11 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
|
|||
);
|
||||
const [tagIds, setTagIds] = useState<string[]>();
|
||||
const [existingTagIds, setExistingTagIds] = useState<string[]>();
|
||||
const [movieMode, setMovieMode] = React.useState<GQL.BulkUpdateIdMode>(
|
||||
const [groupMode, setGroupMode] = React.useState<GQL.BulkUpdateIdMode>(
|
||||
GQL.BulkUpdateIdMode.Add
|
||||
);
|
||||
const [movieIds, setMovieIds] = useState<string[]>();
|
||||
const [existingMovieIds, setExistingMovieIds] = useState<string[]>();
|
||||
const [groupIds, setGroupIds] = useState<string[]>();
|
||||
const [existingGroupIds, setExistingGroupIds] = useState<string[]>();
|
||||
const [organized, setOrganized] = useState<boolean | undefined>();
|
||||
|
||||
const [updateScenes] = useBulkSceneUpdate(getSceneInput());
|
||||
|
@ -62,7 +62,7 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
|
|||
const aggregateStudioId = getAggregateStudioId(props.selected);
|
||||
const aggregatePerformerIds = getAggregatePerformerIds(props.selected);
|
||||
const aggregateTagIds = getAggregateTagIds(props.selected);
|
||||
const aggregateMovieIds = getAggregateMovieIds(props.selected);
|
||||
const aggregateGroupIds = getAggregateGroupIds(props.selected);
|
||||
|
||||
const sceneInput: GQL.BulkSceneUpdateInput = {
|
||||
ids: props.selected.map((scene) => {
|
||||
|
@ -80,9 +80,9 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
|
|||
);
|
||||
sceneInput.tag_ids = getAggregateInputIDs(tagMode, tagIds, aggregateTagIds);
|
||||
sceneInput.movie_ids = getAggregateInputIDs(
|
||||
movieMode,
|
||||
movieIds,
|
||||
aggregateMovieIds
|
||||
groupMode,
|
||||
groupIds,
|
||||
aggregateGroupIds
|
||||
);
|
||||
|
||||
if (organized !== undefined) {
|
||||
|
@ -115,7 +115,7 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
|
|||
let updateStudioID: string | undefined;
|
||||
let updatePerformerIds: string[] = [];
|
||||
let updateTagIds: string[] = [];
|
||||
let updateMovieIds: string[] = [];
|
||||
let updateGroupIds: string[] = [];
|
||||
let updateOrganized: boolean | undefined;
|
||||
let first = true;
|
||||
|
||||
|
@ -126,14 +126,14 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
|
|||
.map((p) => p.id)
|
||||
.sort();
|
||||
const sceneTagIDs = (scene.tags ?? []).map((p) => p.id).sort();
|
||||
const sceneMovieIDs = (scene.movies ?? []).map((m) => m.movie.id).sort();
|
||||
const sceneGroupIDs = (scene.movies ?? []).map((m) => m.movie.id).sort();
|
||||
|
||||
if (first) {
|
||||
updateRating = sceneRating ?? undefined;
|
||||
updateStudioID = sceneStudioID;
|
||||
updatePerformerIds = scenePerformerIDs;
|
||||
updateTagIds = sceneTagIDs;
|
||||
updateMovieIds = sceneMovieIDs;
|
||||
updateGroupIds = sceneGroupIDs;
|
||||
first = false;
|
||||
updateOrganized = scene.organized;
|
||||
} else {
|
||||
|
@ -149,8 +149,8 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
|
|||
if (!isEqual(sceneTagIDs, updateTagIds)) {
|
||||
updateTagIds = [];
|
||||
}
|
||||
if (!isEqual(sceneMovieIDs, updateMovieIds)) {
|
||||
updateMovieIds = [];
|
||||
if (!isEqual(sceneGroupIDs, updateGroupIds)) {
|
||||
updateGroupIds = [];
|
||||
}
|
||||
if (scene.organized !== updateOrganized) {
|
||||
updateOrganized = undefined;
|
||||
|
@ -162,7 +162,7 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
|
|||
setStudioId(updateStudioID);
|
||||
setExistingPerformerIds(updatePerformerIds);
|
||||
setExistingTagIds(updateTagIds);
|
||||
setExistingMovieIds(updateMovieIds);
|
||||
setExistingGroupIds(updateGroupIds);
|
||||
setOrganized(updateOrganized);
|
||||
}, [props.selected]);
|
||||
|
||||
|
@ -173,7 +173,7 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
|
|||
}, [organized, checkboxRef]);
|
||||
|
||||
function renderMultiSelect(
|
||||
type: "performers" | "tags" | "movies",
|
||||
type: "performers" | "tags" | "groups",
|
||||
ids: string[] | undefined
|
||||
) {
|
||||
let mode = GQL.BulkUpdateIdMode.Add;
|
||||
|
@ -187,9 +187,9 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
|
|||
mode = tagMode;
|
||||
existingIds = existingTagIds;
|
||||
break;
|
||||
case "movies":
|
||||
mode = movieMode;
|
||||
existingIds = existingMovieIds;
|
||||
case "groups":
|
||||
mode = groupMode;
|
||||
existingIds = existingGroupIds;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -205,8 +205,8 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
|
|||
case "tags":
|
||||
setTagIds(itemIDs);
|
||||
break;
|
||||
case "movies":
|
||||
setMovieIds(itemIDs);
|
||||
case "groups":
|
||||
setGroupIds(itemIDs);
|
||||
break;
|
||||
}
|
||||
}}
|
||||
|
@ -218,8 +218,8 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
|
|||
case "tags":
|
||||
setTagMode(newMode);
|
||||
break;
|
||||
case "movies":
|
||||
setMovieMode(newMode);
|
||||
case "groups":
|
||||
setGroupMode(newMode);
|
||||
break;
|
||||
}
|
||||
}}
|
||||
|
@ -306,11 +306,11 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
|
|||
{renderMultiSelect("tags", tagIds)}
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="movies">
|
||||
<Form.Group controlId="groups">
|
||||
<Form.Label>
|
||||
<FormattedMessage id="movies" />
|
||||
<FormattedMessage id="groups" />
|
||||
</Form.Label>
|
||||
{renderMultiSelect("movies", movieIds)}
|
||||
{renderMultiSelect("groups", groupIds)}
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group controlId="organized">
|
||||
|
|
|
@ -7,7 +7,7 @@ import { Icon } from "../Shared/Icon";
|
|||
import {
|
||||
GalleryLink,
|
||||
TagLink,
|
||||
MovieLink,
|
||||
GroupLink,
|
||||
SceneMarkerLink,
|
||||
} from "../Shared/TagLink";
|
||||
import { HoverPopover } from "../Shared/HoverPopover";
|
||||
|
@ -143,24 +143,24 @@ const SceneCardPopovers = PatchComponent(
|
|||
return <PerformerPopoverButton performers={props.scene.performers} />;
|
||||
}
|
||||
|
||||
function maybeRenderMoviePopoverButton() {
|
||||
function maybeRenderGroupPopoverButton() {
|
||||
if (props.scene.movies.length <= 0) return;
|
||||
|
||||
const popoverContent = props.scene.movies.map((sceneMovie) => (
|
||||
<div className="movie-tag-container row" key="movie">
|
||||
const popoverContent = props.scene.movies.map((sceneGroup) => (
|
||||
<div className="group-tag-container row" key={sceneGroup.movie.id}>
|
||||
<Link
|
||||
to={`/movies/${sceneMovie.movie.id}`}
|
||||
className="movie-tag col m-auto zoom-2"
|
||||
to={`/groups/${sceneGroup.movie.id}`}
|
||||
className="group-tag col m-auto zoom-2"
|
||||
>
|
||||
<img
|
||||
className="image-thumbnail"
|
||||
alt={sceneMovie.movie.name ?? ""}
|
||||
src={sceneMovie.movie.front_image_path ?? ""}
|
||||
alt={sceneGroup.movie.name ?? ""}
|
||||
src={sceneGroup.movie.front_image_path ?? ""}
|
||||
/>
|
||||
</Link>
|
||||
<MovieLink
|
||||
key={sceneMovie.movie.id}
|
||||
movie={sceneMovie.movie}
|
||||
<GroupLink
|
||||
key={sceneGroup.movie.id}
|
||||
group={sceneGroup.movie}
|
||||
className="d-block"
|
||||
/>
|
||||
</div>
|
||||
|
@ -170,7 +170,7 @@ const SceneCardPopovers = PatchComponent(
|
|||
<HoverPopover
|
||||
placement="bottom"
|
||||
content={popoverContent}
|
||||
className="movie-count tag-tooltip"
|
||||
className="group-count tag-tooltip"
|
||||
>
|
||||
<Button className="minimal">
|
||||
<Icon icon={faFilm} />
|
||||
|
@ -291,7 +291,7 @@ const SceneCardPopovers = PatchComponent(
|
|||
<ButtonGroup className="card-popovers">
|
||||
{maybeRenderTagPopoverButton()}
|
||||
{maybeRenderPerformerPopoverButton()}
|
||||
{maybeRenderMoviePopoverButton()}
|
||||
{maybeRenderGroupPopoverButton()}
|
||||
{maybeRenderSceneMarkerPopoverButton()}
|
||||
{maybeRenderOCounter()}
|
||||
{maybeRenderGallery()}
|
||||
|
|
|
@ -70,7 +70,7 @@ 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 SceneGroupPanel = lazyComponent(() => import("./SceneMoviePanel"));
|
||||
const SceneGalleriesPanel = lazyComponent(
|
||||
() => import("./SceneGalleriesPanel")
|
||||
);
|
||||
|
@ -443,9 +443,9 @@ const ScenePage: React.FC<IProps> = ({
|
|||
</Nav.Item>
|
||||
{scene.movies.length > 0 ? (
|
||||
<Nav.Item>
|
||||
<Nav.Link eventKey="scene-movie-panel">
|
||||
<Nav.Link eventKey="scene-group-panel">
|
||||
<FormattedMessage
|
||||
id="countables.movies"
|
||||
id="countables.groups"
|
||||
values={{ count: scene.movies.length }}
|
||||
/>
|
||||
</Nav.Link>
|
||||
|
@ -514,8 +514,8 @@ const ScenePage: React.FC<IProps> = ({
|
|||
isVisible={activeTabKey === "scene-markers-panel"}
|
||||
/>
|
||||
</Tab.Pane>
|
||||
<Tab.Pane eventKey="scene-movie-panel">
|
||||
<SceneMoviePanel scene={scene} />
|
||||
<Tab.Pane eventKey="scene-group-panel">
|
||||
<SceneGroupPanel scene={scene} />
|
||||
</Tab.Pane>
|
||||
{scene.galleries.length >= 1 && (
|
||||
<Tab.Pane eventKey="scene-galleries-panel">
|
||||
|
|
|
@ -29,7 +29,7 @@ import { useFormik } from "formik";
|
|||
import { Prompt } from "react-router-dom";
|
||||
import { ConfigurationContext } from "src/hooks/Config";
|
||||
import { stashboxDisplayName } from "src/utils/stashbox";
|
||||
import { IMovieEntry, SceneMovieTable } from "./SceneMovieTable";
|
||||
import { IGroupEntry, SceneGroupTable } from "./SceneMovieTable";
|
||||
import { faSearch, faSyncAlt } from "@fortawesome/free-solid-svg-icons";
|
||||
import { objectTitle } from "src/core/files";
|
||||
import { galleryTitle } from "src/core/galleries";
|
||||
|
@ -47,7 +47,7 @@ import {
|
|||
import { formikUtils } from "src/utils/form";
|
||||
import { Studio, StudioSelect } from "src/components/Studios/StudioSelect";
|
||||
import { Gallery, GallerySelect } from "src/components/Galleries/GallerySelect";
|
||||
import { Movie } from "src/components/Movies/MovieSelect";
|
||||
import { Group } from "src/components/Movies/MovieSelect";
|
||||
import { useTagsEdit } from "src/hooks/tagsEdit";
|
||||
|
||||
const SceneScrapeDialog = lazyComponent(() => import("./SceneScrapeDialog"));
|
||||
|
@ -75,7 +75,7 @@ export const SceneEditPanel: React.FC<IProps> = ({
|
|||
|
||||
const [galleries, setGalleries] = useState<Gallery[]>([]);
|
||||
const [performers, setPerformers] = useState<Performer[]>([]);
|
||||
const [movies, setMovies] = useState<Movie[]>([]);
|
||||
const [groups, setGroups] = useState<Group[]>([]);
|
||||
const [studio, setStudio] = useState<Studio | null>(null);
|
||||
|
||||
const Scrapers = useListSceneScrapers();
|
||||
|
@ -104,7 +104,7 @@ export const SceneEditPanel: React.FC<IProps> = ({
|
|||
}, [scene.performers]);
|
||||
|
||||
useEffect(() => {
|
||||
setMovies(scene.movies?.map((m) => m.movie) ?? []);
|
||||
setGroups(scene.movies?.map((m) => m.movie) ?? []);
|
||||
}, [scene.movies]);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -191,12 +191,12 @@ export const SceneEditPanel: React.FC<IProps> = ({
|
|||
return formik.values.movies
|
||||
.map((m) => {
|
||||
return {
|
||||
movie: movies.find((mm) => mm.id === m.movie_id),
|
||||
movie: groups.find((mm) => mm.id === m.movie_id),
|
||||
scene_index: m.scene_index,
|
||||
};
|
||||
})
|
||||
.filter((m) => m.movie !== undefined) as IMovieEntry[];
|
||||
}, [formik.values.movies, movies]);
|
||||
.filter((m) => m.movie !== undefined) as IGroupEntry[];
|
||||
}, [formik.values.movies, groups]);
|
||||
|
||||
function onSetGalleries(items: Gallery[]) {
|
||||
setGalleries(items);
|
||||
|
@ -253,8 +253,8 @@ export const SceneEditPanel: React.FC<IProps> = ({
|
|||
setQueryableScrapers(newQueryableScrapers);
|
||||
}, [Scrapers, stashConfig]);
|
||||
|
||||
function onSetMovies(items: Movie[]) {
|
||||
setMovies(items);
|
||||
function onSetGroups(items: Group[]) {
|
||||
setGroups(items);
|
||||
|
||||
const existingMovies = formik.values.movies;
|
||||
|
||||
|
@ -386,7 +386,7 @@ export const SceneEditPanel: React.FC<IProps> = ({
|
|||
sceneStudio={studio}
|
||||
sceneTags={tags}
|
||||
scenePerformers={performers}
|
||||
sceneMovies={movies}
|
||||
sceneGroups={groups}
|
||||
scraped={scrapedScene}
|
||||
endpoint={endpoint}
|
||||
onClose={(s) => onScrapeDialogClosed(s)}
|
||||
|
@ -574,7 +574,7 @@ export const SceneEditPanel: React.FC<IProps> = ({
|
|||
});
|
||||
|
||||
if (idMovis.length > 0) {
|
||||
onSetMovies(
|
||||
onSetGroups(
|
||||
idMovis.map((p) => {
|
||||
return {
|
||||
id: p.stored_id!,
|
||||
|
@ -725,8 +725,8 @@ export const SceneEditPanel: React.FC<IProps> = ({
|
|||
return renderField("performer_ids", title, control, fullWidthProps);
|
||||
}
|
||||
|
||||
function onSetMovieEntries(input: IMovieEntry[]) {
|
||||
setMovies(input.map((m) => m.movie));
|
||||
function onSetMovieEntries(input: IGroupEntry[]) {
|
||||
setGroups(input.map((m) => m.movie));
|
||||
|
||||
const newMovies = input.map((m) => ({
|
||||
movie_id: m.movie.id,
|
||||
|
@ -737,9 +737,9 @@ export const SceneEditPanel: React.FC<IProps> = ({
|
|||
}
|
||||
|
||||
function renderMoviesField() {
|
||||
const title = intl.formatMessage({ id: "movies" });
|
||||
const title = intl.formatMessage({ id: "groups" });
|
||||
const control = (
|
||||
<SceneMovieTable value={movieEntries} onUpdate={onSetMovieEntries} />
|
||||
<SceneGroupTable value={movieEntries} onUpdate={onSetMovieEntries} />
|
||||
);
|
||||
|
||||
return renderField("movies", title, control, fullWidthProps);
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
import React from "react";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { MovieCard } from "src/components/Movies/MovieCard";
|
||||
import { GroupCard } from "src/components/Movies/MovieCard";
|
||||
|
||||
interface ISceneMoviePanelProps {
|
||||
interface ISceneGroupPanelProps {
|
||||
scene: GQL.SceneDataFragment;
|
||||
}
|
||||
|
||||
export const SceneMoviePanel: React.FC<ISceneMoviePanelProps> = (
|
||||
props: ISceneMoviePanelProps
|
||||
export const SceneGroupPanel: React.FC<ISceneGroupPanelProps> = (
|
||||
props: ISceneGroupPanelProps
|
||||
) => {
|
||||
const cards = props.scene.movies.map((sceneMovie) => (
|
||||
<MovieCard
|
||||
key={sceneMovie.movie.id}
|
||||
movie={sceneMovie.movie}
|
||||
sceneIndex={sceneMovie.scene_index ?? undefined}
|
||||
const cards = props.scene.movies.map((sceneGroup) => (
|
||||
<GroupCard
|
||||
key={sceneGroup.movie.id}
|
||||
group={sceneGroup.movie}
|
||||
sceneIndex={sceneGroup.scene_index ?? undefined}
|
||||
/>
|
||||
));
|
||||
|
||||
|
@ -24,4 +24,4 @@ export const SceneMoviePanel: React.FC<ISceneMoviePanelProps> = (
|
|||
);
|
||||
};
|
||||
|
||||
export default SceneMoviePanel;
|
||||
export default SceneGroupPanel;
|
||||
|
|
|
@ -2,27 +2,27 @@ import React, { useMemo } from "react";
|
|||
import { useIntl } from "react-intl";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { Form, Row, Col } from "react-bootstrap";
|
||||
import { Movie, MovieSelect } from "src/components/Movies/MovieSelect";
|
||||
import { Group, GroupSelect } from "src/components/Movies/MovieSelect";
|
||||
import cx from "classnames";
|
||||
|
||||
export type MovieSceneIndexMap = Map<string, number | undefined>;
|
||||
|
||||
export interface IMovieEntry {
|
||||
movie: Movie;
|
||||
export interface IGroupEntry {
|
||||
movie: Group;
|
||||
scene_index?: GQL.InputMaybe<number> | undefined;
|
||||
}
|
||||
|
||||
export interface IProps {
|
||||
value: IMovieEntry[];
|
||||
onUpdate: (input: IMovieEntry[]) => void;
|
||||
value: IGroupEntry[];
|
||||
onUpdate: (input: IGroupEntry[]) => void;
|
||||
}
|
||||
|
||||
export const SceneMovieTable: React.FC<IProps> = (props) => {
|
||||
export const SceneGroupTable: React.FC<IProps> = (props) => {
|
||||
const { value, onUpdate } = props;
|
||||
|
||||
const intl = useIntl();
|
||||
|
||||
const movieIDs = useMemo(() => value.map((m) => m.movie.id), [value]);
|
||||
const groupIDs = useMemo(() => value.map((m) => m.movie.id), [value]);
|
||||
|
||||
const updateFieldChanged = (index: number, sceneIndex: number | null) => {
|
||||
const newValues = value.map((existing, i) => {
|
||||
|
@ -38,21 +38,21 @@ export const SceneMovieTable: React.FC<IProps> = (props) => {
|
|||
onUpdate(newValues);
|
||||
};
|
||||
|
||||
function onMovieSet(index: number, movies: Movie[]) {
|
||||
if (!movies.length) {
|
||||
function onGroupSet(index: number, groups: Group[]) {
|
||||
if (!groups.length) {
|
||||
// remove this entry
|
||||
const newValues = value.filter((_, i) => i !== index);
|
||||
onUpdate(newValues);
|
||||
return;
|
||||
}
|
||||
|
||||
const movie = movies[0];
|
||||
const group = groups[0];
|
||||
|
||||
const newValues = value.map((existing, i) => {
|
||||
if (i === index) {
|
||||
return {
|
||||
...existing,
|
||||
movie: movie,
|
||||
movie: group,
|
||||
};
|
||||
}
|
||||
return existing;
|
||||
|
@ -61,17 +61,17 @@ export const SceneMovieTable: React.FC<IProps> = (props) => {
|
|||
onUpdate(newValues);
|
||||
}
|
||||
|
||||
function onNewMovieSet(movies: Movie[]) {
|
||||
if (!movies.length) {
|
||||
function onNewGroupSet(groups: Group[]) {
|
||||
if (!groups.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const movie = movies[0];
|
||||
const group = groups[0];
|
||||
|
||||
const newValues = [
|
||||
...value,
|
||||
{
|
||||
movie: movie,
|
||||
movie: group,
|
||||
scene_index: null,
|
||||
},
|
||||
];
|
||||
|
@ -83,12 +83,12 @@ export const SceneMovieTable: React.FC<IProps> = (props) => {
|
|||
return (
|
||||
<>
|
||||
{value.map((m, i) => (
|
||||
<Row key={m.movie.id} className="movie-row">
|
||||
<Row key={m.movie.id} className="group-row">
|
||||
<Col xs={9}>
|
||||
<MovieSelect
|
||||
onSelect={(items) => onMovieSet(i, items)}
|
||||
<GroupSelect
|
||||
onSelect={(items) => onGroupSet(i, items)}
|
||||
values={[m.movie!]}
|
||||
excludeIds={movieIDs}
|
||||
excludeIds={groupIDs}
|
||||
/>
|
||||
</Col>
|
||||
<Col xs={3}>
|
||||
|
@ -108,12 +108,12 @@ export const SceneMovieTable: React.FC<IProps> = (props) => {
|
|||
</Col>
|
||||
</Row>
|
||||
))}
|
||||
<Row className="movie-row">
|
||||
<Row className="group-row">
|
||||
<Col xs={12}>
|
||||
<MovieSelect
|
||||
onSelect={(items) => onNewMovieSet(items)}
|
||||
<GroupSelect
|
||||
onSelect={(items) => onNewGroupSet(items)}
|
||||
values={[]}
|
||||
excludeIds={movieIDs}
|
||||
excludeIds={groupIDs}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
|
@ -122,11 +122,11 @@ export const SceneMovieTable: React.FC<IProps> = (props) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className={cx("movie-table", { "no-movies": !value.length })}>
|
||||
<Row className="movie-table-header">
|
||||
<div className={cx("group-table", { "no-groups": !value.length })}>
|
||||
<Row className="group-table-header">
|
||||
<Col xs={9}></Col>
|
||||
<Form.Label column xs={3} className="movie-scene-number-header">
|
||||
{intl.formatMessage({ id: "movie_scene_number" })}
|
||||
<Form.Label column xs={3} className="group-scene-number-header">
|
||||
{intl.formatMessage({ id: "group_scene_number" })}
|
||||
</Form.Label>
|
||||
</Row>
|
||||
{renderTableData()}
|
||||
|
|
|
@ -17,18 +17,18 @@ import {
|
|||
ScrapeResult,
|
||||
} from "src/components/Shared/ScrapeDialog/scrapeResult";
|
||||
import {
|
||||
ScrapedMoviesRow,
|
||||
ScrapedGroupsRow,
|
||||
ScrapedPerformersRow,
|
||||
ScrapedStudioRow,
|
||||
} from "src/components/Shared/ScrapeDialog/ScrapedObjectsRow";
|
||||
import {
|
||||
useCreateScrapedMovie,
|
||||
useCreateScrapedGroup,
|
||||
useCreateScrapedPerformer,
|
||||
useCreateScrapedStudio,
|
||||
} from "src/components/Shared/ScrapeDialog/createObjects";
|
||||
import { Tag } from "src/components/Tags/TagSelect";
|
||||
import { Studio } from "src/components/Studios/StudioSelect";
|
||||
import { Movie } from "src/components/Movies/MovieSelect";
|
||||
import { Group } from "src/components/Movies/MovieSelect";
|
||||
import { useScrapedTags } from "src/components/Shared/ScrapeDialog/scrapedTags";
|
||||
|
||||
interface ISceneScrapeDialogProps {
|
||||
|
@ -36,7 +36,7 @@ interface ISceneScrapeDialogProps {
|
|||
sceneStudio: Studio | null;
|
||||
scenePerformers: Performer[];
|
||||
sceneTags: Tag[];
|
||||
sceneMovies: Movie[];
|
||||
sceneGroups: Group[];
|
||||
scraped: GQL.ScrapedScene;
|
||||
endpoint?: string;
|
||||
|
||||
|
@ -48,7 +48,7 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = ({
|
|||
sceneStudio,
|
||||
scenePerformers,
|
||||
sceneTags,
|
||||
sceneMovies,
|
||||
sceneGroups,
|
||||
scraped,
|
||||
onClose,
|
||||
endpoint,
|
||||
|
@ -114,12 +114,12 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = ({
|
|||
scraped.performers?.filter((t) => !t.stored_id) ?? []
|
||||
);
|
||||
|
||||
const [movies, setMovies] = useState<
|
||||
const [groups, setGroups] = useState<
|
||||
ObjectListScrapeResult<GQL.ScrapedMovie>
|
||||
>(
|
||||
new ObjectListScrapeResult<GQL.ScrapedMovie>(
|
||||
sortStoredIdObjects(
|
||||
sceneMovies.map((p) => ({
|
||||
sceneGroups.map((p) => ({
|
||||
stored_id: p.id,
|
||||
name: p.name,
|
||||
}))
|
||||
|
@ -127,7 +127,7 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = ({
|
|||
sortStoredIdObjects(scraped.movies ?? undefined)
|
||||
)
|
||||
);
|
||||
const [newMovies, setNewMovies] = useState<GQL.ScrapedMovie[]>(
|
||||
const [newGroups, setNewGroups] = useState<GQL.ScrapedMovie[]>(
|
||||
scraped.movies?.filter((t) => !t.stored_id) ?? []
|
||||
);
|
||||
|
||||
|
@ -157,11 +157,11 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = ({
|
|||
setNewObjects: setNewPerformers,
|
||||
});
|
||||
|
||||
const createNewMovie = useCreateScrapedMovie({
|
||||
scrapeResult: movies,
|
||||
setScrapeResult: setMovies,
|
||||
newObjects: newMovies,
|
||||
setNewObjects: setNewMovies,
|
||||
const createNewGroup = useCreateScrapedGroup({
|
||||
scrapeResult: groups,
|
||||
setScrapeResult: setGroups,
|
||||
newObjects: newGroups,
|
||||
setNewObjects: setNewGroups,
|
||||
});
|
||||
|
||||
const intl = useIntl();
|
||||
|
@ -176,7 +176,7 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = ({
|
|||
director,
|
||||
studio,
|
||||
performers,
|
||||
movies,
|
||||
groups,
|
||||
tags,
|
||||
details,
|
||||
image,
|
||||
|
@ -184,7 +184,7 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = ({
|
|||
].every((r) => !r.scraped) &&
|
||||
newTags.length === 0 &&
|
||||
newPerformers.length === 0 &&
|
||||
newMovies.length === 0 &&
|
||||
newGroups.length === 0 &&
|
||||
!newStudio
|
||||
) {
|
||||
onClose();
|
||||
|
@ -202,7 +202,7 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = ({
|
|||
director: director.getNewValue(),
|
||||
studio: newStudioValue,
|
||||
performers: performers.getNewValue(),
|
||||
movies: movies.getNewValue(),
|
||||
movies: groups.getNewValue(),
|
||||
tags: tags.getNewValue(),
|
||||
details: details.getNewValue(),
|
||||
image: image.getNewValue(),
|
||||
|
@ -253,12 +253,12 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = ({
|
|||
newObjects={newPerformers}
|
||||
onCreateNew={createNewPerformer}
|
||||
/>
|
||||
<ScrapedMoviesRow
|
||||
title={intl.formatMessage({ id: "movies" })}
|
||||
result={movies}
|
||||
onChange={(value) => setMovies(value)}
|
||||
newObjects={newMovies}
|
||||
onCreateNew={createNewMovie}
|
||||
<ScrapedGroupsRow
|
||||
title={intl.formatMessage({ id: "groups" })}
|
||||
result={groups}
|
||||
onChange={(value) => setGroups(value)}
|
||||
newObjects={newGroups}
|
||||
onCreateNew={createNewGroup}
|
||||
/>
|
||||
{scrapedTagsRow}
|
||||
<ScrapedTextAreaRow
|
||||
|
|
|
@ -124,12 +124,12 @@ export const SceneListTable: React.FC<ISceneListTableProps> = (
|
|||
}
|
||||
};
|
||||
|
||||
const MovieCell = (scene: GQL.SlimSceneDataFragment) => (
|
||||
const GroupCell = (scene: GQL.SlimSceneDataFragment) => (
|
||||
<ul className="comma-list overflowable">
|
||||
{scene.movies.map((sceneMovie) => (
|
||||
<li key={sceneMovie.movie.id}>
|
||||
<Link to={NavUtils.makeMovieScenesUrl(sceneMovie.movie)}>
|
||||
<span className="ellips-data">{sceneMovie.movie.name}</span>
|
||||
{scene.movies.map((sceneGroup) => (
|
||||
<li key={sceneGroup.movie.id}>
|
||||
<Link to={NavUtils.makeGroupScenesUrl(sceneGroup.movie)}>
|
||||
<span className="ellips-data">{sceneGroup.movie.name}</span>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
|
@ -322,10 +322,10 @@ export const SceneListTable: React.FC<ISceneListTableProps> = (
|
|||
render: TagCell,
|
||||
},
|
||||
{
|
||||
value: "movies",
|
||||
label: intl.formatMessage({ id: "movies" }),
|
||||
value: "groups",
|
||||
label: intl.formatMessage({ id: "groups" }),
|
||||
defaultShow: true,
|
||||
render: MovieCell,
|
||||
render: GroupCell,
|
||||
},
|
||||
{
|
||||
value: "galleries",
|
||||
|
|
|
@ -30,7 +30,7 @@ import {
|
|||
hasScrapedValues,
|
||||
} from "../Shared/ScrapeDialog/scrapeResult";
|
||||
import {
|
||||
ScrapedMoviesRow,
|
||||
ScrapedGroupsRow,
|
||||
ScrapedPerformersRow,
|
||||
ScrapedStudioRow,
|
||||
ScrapedTagsRow,
|
||||
|
@ -100,7 +100,7 @@ const SceneMergeDetails: React.FC<ISceneMergeDetailsProps> = ({
|
|||
};
|
||||
}
|
||||
|
||||
function movieToStoredID(o: { movie: { id: string; name: string } }) {
|
||||
function groupToStoredID(o: { movie: { id: string; name: string } }) {
|
||||
return {
|
||||
stored_id: o.movie.id,
|
||||
name: o.movie.name,
|
||||
|
@ -141,11 +141,11 @@ const SceneMergeDetails: React.FC<ISceneMergeDetailsProps> = ({
|
|||
)
|
||||
);
|
||||
|
||||
const [movies, setMovies] = useState<
|
||||
const [groups, setGroups] = useState<
|
||||
ObjectListScrapeResult<GQL.ScrapedMovie>
|
||||
>(
|
||||
new ObjectListScrapeResult<GQL.ScrapedMovie>(
|
||||
sortStoredIdObjects(dest.movies.map(movieToStoredID))
|
||||
sortStoredIdObjects(dest.movies.map(groupToStoredID))
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -252,10 +252,10 @@ const SceneMergeDetails: React.FC<ISceneMergeDetailsProps> = ({
|
|||
)
|
||||
);
|
||||
|
||||
setMovies(
|
||||
setGroups(
|
||||
new ObjectListScrapeResult<GQL.ScrapedMovie>(
|
||||
sortStoredIdObjects(dest.movies.map(movieToStoredID)),
|
||||
uniqIDStoredIDs(all.map((s) => s.movies.map(movieToStoredID)).flat())
|
||||
sortStoredIdObjects(dest.movies.map(groupToStoredID)),
|
||||
uniqIDStoredIDs(all.map((s) => s.movies.map(groupToStoredID)).flat())
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -331,7 +331,7 @@ const SceneMergeDetails: React.FC<ISceneMergeDetailsProps> = ({
|
|||
galleries,
|
||||
studio,
|
||||
performers,
|
||||
movies,
|
||||
groups,
|
||||
tags,
|
||||
details,
|
||||
organized,
|
||||
|
@ -348,7 +348,7 @@ const SceneMergeDetails: React.FC<ISceneMergeDetailsProps> = ({
|
|||
galleries,
|
||||
studio,
|
||||
performers,
|
||||
movies,
|
||||
groups,
|
||||
tags,
|
||||
details,
|
||||
organized,
|
||||
|
@ -508,10 +508,10 @@ const SceneMergeDetails: React.FC<ISceneMergeDetailsProps> = ({
|
|||
result={performers}
|
||||
onChange={(value) => setPerformers(value)}
|
||||
/>
|
||||
<ScrapedMoviesRow
|
||||
title={intl.formatMessage({ id: "movies" })}
|
||||
result={movies}
|
||||
onChange={(value) => setMovies(value)}
|
||||
<ScrapedGroupsRow
|
||||
title={intl.formatMessage({ id: "groups" })}
|
||||
result={groups}
|
||||
onChange={(value) => setGroups(value)}
|
||||
/>
|
||||
<ScrapedTagsRow
|
||||
title={intl.formatMessage({ id: "tags" })}
|
||||
|
@ -585,7 +585,7 @@ const SceneMergeDetails: React.FC<ISceneMergeDetailsProps> = ({
|
|||
gallery_ids: galleries.getNewValue(),
|
||||
studio_id: studio.getNewValue()?.stored_id,
|
||||
performer_ids: performers.getNewValue()?.map((p) => p.stored_id!),
|
||||
movies: movies.getNewValue()?.map((m) => {
|
||||
movies: groups.getNewValue()?.map((m) => {
|
||||
// find the equivalent movie in the original scenes
|
||||
const found = all
|
||||
.map((s) => s.movies)
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
}
|
||||
|
||||
.performer-tag-container,
|
||||
.movie-tag-container {
|
||||
.group-tag-container {
|
||||
display: inline-block;
|
||||
margin: 5px;
|
||||
}
|
||||
|
@ -34,7 +34,7 @@
|
|||
}
|
||||
|
||||
.performer-tag.image,
|
||||
.movie-tag.image {
|
||||
.group-tag.image {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
|
@ -288,19 +288,19 @@ textarea.scene-description {
|
|||
max-width: 100%;
|
||||
}
|
||||
|
||||
.movie-image {
|
||||
.group-image {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.movie-table {
|
||||
.group-table {
|
||||
width: 100%;
|
||||
|
||||
.movie-row {
|
||||
.group-row {
|
||||
align-items: center;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.movie-scene-number-header {
|
||||
.group-scene-number-header {
|
||||
color: $text-muted;
|
||||
font-size: 0.8em;
|
||||
padding-bottom: 0;
|
||||
|
@ -308,7 +308,7 @@ textarea.scene-description {
|
|||
}
|
||||
}
|
||||
|
||||
.movie-table.no-movies .movie-table-header {
|
||||
.group-table.no-groups .group-table-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from "react";
|
||||
import React, { useCallback, useMemo } from "react";
|
||||
import { Button, Form } from "react-bootstrap";
|
||||
import { FormattedMessage, useIntl } from "react-intl";
|
||||
import { DurationInput } from "src/components/Shared/DurationInput";
|
||||
|
@ -47,7 +47,7 @@ import { defaultMaxOptionsShown } from "src/core/config";
|
|||
const allMenuItems = [
|
||||
{ id: "scenes", headingID: "scenes" },
|
||||
{ id: "images", headingID: "images" },
|
||||
{ id: "movies", headingID: "movies" },
|
||||
{ id: "groups", headingID: "groups" },
|
||||
{ id: "markers", headingID: "markers" },
|
||||
{ id: "galleries", headingID: "galleries" },
|
||||
{ id: "performers", headingID: "performers" },
|
||||
|
@ -67,6 +67,22 @@ export const SettingsInterfacePanel: React.FC = () => {
|
|||
error,
|
||||
} = useSettings();
|
||||
|
||||
// convert old movies menu item to groups
|
||||
const massageMenuItems = useCallback((menuItems: string[]) => {
|
||||
return menuItems.map((item) => {
|
||||
if (item === "movies") {
|
||||
return "groups";
|
||||
}
|
||||
return item;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const massagedMenuItems = useMemo(() => {
|
||||
if (!iface.menuItems) return iface.menuItems;
|
||||
|
||||
return massageMenuItems(iface.menuItems);
|
||||
}, [iface.menuItems, massageMenuItems]);
|
||||
|
||||
const {
|
||||
interactive,
|
||||
state: interactiveState,
|
||||
|
@ -231,8 +247,8 @@ export const SettingsInterfacePanel: React.FC = () => {
|
|||
<CheckboxGroup
|
||||
groupId="menu-items"
|
||||
items={allMenuItems}
|
||||
checkedIds={iface.menuItems ?? undefined}
|
||||
onChange={(v) => saveInterface({ menuItems: v })}
|
||||
checkedIds={massagedMenuItems ?? undefined}
|
||||
onChange={(v) => saveInterface({ menuItems: massageMenuItems(v) })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
@ -563,7 +579,7 @@ export const SettingsInterfacePanel: React.FC = () => {
|
|||
</div>
|
||||
<BooleanSetting
|
||||
id="enableMovieBackgroundImage"
|
||||
headingID="movie"
|
||||
headingID="group"
|
||||
checked={ui.enableMovieBackgroundImage ?? undefined}
|
||||
onChange={(v) => saveUI({ enableMovieBackgroundImage: v })}
|
||||
/>
|
||||
|
@ -659,8 +675,8 @@ export const SettingsInterfacePanel: React.FC = () => {
|
|||
}
|
||||
/>
|
||||
<BooleanSetting
|
||||
id="disableDropdownCreate_movie"
|
||||
headingID="movie"
|
||||
id="disableDropdownCreate_group"
|
||||
headingID="group"
|
||||
checked={iface.disableDropdownCreate?.movie ?? undefined}
|
||||
onChange={(v) =>
|
||||
saveInterface({
|
||||
|
|
|
@ -79,7 +79,7 @@ export const SettingsScrapingPanel: React.FC = () => {
|
|||
useListSceneScrapers();
|
||||
const { data: galleryScrapers, loading: loadingGalleries } =
|
||||
useListGalleryScrapers();
|
||||
const { data: movieScrapers, loading: loadingMovies } =
|
||||
const { data: groupScrapers, loading: loadingGroups } =
|
||||
useListMovieScrapers();
|
||||
|
||||
const { general, scraping, loading, error, saveGeneral, saveScraping } =
|
||||
|
@ -158,13 +158,13 @@ export const SettingsScrapingPanel: React.FC = () => {
|
|||
);
|
||||
}
|
||||
|
||||
function renderMovieScrapeTypes(types: ScrapeType[]) {
|
||||
function renderGroupScrapeTypes(types: ScrapeType[]) {
|
||||
const typeStrings = types.map((t) => {
|
||||
switch (t) {
|
||||
case ScrapeType.Fragment:
|
||||
return intl.formatMessage(
|
||||
{ id: "config.scraping.entity_metadata" },
|
||||
{ entityType: intl.formatMessage({ id: "movie" }) }
|
||||
{ entityType: intl.formatMessage({ id: "group" }) }
|
||||
);
|
||||
default:
|
||||
return t;
|
||||
|
@ -246,12 +246,12 @@ export const SettingsScrapingPanel: React.FC = () => {
|
|||
);
|
||||
}
|
||||
|
||||
function renderMovieScrapers() {
|
||||
const elements = (movieScrapers?.listScrapers ?? []).map((scraper) => (
|
||||
function renderGroupScrapers() {
|
||||
const elements = (groupScrapers?.listScrapers ?? []).map((scraper) => (
|
||||
<tr key={scraper.id}>
|
||||
<td>{scraper.name}</td>
|
||||
<td>
|
||||
{renderMovieScrapeTypes(scraper.movie?.supported_scrapes ?? [])}
|
||||
{renderGroupScrapeTypes(scraper.movie?.supported_scrapes ?? [])}
|
||||
</td>
|
||||
<td>{renderURLs(scraper.movie?.urls ?? [])}</td>
|
||||
</tr>
|
||||
|
@ -260,7 +260,7 @@ export const SettingsScrapingPanel: React.FC = () => {
|
|||
return renderTable(
|
||||
intl.formatMessage(
|
||||
{ id: "config.scraping.entity_scrapers" },
|
||||
{ entityType: intl.formatMessage({ id: "movie" }) }
|
||||
{ entityType: intl.formatMessage({ id: "group" }) }
|
||||
),
|
||||
elements
|
||||
);
|
||||
|
@ -297,7 +297,7 @@ export const SettingsScrapingPanel: React.FC = () => {
|
|||
loadingScenes ||
|
||||
loadingGalleries ||
|
||||
loadingPerformers ||
|
||||
loadingMovies
|
||||
loadingGroups
|
||||
)
|
||||
return <LoadingIndicator />;
|
||||
|
||||
|
@ -361,7 +361,7 @@ export const SettingsScrapingPanel: React.FC = () => {
|
|||
{renderSceneScrapers()}
|
||||
{renderGalleryScrapers()}
|
||||
{renderPerformerScrapers()}
|
||||
{renderMovieScrapers()}
|
||||
{renderGroupScrapers()}
|
||||
</div>
|
||||
</SettingSection>
|
||||
</>
|
||||
|
|
|
@ -6,14 +6,14 @@ import NavUtils from "src/utils/navigation";
|
|||
|
||||
export const DirectorLink: React.FC<{
|
||||
director: string;
|
||||
linkType: "scene" | "movie";
|
||||
linkType: "scene" | "group";
|
||||
}> = ({ director: director, linkType = "scene" }) => {
|
||||
const link = useMemo(() => {
|
||||
switch (linkType) {
|
||||
case "scene":
|
||||
return NavUtils.makeDirectorScenesUrl(director);
|
||||
case "movie":
|
||||
return NavUtils.makeDirectorMoviesUrl(director);
|
||||
case "group":
|
||||
return NavUtils.makeDirectorGroupsUrl(director);
|
||||
}
|
||||
}, [director, linkType]);
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
} from "../Galleries/GallerySelect";
|
||||
|
||||
interface IMultiSetProps {
|
||||
type: "performers" | "studios" | "tags" | "movies" | "galleries";
|
||||
type: "performers" | "studios" | "tags" | "groups" | "galleries";
|
||||
existingIds?: string[];
|
||||
ids?: string[];
|
||||
mode: GQL.BulkUpdateIdMode;
|
||||
|
|
|
@ -20,7 +20,7 @@ type PopoverLinkType =
|
|||
| "image"
|
||||
| "gallery"
|
||||
| "marker"
|
||||
| "movie"
|
||||
| "group"
|
||||
| "performer"
|
||||
| "studio";
|
||||
|
||||
|
@ -52,7 +52,7 @@ export const PopoverCountButton: React.FC<IProps> = ({
|
|||
return faImages;
|
||||
case "marker":
|
||||
return faMapMarkerAlt;
|
||||
case "movie":
|
||||
case "group":
|
||||
return faFilm;
|
||||
case "performer":
|
||||
return faUser;
|
||||
|
@ -83,10 +83,10 @@ export const PopoverCountButton: React.FC<IProps> = ({
|
|||
one: "marker",
|
||||
other: "markers",
|
||||
};
|
||||
case "movie":
|
||||
case "group":
|
||||
return {
|
||||
one: "movie",
|
||||
other: "movies",
|
||||
one: "group",
|
||||
other: "groups",
|
||||
};
|
||||
case "performer":
|
||||
return {
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
} from "src/components/Shared/ScrapeDialog/scrapeResult";
|
||||
import { TagSelect } from "src/components/Tags/TagSelect";
|
||||
import { StudioSelect } from "src/components/Studios/StudioSelect";
|
||||
import { MovieSelect } from "src/components/Movies/MovieSelect";
|
||||
import { GroupSelect } from "src/components/Movies/MovieSelect";
|
||||
|
||||
interface IScrapedStudioRow {
|
||||
title: string;
|
||||
|
@ -196,10 +196,10 @@ export const ScrapedPerformersRow: React.FC<
|
|||
);
|
||||
};
|
||||
|
||||
export const ScrapedMoviesRow: React.FC<
|
||||
export const ScrapedGroupsRow: React.FC<
|
||||
IScrapedObjectRowImpl<GQL.ScrapedMovie>
|
||||
> = ({ title, result, onChange, newObjects, onCreateNew }) => {
|
||||
const moviesCopy = useMemo(() => {
|
||||
const groupsCopy = useMemo(() => {
|
||||
return (
|
||||
newObjects?.map((p) => {
|
||||
const name: string = p.name ?? "";
|
||||
|
@ -208,7 +208,7 @@ export const ScrapedMoviesRow: React.FC<
|
|||
);
|
||||
}, [newObjects]);
|
||||
|
||||
function renderScrapedMovies(
|
||||
function renderScrapedGroups(
|
||||
scrapeResult: ScrapeResult<GQL.ScrapedMovie[]>,
|
||||
isNew?: boolean,
|
||||
onChangeFn?: (value: GQL.ScrapedMovie[]) => void
|
||||
|
@ -228,7 +228,7 @@ export const ScrapedMoviesRow: React.FC<
|
|||
});
|
||||
|
||||
return (
|
||||
<MovieSelect
|
||||
<GroupSelect
|
||||
isMulti
|
||||
className="form-control react-select"
|
||||
isDisabled={!isNew}
|
||||
|
@ -247,9 +247,9 @@ export const ScrapedMoviesRow: React.FC<
|
|||
<ScrapedObjectsRow<GQL.ScrapedMovie>
|
||||
title={title}
|
||||
result={result}
|
||||
renderObjects={renderScrapedMovies}
|
||||
renderObjects={renderScrapedGroups}
|
||||
onChange={onChange}
|
||||
newObjects={moviesCopy}
|
||||
newObjects={groupsCopy}
|
||||
onCreateNew={onCreateNew}
|
||||
getName={(value) => value.name ?? ""}
|
||||
/>
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
import { ObjectScrapeResult, ScrapeResult } from "./scrapeResult";
|
||||
import { useIntl } from "react-intl";
|
||||
import { scrapedPerformerToCreateInput } from "src/core/performers";
|
||||
import { scrapedMovieToCreateInput } from "src/core/movies";
|
||||
import { scrapedGroupToCreateInput } from "src/core/movies";
|
||||
|
||||
function useCreateObject<T>(
|
||||
entityTypeID: string,
|
||||
|
@ -123,16 +123,16 @@ export function useCreateScrapedPerformer(
|
|||
return useCreateObject("performer", createNewPerformer);
|
||||
}
|
||||
|
||||
export function useCreateScrapedMovie(
|
||||
export function useCreateScrapedGroup(
|
||||
props: IUseCreateNewObjectProps<GQL.ScrapedMovie>
|
||||
) {
|
||||
const { scrapeResult, setScrapeResult, newObjects, setNewObjects } = props;
|
||||
const [createMovie] = useMovieCreate();
|
||||
const [createGroup] = useMovieCreate();
|
||||
|
||||
async function createNewMovie(toCreate: GQL.ScrapedMovie) {
|
||||
const input = scrapedMovieToCreateInput(toCreate);
|
||||
async function createNewGroup(toCreate: GQL.ScrapedMovie) {
|
||||
const input = scrapedGroupToCreateInput(toCreate);
|
||||
|
||||
const result = await createMovie({
|
||||
const result = await createGroup({
|
||||
variables: { input: input },
|
||||
});
|
||||
|
||||
|
@ -150,14 +150,14 @@ export function useCreateScrapedMovie(
|
|||
// remove the object from the list
|
||||
const newObjectsClone = newObjects.concat();
|
||||
const pIndex = newObjectsClone.findIndex((p) => p.name === toCreate.name);
|
||||
if (pIndex === -1) throw new Error("Could not find movie to remove");
|
||||
if (pIndex === -1) throw new Error("Could not find group to remove");
|
||||
|
||||
newObjectsClone.splice(pIndex, 1);
|
||||
|
||||
setNewObjects(newObjectsClone);
|
||||
}
|
||||
|
||||
return useCreateObject("movie", createNewMovie);
|
||||
return useCreateObject("group", createNewGroup);
|
||||
}
|
||||
|
||||
export function useCreateScrapedTag(
|
||||
|
|
|
@ -26,7 +26,7 @@ import { faTableColumns } from "@fortawesome/free-solid-svg-icons";
|
|||
import { TagIDSelect } from "../Tags/TagSelect";
|
||||
import { StudioIDSelect } from "../Studios/StudioSelect";
|
||||
import { GalleryIDSelect } from "../Galleries/GallerySelect";
|
||||
import { MovieIDSelect } from "../Movies/MovieSelect";
|
||||
import { GroupIDSelect } from "../Movies/MovieSelect";
|
||||
import { SceneIDSelect } from "../Scenes/SceneSelect";
|
||||
|
||||
export type SelectObject = {
|
||||
|
@ -44,7 +44,7 @@ interface ITypeProps {
|
|||
| "scene_tags"
|
||||
| "performer_tags"
|
||||
| "scenes"
|
||||
| "movies"
|
||||
| "groups"
|
||||
| "galleries";
|
||||
}
|
||||
interface IFilterProps {
|
||||
|
@ -364,8 +364,8 @@ export const StudioSelect: React.FC<
|
|||
return <StudioIDSelect {...props} />;
|
||||
};
|
||||
|
||||
export const MovieSelect: React.FC<IFilterProps> = (props) => {
|
||||
return <MovieIDSelect {...props} />;
|
||||
export const GroupSelect: React.FC<IFilterProps> = (props) => {
|
||||
return <GroupIDSelect {...props} />;
|
||||
};
|
||||
|
||||
export const TagSelect: React.FC<
|
||||
|
@ -382,8 +382,8 @@ export const FilterSelect: React.FC<IFilterProps & ITypeProps> = (props) => {
|
|||
return <StudioSelect {...props} creatable={false} />;
|
||||
case "scenes":
|
||||
return <SceneSelect {...props} creatable={false} />;
|
||||
case "movies":
|
||||
return <MovieSelect {...props} creatable={false} />;
|
||||
case "groups":
|
||||
return <GroupSelect {...props} creatable={false} />;
|
||||
case "galleries":
|
||||
return <GallerySelect {...props} />;
|
||||
default:
|
||||
|
|
|
@ -71,25 +71,25 @@ export const PerformerLink: React.FC<IPerformerLinkProps> = ({
|
|||
);
|
||||
};
|
||||
|
||||
interface IMovieLinkProps {
|
||||
movie: INamedObject;
|
||||
interface IGroupLinkProps {
|
||||
group: INamedObject;
|
||||
linkType?: "scene";
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const MovieLink: React.FC<IMovieLinkProps> = ({
|
||||
movie,
|
||||
export const GroupLink: React.FC<IGroupLinkProps> = ({
|
||||
group,
|
||||
linkType = "scene",
|
||||
className,
|
||||
}) => {
|
||||
const link = useMemo(() => {
|
||||
switch (linkType) {
|
||||
case "scene":
|
||||
return NavUtils.makeMovieScenesUrl(movie);
|
||||
return NavUtils.makeGroupScenesUrl(group);
|
||||
}
|
||||
}, [movie, linkType]);
|
||||
}, [group, linkType]);
|
||||
|
||||
const title = movie.name || "";
|
||||
const title = group.name || "";
|
||||
|
||||
return (
|
||||
<CommonLinkComponent link={link} className={className}>
|
||||
|
@ -197,7 +197,7 @@ interface ITagLinkProps {
|
|||
| "image"
|
||||
| "details"
|
||||
| "performer"
|
||||
| "movie"
|
||||
| "group"
|
||||
| "studio";
|
||||
className?: string;
|
||||
hoverPlacement?: Placement;
|
||||
|
@ -225,8 +225,8 @@ export const TagLink: React.FC<ITagLinkProps> = ({
|
|||
return NavUtils.makeTagGalleriesUrl(tag);
|
||||
case "image":
|
||||
return NavUtils.makeTagImagesUrl(tag);
|
||||
case "movie":
|
||||
return NavUtils.makeTagMoviesUrl(tag);
|
||||
case "group":
|
||||
return NavUtils.makeTagGroupsUrl(tag);
|
||||
case "details":
|
||||
return NavUtils.makeTagUrl(tag.id ?? "");
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ export const Stats: React.FC = () => {
|
|||
<FormattedNumber value={data.stats.movie_count} />
|
||||
</p>
|
||||
<p className="heading">
|
||||
<FormattedMessage id="movies" />
|
||||
<FormattedMessage id="groups" />
|
||||
</p>
|
||||
</div>
|
||||
<div className="stats-element">
|
||||
|
|
|
@ -142,15 +142,15 @@ export const StudioCard: React.FC<IProps> = ({
|
|||
);
|
||||
}
|
||||
|
||||
function maybeRenderMoviesPopoverButton() {
|
||||
function maybeRenderGroupsPopoverButton() {
|
||||
if (!studio.movie_count) return;
|
||||
|
||||
return (
|
||||
<PopoverCountButton
|
||||
className="movie-count"
|
||||
type="movie"
|
||||
className="group-count"
|
||||
type="group"
|
||||
count={studio.movie_count}
|
||||
url={NavUtils.makeStudioMoviesUrl(studio)}
|
||||
url={NavUtils.makeStudioGroupsUrl(studio)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -199,7 +199,7 @@ export const StudioCard: React.FC<IProps> = ({
|
|||
<hr />
|
||||
<ButtonGroup className="card-popovers">
|
||||
{maybeRenderScenesPopoverButton()}
|
||||
{maybeRenderMoviesPopoverButton()}
|
||||
{maybeRenderGroupsPopoverButton()}
|
||||
{maybeRenderImagesPopoverButton()}
|
||||
{maybeRenderGalleriesPopoverButton()}
|
||||
{maybeRenderPerformersPopoverButton()}
|
||||
|
|
|
@ -31,7 +31,7 @@ import {
|
|||
CompressedStudioDetailsPanel,
|
||||
StudioDetailsPanel,
|
||||
} from "./StudioDetailsPanel";
|
||||
import { StudioMoviesPanel } from "./StudioMoviesPanel";
|
||||
import { StudioGroupsPanel } from "./StudioMoviesPanel";
|
||||
import {
|
||||
faTrashAlt,
|
||||
faLink,
|
||||
|
@ -63,7 +63,7 @@ const validTabs = [
|
|||
"galleries",
|
||||
"images",
|
||||
"performers",
|
||||
"movies",
|
||||
"groups",
|
||||
"childstudios",
|
||||
] as const;
|
||||
type TabKey = (typeof validTabs)[number];
|
||||
|
@ -108,7 +108,7 @@ const StudioPage: React.FC<IProps> = ({ studio, tabKey }) => {
|
|||
(showAllCounts ? studio.image_count_all : studio.image_count) ?? 0;
|
||||
const performerCount =
|
||||
(showAllCounts ? studio.performer_count_all : studio.performer_count) ?? 0;
|
||||
const movieCount =
|
||||
const groupCount =
|
||||
(showAllCounts ? studio.movie_count_all : studio.movie_count) ?? 0;
|
||||
|
||||
const populatedDefaultTab = useMemo(() => {
|
||||
|
@ -120,8 +120,8 @@ const StudioPage: React.FC<IProps> = ({ studio, tabKey }) => {
|
|||
ret = "images";
|
||||
} else if (performerCount != 0) {
|
||||
ret = "performers";
|
||||
} else if (movieCount != 0) {
|
||||
ret = "movies";
|
||||
} else if (groupCount != 0) {
|
||||
ret = "groups";
|
||||
} else if (studio.child_studios.length != 0) {
|
||||
ret = "childstudios";
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ const StudioPage: React.FC<IProps> = ({ studio, tabKey }) => {
|
|||
galleryCount,
|
||||
imageCount,
|
||||
performerCount,
|
||||
movieCount,
|
||||
groupCount,
|
||||
studio,
|
||||
]);
|
||||
|
||||
|
@ -437,19 +437,19 @@ const StudioPage: React.FC<IProps> = ({ studio, tabKey }) => {
|
|||
/>
|
||||
</Tab>
|
||||
<Tab
|
||||
eventKey="movies"
|
||||
eventKey="groups"
|
||||
title={
|
||||
<>
|
||||
{intl.formatMessage({ id: "movies" })}
|
||||
{intl.formatMessage({ id: "groups" })}
|
||||
<Counter
|
||||
abbreviateCounter={abbreviateCounter}
|
||||
count={movieCount}
|
||||
count={groupCount}
|
||||
hideZero
|
||||
/>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<StudioMoviesPanel active={tabKey === "movies"} studio={studio} />
|
||||
<StudioGroupsPanel active={tabKey === "groups"} studio={studio} />
|
||||
</Tab>
|
||||
<Tab
|
||||
eventKey="childstudios"
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
import React from "react";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { MovieList } from "src/components/Movies/MovieList";
|
||||
import { GroupList } from "src/components/Movies/MovieList";
|
||||
import { useStudioFilterHook } from "src/core/studios";
|
||||
import { View } from "src/components/List/views";
|
||||
|
||||
interface IStudioMoviesPanel {
|
||||
interface IStudioGroupsPanel {
|
||||
active: boolean;
|
||||
studio: GQL.StudioDataFragment;
|
||||
}
|
||||
|
||||
export const StudioMoviesPanel: React.FC<IStudioMoviesPanel> = ({
|
||||
export const StudioGroupsPanel: React.FC<IStudioGroupsPanel> = ({
|
||||
active,
|
||||
studio,
|
||||
}) => {
|
||||
const filterHook = useStudioFilterHook(studio);
|
||||
return (
|
||||
<MovieList
|
||||
<GroupList
|
||||
filterHook={filterHook}
|
||||
alterQuery={active}
|
||||
view={View.StudioMovies}
|
||||
view={View.StudioGroups}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -236,15 +236,15 @@ export const TagCard: React.FC<IProps> = ({
|
|||
);
|
||||
}
|
||||
|
||||
function maybeRenderMoviesPopoverButton() {
|
||||
function maybeRenderGroupsPopoverButton() {
|
||||
if (!tag.movie_count) return;
|
||||
|
||||
return (
|
||||
<PopoverCountButton
|
||||
className="movie-count"
|
||||
type="movie"
|
||||
className="group-count"
|
||||
type="group"
|
||||
count={tag.movie_count}
|
||||
url={NavUtils.makeTagMoviesUrl(tag)}
|
||||
url={NavUtils.makeTagGroupsUrl(tag)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -258,7 +258,7 @@ export const TagCard: React.FC<IProps> = ({
|
|||
{maybeRenderScenesPopoverButton()}
|
||||
{maybeRenderImagesPopoverButton()}
|
||||
{maybeRenderGalleriesPopoverButton()}
|
||||
{maybeRenderMoviesPopoverButton()}
|
||||
{maybeRenderGroupsPopoverButton()}
|
||||
{maybeRenderSceneMarkersPopoverButton()}
|
||||
{maybeRenderPerformersPopoverButton()}
|
||||
{maybeRenderStudiosPopoverButton()}
|
||||
|
|
|
@ -42,7 +42,7 @@ import {
|
|||
import { DetailImage } from "src/components/Shared/DetailImage";
|
||||
import { useLoadStickyHeader } from "src/hooks/detailsPanel";
|
||||
import { useScrollToTopOnMount } from "src/hooks/scrollToTop";
|
||||
import { TagMoviesPanel } from "./TagMoviesPanel";
|
||||
import { TagGroupsPanel } from "./TagMoviesPanel";
|
||||
|
||||
interface IProps {
|
||||
tag: GQL.TagDataFragment;
|
||||
|
@ -59,7 +59,7 @@ const validTabs = [
|
|||
"scenes",
|
||||
"images",
|
||||
"galleries",
|
||||
"movies",
|
||||
"groups",
|
||||
"markers",
|
||||
"performers",
|
||||
"studios",
|
||||
|
@ -105,7 +105,7 @@ const TagPage: React.FC<IProps> = ({ tag, tabKey }) => {
|
|||
(showAllCounts ? tag.image_count_all : tag.image_count) ?? 0;
|
||||
const galleryCount =
|
||||
(showAllCounts ? tag.gallery_count_all : tag.gallery_count) ?? 0;
|
||||
const movieCount =
|
||||
const groupCount =
|
||||
(showAllCounts ? tag.movie_count_all : tag.movie_count) ?? 0;
|
||||
const sceneMarkerCount =
|
||||
(showAllCounts ? tag.scene_marker_count_all : tag.scene_marker_count) ?? 0;
|
||||
|
@ -121,8 +121,8 @@ const TagPage: React.FC<IProps> = ({ tag, tabKey }) => {
|
|||
ret = "images";
|
||||
} else if (galleryCount != 0) {
|
||||
ret = "galleries";
|
||||
} else if (movieCount != 0) {
|
||||
ret = "movies";
|
||||
} else if (groupCount != 0) {
|
||||
ret = "groups";
|
||||
} else if (sceneMarkerCount != 0) {
|
||||
ret = "markers";
|
||||
} else if (performerCount != 0) {
|
||||
|
@ -140,7 +140,7 @@ const TagPage: React.FC<IProps> = ({ tag, tabKey }) => {
|
|||
sceneMarkerCount,
|
||||
performerCount,
|
||||
studioCount,
|
||||
movieCount,
|
||||
groupCount,
|
||||
]);
|
||||
|
||||
const setTabKey = useCallback(
|
||||
|
@ -484,19 +484,19 @@ const TagPage: React.FC<IProps> = ({ tag, tabKey }) => {
|
|||
<TagGalleriesPanel active={tabKey === "galleries"} tag={tag} />
|
||||
</Tab>
|
||||
<Tab
|
||||
eventKey="movies"
|
||||
eventKey="groups"
|
||||
title={
|
||||
<>
|
||||
{intl.formatMessage({ id: "movies" })}
|
||||
{intl.formatMessage({ id: "groups" })}
|
||||
<Counter
|
||||
abbreviateCounter={abbreviateCounter}
|
||||
count={movieCount}
|
||||
count={groupCount}
|
||||
hideZero
|
||||
/>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<TagMoviesPanel active={tabKey === "movies"} tag={tag} />
|
||||
<TagGroupsPanel active={tabKey === "groups"} tag={tag} />
|
||||
</Tab>
|
||||
<Tab
|
||||
eventKey="markers"
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import React from "react";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { useTagFilterHook } from "src/core/tags";
|
||||
import { MovieList } from "src/components/Movies/MovieList";
|
||||
import { GroupList } from "src/components/Movies/MovieList";
|
||||
|
||||
export const TagMoviesPanel: React.FC<{
|
||||
export const TagGroupsPanel: React.FC<{
|
||||
active: boolean;
|
||||
tag: GQL.TagDataFragment;
|
||||
}> = ({ active, tag }) => {
|
||||
const filterHook = useTagFilterHook(tag);
|
||||
return <MovieList filterHook={filterHook} alterQuery={active} />;
|
||||
return <GroupList filterHook={filterHook} alterQuery={active} />;
|
||||
};
|
||||
|
|
|
@ -143,7 +143,7 @@ export function generateDefaultFrontPageContent(intl: IntlShape) {
|
|||
return [
|
||||
recentlyReleased(intl, FilterMode.Scenes, "scenes"),
|
||||
recentlyAdded(intl, FilterMode.Studios, "studios"),
|
||||
recentlyReleased(intl, FilterMode.Movies, "movies"),
|
||||
recentlyReleased(intl, FilterMode.Movies, "groups"),
|
||||
recentlyAdded(intl, FilterMode.Performers, "performers"),
|
||||
recentlyReleased(intl, FilterMode.Galleries, "galleries"),
|
||||
];
|
||||
|
@ -156,8 +156,8 @@ export function generatePremadeFrontPageContent(intl: IntlShape) {
|
|||
recentlyReleased(intl, FilterMode.Galleries, "galleries"),
|
||||
recentlyAdded(intl, FilterMode.Galleries, "galleries"),
|
||||
recentlyAdded(intl, FilterMode.Images, "images"),
|
||||
recentlyReleased(intl, FilterMode.Movies, "movies"),
|
||||
recentlyAdded(intl, FilterMode.Movies, "movies"),
|
||||
recentlyReleased(intl, FilterMode.Movies, "groups"),
|
||||
recentlyAdded(intl, FilterMode.Movies, "groups"),
|
||||
recentlyAdded(intl, FilterMode.Studios, "studios"),
|
||||
recentlyAdded(intl, FilterMode.Performers, "performers"),
|
||||
];
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as GQL from "src/core/generated-graphql";
|
||||
import TextUtils from "src/utils/text";
|
||||
|
||||
export const scrapedMovieToCreateInput = (toCreate: GQL.ScrapedMovie) => {
|
||||
export const scrapedGroupToCreateInput = (toCreate: GQL.ScrapedMovie) => {
|
||||
const input: GQL.MovieCreateInput = {
|
||||
name: toCreate.name ?? "",
|
||||
url: toCreate.url,
|
||||
|
|
|
@ -54,7 +54,7 @@ body {
|
|||
}
|
||||
}
|
||||
|
||||
#movie-page,
|
||||
#group-page,
|
||||
#performer-page,
|
||||
#studio-page,
|
||||
#tag-page {
|
||||
|
@ -83,7 +83,7 @@ dd {
|
|||
display: none;
|
||||
}
|
||||
|
||||
.movie-name,
|
||||
.group-name,
|
||||
.performer-name,
|
||||
.studio-name,
|
||||
.tag-name {
|
||||
|
@ -93,7 +93,7 @@ dd {
|
|||
.sticky.detail-header-group {
|
||||
padding: 1rem 2.5rem;
|
||||
|
||||
a.movie-name,
|
||||
a.group-name,
|
||||
a.performer-name,
|
||||
a.studio-name,
|
||||
a.tag-name {
|
||||
|
@ -313,7 +313,7 @@ dd {
|
|||
justify-content: center;
|
||||
padding: 0 1rem;
|
||||
|
||||
.movie-images {
|
||||
.group-images {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
@ -322,7 +322,7 @@ dd {
|
|||
height: auto;
|
||||
padding: 0;
|
||||
|
||||
.movie-images {
|
||||
.group-images {
|
||||
.img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
@ -335,18 +335,18 @@ dd {
|
|||
transition: 0.5s;
|
||||
}
|
||||
|
||||
.movie-images img {
|
||||
.group-images img {
|
||||
@media (max-width: 576px) {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#movie-page .detail-header-image .movie-images img {
|
||||
#group-page .detail-header-image .group-images img {
|
||||
max-width: 13rem;
|
||||
}
|
||||
|
||||
#movie-page .detail-header-image img,
|
||||
#group-page .detail-header-image img,
|
||||
#performer-page .detail-header-image img,
|
||||
#tag-page .detail-header-image img {
|
||||
border-radius: 0.5rem;
|
||||
|
|
|
@ -798,9 +798,9 @@
|
|||
"countables": {
|
||||
"files": "{count, plural, one {File} other {Files}}",
|
||||
"galleries": "{count, plural, one {Gallery} other {Galleries}}",
|
||||
"groups": "{count, plural, one {Group} other {Groups}}",
|
||||
"images": "{count, plural, one {Image} other {Images}}",
|
||||
"markers": "{count, plural, one {Marker} other {Markers}}",
|
||||
"movies": "{count, plural, one {Movie} other {Movies}}",
|
||||
"performers": "{count, plural, one {Performer} other {Performers}}",
|
||||
"scenes": "{count, plural, one {Scene} other {Scenes}}",
|
||||
"studios": "{count, plural, one {Studio} other {Studios}}",
|
||||
|
@ -1060,6 +1060,10 @@
|
|||
"TRANSGENDER_FEMALE": "Transgender Female",
|
||||
"TRANSGENDER_MALE": "Transgender Male"
|
||||
},
|
||||
"group": "Group",
|
||||
"group_count": "Group Count",
|
||||
"group_scene_number": "Scene Number",
|
||||
"groups": "Groups",
|
||||
"hair_color": "Hair Colour",
|
||||
"handy_connection_status": {
|
||||
"connecting": "Connecting",
|
||||
|
@ -1117,10 +1121,6 @@
|
|||
},
|
||||
"megabits_per_second": "{value} mbps",
|
||||
"metadata": "Metadata",
|
||||
"movie": "Movie",
|
||||
"movie_count": "Movie Count",
|
||||
"movie_scene_number": "Scene Number",
|
||||
"movies": "Movies",
|
||||
"name": "Name",
|
||||
"new": "New",
|
||||
"none": "None",
|
||||
|
|
|
@ -173,7 +173,7 @@ export type InputType =
|
|||
| "performer_tags"
|
||||
| "scenes"
|
||||
| "scene_tags"
|
||||
| "movies"
|
||||
| "groups"
|
||||
| "galleries"
|
||||
| undefined;
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { ILabeledIdCriterion, ILabeledIdCriterionOption } from "./criterion";
|
||||
|
||||
const inputType = "movies";
|
||||
const inputType = "groups";
|
||||
|
||||
export const MoviesCriterionOption = new ILabeledIdCriterionOption(
|
||||
"movies",
|
||||
"groups",
|
||||
"movies",
|
||||
false,
|
||||
inputType,
|
||||
|
|
|
@ -47,7 +47,6 @@ const sortByOptions = [
|
|||
"resume_time",
|
||||
"play_duration",
|
||||
"play_count",
|
||||
"movie_scene_number",
|
||||
"interactive",
|
||||
"interactive_speed",
|
||||
"perceptual_similarity",
|
||||
|
@ -59,6 +58,10 @@ const sortByOptions = [
|
|||
messageID: "o_count",
|
||||
value: "o_counter",
|
||||
},
|
||||
{
|
||||
messageID: "group_scene_number",
|
||||
value: "movie_scene_number",
|
||||
},
|
||||
]);
|
||||
const displayModeOptions = [
|
||||
DisplayMode.Grid,
|
||||
|
|
|
@ -36,7 +36,7 @@ const sortByOptions = ["name", "random"]
|
|||
value: "scenes_count",
|
||||
},
|
||||
{
|
||||
messageID: "movie_count",
|
||||
messageID: "group_count",
|
||||
value: "movies_count",
|
||||
},
|
||||
{
|
||||
|
@ -62,7 +62,7 @@ const criterionOptions = [
|
|||
createMandatoryNumberCriterionOption("gallery_count"),
|
||||
createMandatoryNumberCriterionOption("performer_count"),
|
||||
createMandatoryNumberCriterionOption("studio_count"),
|
||||
createMandatoryNumberCriterionOption("movie_count"),
|
||||
createMandatoryNumberCriterionOption("movie_count", "group_count"),
|
||||
createMandatoryNumberCriterionOption("marker_count"),
|
||||
ParentTagsCriterionOption,
|
||||
new MandatoryNumberCriterionOption("parent_tag_count", "parent_count"),
|
||||
|
|
|
@ -693,12 +693,12 @@ declare namespace PluginApi {
|
|||
function makePerformerScenesUrl(...args: any[]): any;
|
||||
function makePerformerImagesUrl(...args: any[]): any;
|
||||
function makePerformerGalleriesUrl(...args: any[]): any;
|
||||
function makePerformerMoviesUrl(...args: any[]): any;
|
||||
function makePerformerGroupsUrl(...args: any[]): any;
|
||||
function makePerformersCountryUrl(...args: any[]): any;
|
||||
function makeStudioScenesUrl(...args: any[]): any;
|
||||
function makeStudioImagesUrl(...args: any[]): any;
|
||||
function makeStudioGalleriesUrl(...args: any[]): any;
|
||||
function makeStudioMoviesUrl(...args: any[]): any;
|
||||
function makeStudioGroupsUrl(...args: any[]): any;
|
||||
function makeStudioPerformersUrl(...args: any[]): any;
|
||||
function makeTagUrl(...args: any[]): any;
|
||||
function makeParentTagsUrl(...args: any[]): any;
|
||||
|
@ -710,7 +710,7 @@ declare namespace PluginApi {
|
|||
function makeTagImagesUrl(...args: any[]): any;
|
||||
function makeScenesPHashMatchUrl(...args: any[]): any;
|
||||
function makeSceneMarkerUrl(...args: any[]): any;
|
||||
function makeMovieScenesUrl(...args: any[]): any;
|
||||
function makeGroupScenesUrl(...args: any[]): any;
|
||||
function makeChildStudiosUrl(...args: any[]): any;
|
||||
function makeGalleryImagesUrl(...args: any[]): any;
|
||||
}
|
||||
|
|
|
@ -81,11 +81,11 @@ export function getAggregateTagIds(state: { tags: IHasID[] }[]) {
|
|||
return getAggregateIds(sortedLists);
|
||||
}
|
||||
|
||||
interface IMovie {
|
||||
interface IGroup {
|
||||
movie: IHasID;
|
||||
}
|
||||
|
||||
export function getAggregateMovieIds(state: { movies: IMovie[] }[]) {
|
||||
export function getAggregateGroupIds(state: { movies: IGroup[] }[]) {
|
||||
const sortedLists = state.map((o) =>
|
||||
o.movies.map((oo) => oo.movie.id).sort()
|
||||
);
|
||||
|
|
|
@ -103,7 +103,7 @@ const makePerformerGalleriesUrl = (
|
|||
return `/galleries?${filter.makeQueryParameters()}`;
|
||||
};
|
||||
|
||||
const makePerformerMoviesUrl = (
|
||||
const makePerformerGroupsUrl = (
|
||||
performer: Partial<GQL.PerformerDataFragment>,
|
||||
extraPerformer?: ILabeledId,
|
||||
extraCriteria?: Criterion<CriterionValue>[]
|
||||
|
@ -121,7 +121,7 @@ const makePerformerMoviesUrl = (
|
|||
|
||||
filter.criteria.push(criterion);
|
||||
addExtraCriteria(filter.criteria, extraCriteria);
|
||||
return `/movies?${filter.makeQueryParameters()}`;
|
||||
return `/groups?${filter.makeQueryParameters()}`;
|
||||
};
|
||||
|
||||
const makePerformersCountryUrl = (
|
||||
|
@ -174,7 +174,7 @@ const makeStudioGalleriesUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
|||
return `/galleries?${filter.makeQueryParameters()}`;
|
||||
};
|
||||
|
||||
const makeStudioMoviesUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
||||
const makeStudioGroupsUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
||||
if (!studio.id) return "#";
|
||||
const filter = new ListFilterModel(GQL.FilterMode.Movies, undefined);
|
||||
const criterion = new StudiosCriterion();
|
||||
|
@ -184,7 +184,7 @@ const makeStudioMoviesUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
|||
depth: 0,
|
||||
};
|
||||
filter.criteria.push(criterion);
|
||||
return `/movies?${filter.makeQueryParameters()}`;
|
||||
return `/groups?${filter.makeQueryParameters()}`;
|
||||
};
|
||||
|
||||
const makeStudioPerformersUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
||||
|
@ -211,12 +211,12 @@ const makeChildStudiosUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
|||
return `/studios?${filter.makeQueryParameters()}`;
|
||||
};
|
||||
|
||||
const makeMovieScenesUrl = (movie: Partial<GQL.MovieDataFragment>) => {
|
||||
if (!movie.id) return "#";
|
||||
const makeGroupScenesUrl = (group: Partial<GQL.MovieDataFragment>) => {
|
||||
if (!group.id) return "#";
|
||||
const filter = new ListFilterModel(GQL.FilterMode.Scenes, undefined);
|
||||
const criterion = new MoviesCriterion();
|
||||
criterion.value = [
|
||||
{ id: movie.id, label: movie.name || `Movie ${movie.id}` },
|
||||
{ id: group.id, label: group.name || `Group ${group.id}` },
|
||||
];
|
||||
filter.criteria.push(criterion);
|
||||
return `/scenes?${filter.makeQueryParameters()}`;
|
||||
|
@ -298,8 +298,8 @@ const makeTagImagesUrl = (tag: INamedObject) => {
|
|||
return `/images?${makeTagFilter(GQL.FilterMode.Images, tag)}`;
|
||||
};
|
||||
|
||||
const makeTagMoviesUrl = (tag: INamedObject) => {
|
||||
return `/movies?${makeTagFilter(GQL.FilterMode.Movies, tag)}`;
|
||||
const makeTagGroupsUrl = (tag: INamedObject) => {
|
||||
return `/groups?${makeTagFilter(GQL.FilterMode.Movies, tag)}`;
|
||||
};
|
||||
|
||||
type SceneMarkerDataFragment = Pick<GQL.SceneMarker, "id" | "seconds"> & {
|
||||
|
@ -349,13 +349,13 @@ const makeDirectorScenesUrl = (director: string) => {
|
|||
return `/scenes?${filter.makeQueryParameters()}`;
|
||||
};
|
||||
|
||||
const makeDirectorMoviesUrl = (director: string) => {
|
||||
const makeDirectorGroupsUrl = (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()}`;
|
||||
return `/groups?${filter.makeQueryParameters()}`;
|
||||
};
|
||||
|
||||
const makePhotographerGalleriesUrl = (photographer: string) => {
|
||||
|
@ -401,12 +401,12 @@ const NavUtils = {
|
|||
makePerformerScenesUrl,
|
||||
makePerformerImagesUrl,
|
||||
makePerformerGalleriesUrl,
|
||||
makePerformerMoviesUrl,
|
||||
makePerformerGroupsUrl,
|
||||
makePerformersCountryUrl,
|
||||
makeStudioScenesUrl,
|
||||
makeStudioImagesUrl,
|
||||
makeStudioGalleriesUrl,
|
||||
makeStudioMoviesUrl,
|
||||
makeStudioGroupsUrl: makeStudioGroupsUrl,
|
||||
makeStudioPerformersUrl,
|
||||
makeTagUrl,
|
||||
makeParentTagsUrl,
|
||||
|
@ -417,16 +417,16 @@ const NavUtils = {
|
|||
makeTagStudiosUrl,
|
||||
makeTagGalleriesUrl,
|
||||
makeTagImagesUrl,
|
||||
makeTagMoviesUrl,
|
||||
makeTagGroupsUrl,
|
||||
makeScenesPHashMatchUrl,
|
||||
makeSceneMarkerUrl,
|
||||
makeMovieScenesUrl,
|
||||
makeGroupScenesUrl,
|
||||
makeChildStudiosUrl,
|
||||
makeGalleryImagesUrl,
|
||||
makeDirectorScenesUrl,
|
||||
makePhotographerGalleriesUrl,
|
||||
makePhotographerImagesUrl,
|
||||
makeDirectorMoviesUrl,
|
||||
makeDirectorGroupsUrl,
|
||||
};
|
||||
|
||||
export default NavUtils;
|
||||
|
|
Loading…
Reference in New Issue