mirror of
https://github.com/stashapp/stash.git
synced 2025-12-16 20:07:05 +03:00
Add additional fields and restyle Movie select and Gallery select (#4851)
* Add new fields and restyle gallery selector * Add new fields and style movie selector
This commit is contained in:
@@ -358,7 +358,7 @@ func (qb *MovieStore) makeQuery(ctx context.Context, movieFilter *models.MovieFi
|
||||
distinctIDs(&query, movieTable)
|
||||
|
||||
if q := findFilter.Q; q != nil && *q != "" {
|
||||
searchColumns := []string{"movies.name"}
|
||||
searchColumns := []string{"movies.name", "movies.aliases"}
|
||||
query.parseQueryString(searchColumns, *q)
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,16 @@ fragment GalleryData on Gallery {
|
||||
fragment SelectGalleryData on Gallery {
|
||||
id
|
||||
title
|
||||
date
|
||||
code
|
||||
studio {
|
||||
name
|
||||
}
|
||||
cover {
|
||||
paths {
|
||||
thumbnail
|
||||
}
|
||||
}
|
||||
files {
|
||||
path
|
||||
}
|
||||
|
||||
@@ -8,5 +8,10 @@ fragment SlimMovieData on Movie {
|
||||
fragment SelectMovieData on Movie {
|
||||
id
|
||||
name
|
||||
aliases
|
||||
date
|
||||
studio {
|
||||
name
|
||||
}
|
||||
front_image_path
|
||||
}
|
||||
|
||||
@@ -33,10 +33,13 @@ import {
|
||||
CriterionValue,
|
||||
} from "src/models/list-filter/criteria/criterion";
|
||||
import { PathCriterion } from "src/models/list-filter/criteria/path";
|
||||
import { TruncatedText } from "../Shared/TruncatedText";
|
||||
|
||||
export type Gallery = Pick<GQL.Gallery, "id" | "title"> & {
|
||||
export type Gallery = Pick<GQL.Gallery, "id" | "title" | "date" | "code"> & {
|
||||
studio?: Pick<GQL.Studio, "name"> | null;
|
||||
files: Pick<GQL.GalleryFile, "path">[];
|
||||
folder?: Pick<GQL.Folder, "path"> | null;
|
||||
cover?: Pick<GQL.Image, "paths"> | null;
|
||||
};
|
||||
type Option = SelectOption<Gallery>;
|
||||
|
||||
@@ -111,10 +114,41 @@ const _GallerySelect: React.FC<
|
||||
thisOptionProps = {
|
||||
...optionProps,
|
||||
children: (
|
||||
<span>
|
||||
<span>{title}</span>
|
||||
<span className="gallery-select-option">
|
||||
<span className="gallery-select-row">
|
||||
{object.cover?.paths?.thumbnail && (
|
||||
<img
|
||||
className="gallery-select-image"
|
||||
src={object.cover.paths.thumbnail}
|
||||
loading="lazy"
|
||||
/>
|
||||
)}
|
||||
|
||||
<span className="gallery-select-details">
|
||||
<TruncatedText
|
||||
className="gallery-select-title"
|
||||
text={title}
|
||||
lineCount={1}
|
||||
/>
|
||||
|
||||
{object.studio?.name && (
|
||||
<span className="gallery-select-studio">
|
||||
{object.studio?.name}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{object.date && (
|
||||
<span className="gallery-select-date">{object.date}</span>
|
||||
)}
|
||||
|
||||
{object.code && (
|
||||
<span className="gallery-select-code">{object.code}</span>
|
||||
)}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
{matchedPath && (
|
||||
<span className="gallery-select-alias">{` (${matchedPath})`}</span>
|
||||
<span className="gallery-select-alias">{`(${matchedPath})`}</span>
|
||||
)}
|
||||
</span>
|
||||
),
|
||||
|
||||
@@ -349,8 +349,51 @@ $galleryTabWidth: 450px;
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
.gallery-select-alias {
|
||||
.gallery-select-option {
|
||||
.gallery-select-row {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
||||
.gallery-select-image {
|
||||
background-color: $body-bg;
|
||||
margin-right: 0.4em;
|
||||
max-height: 50px;
|
||||
max-width: 89px;
|
||||
object-fit: contain;
|
||||
object-position: center;
|
||||
}
|
||||
|
||||
.gallery-select-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
max-height: 4.1rem;
|
||||
overflow: hidden;
|
||||
|
||||
.gallery-select-title {
|
||||
flex-shrink: 0;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.gallery-select-date,
|
||||
.gallery-select-studio,
|
||||
.gallery-select-code {
|
||||
color: $text-muted;
|
||||
flex-shrink: 0;
|
||||
font-size: 0.9rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gallery-select-alias {
|
||||
font-size: 0.8rem;
|
||||
font-weight: bold;
|
||||
white-space: pre;
|
||||
width: 100%;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,14 @@ import { useCompare } from "src/hooks/state";
|
||||
import { Placement } from "react-bootstrap/esm/Overlay";
|
||||
import { sortByRelevance } from "src/utils/query";
|
||||
import { PatchComponent } from "src/patch";
|
||||
import { TruncatedText } from "../Shared/TruncatedText";
|
||||
|
||||
export type Movie = Pick<GQL.Movie, "id" | "name">;
|
||||
export type Movie = Pick<
|
||||
GQL.Movie,
|
||||
"id" | "name" | "date" | "front_image_path" | "aliases"
|
||||
> & {
|
||||
studio?: Pick<GQL.Studio, "name"> | null;
|
||||
};
|
||||
type Option = SelectOption<Movie>;
|
||||
|
||||
const _MovieSelect: React.FC<
|
||||
@@ -64,7 +70,12 @@ const _MovieSelect: React.FC<
|
||||
return !exclude.includes(movie.id.toString());
|
||||
});
|
||||
|
||||
return sortByRelevance(input, ret, (m) => m.name).map((movie) => ({
|
||||
return sortByRelevance(
|
||||
input,
|
||||
ret,
|
||||
(m) => m.name,
|
||||
(m) => (m.aliases ? [m.aliases] : [])
|
||||
).map((movie) => ({
|
||||
value: movie.id,
|
||||
object: movie,
|
||||
}));
|
||||
@@ -77,9 +88,53 @@ const _MovieSelect: React.FC<
|
||||
|
||||
const title = object.name;
|
||||
|
||||
// if name does not match the input value but an alias does, show the alias
|
||||
const { inputValue } = optionProps.selectProps;
|
||||
let alias: string | undefined = "";
|
||||
if (!title.toLowerCase().includes(inputValue.toLowerCase())) {
|
||||
alias = object.aliases || undefined;
|
||||
}
|
||||
|
||||
thisOptionProps = {
|
||||
...optionProps,
|
||||
children: <span>{title}</span>,
|
||||
children: (
|
||||
<span className="movie-select-option">
|
||||
<span className="movie-select-row">
|
||||
{object.front_image_path && (
|
||||
<img
|
||||
className="movie-select-image"
|
||||
src={object.front_image_path}
|
||||
loading="lazy"
|
||||
/>
|
||||
)}
|
||||
|
||||
<span className="movie-select-details">
|
||||
<TruncatedText
|
||||
className="movie-select-title"
|
||||
text={
|
||||
<span>
|
||||
{title}
|
||||
{alias && (
|
||||
<span className="movie-select-alias">{` (${alias})`}</span>
|
||||
)}
|
||||
</span>
|
||||
}
|
||||
lineCount={1}
|
||||
/>
|
||||
|
||||
{object.studio?.name && (
|
||||
<span className="movie-select-studio">
|
||||
{object.studio?.name}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{object.date && (
|
||||
<span className="movie-select-date">{object.date}</span>
|
||||
)}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
),
|
||||
};
|
||||
|
||||
return <reactSelectComponents.Option {...thisOptionProps} />;
|
||||
@@ -140,7 +195,10 @@ const _MovieSelect: React.FC<
|
||||
|
||||
if (
|
||||
options.some((o) => {
|
||||
return o.name.toLowerCase() === inputValue.toLowerCase();
|
||||
return (
|
||||
o.name.toLowerCase() === inputValue.toLowerCase() ||
|
||||
o.aliases?.toLowerCase() === inputValue.toLowerCase()
|
||||
);
|
||||
})
|
||||
) {
|
||||
return false;
|
||||
|
||||
@@ -43,3 +43,49 @@
|
||||
#movie-page .rating-number .text-input {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.movie-select-option {
|
||||
.movie-select-row {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
||||
.movie-select-image {
|
||||
background-color: $body-bg;
|
||||
margin-right: 0.4em;
|
||||
max-height: 50px;
|
||||
max-width: 89px;
|
||||
object-fit: contain;
|
||||
object-position: center;
|
||||
}
|
||||
|
||||
.movie-select-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
max-height: 4.1rem;
|
||||
overflow: hidden;
|
||||
|
||||
.movie-select-title {
|
||||
flex-shrink: 0;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
|
||||
.movie-select-alias {
|
||||
font-size: 0.8rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.movie-select-date,
|
||||
.movie-select-studio {
|
||||
color: $text-muted;
|
||||
flex-shrink: 0;
|
||||
font-size: 0.9rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,9 +36,7 @@ import { TruncatedText } from "../Shared/TruncatedText";
|
||||
|
||||
export type Scene = Pick<GQL.Scene, "id" | "title" | "date" | "code"> & {
|
||||
studio?: Pick<GQL.Studio, "name"> | null;
|
||||
} & {
|
||||
files?: Pick<GQL.VideoFile, "path">[];
|
||||
} & {
|
||||
paths?: Pick<GQL.ScenePathsType, "screenshot">;
|
||||
};
|
||||
|
||||
|
||||
@@ -215,7 +215,7 @@ export const ScrapedMoviesRow: React.FC<
|
||||
const value = resultValue ?? [];
|
||||
|
||||
const selectValue = value.map((p) => {
|
||||
const aliases: string[] = [];
|
||||
const aliases: string = "";
|
||||
return {
|
||||
id: p.stored_id ?? "",
|
||||
name: p.name ?? "",
|
||||
|
||||
Reference in New Issue
Block a user