mirror of https://github.com/stashapp/stash.git
Add support for favorite Studios (#4675)
* Backend changes * Add favorite icon to studio cards * Add favorite button to studio page * Add studio favorite filtering
This commit is contained in:
parent
e5929389b4
commit
8c454582c7
|
@ -325,6 +325,8 @@ input StudioFilterType {
|
|||
is_missing: String
|
||||
# rating expressed as 1-100
|
||||
rating100: IntCriterionInput
|
||||
"Filter by favorite"
|
||||
favorite: Boolean
|
||||
"Filter by scene count"
|
||||
scene_count: IntCriterionInput
|
||||
"Filter by image count"
|
||||
|
|
|
@ -16,6 +16,7 @@ type Studio {
|
|||
stash_ids: [StashID!]!
|
||||
# rating expressed as 1-100
|
||||
rating100: Int
|
||||
favorite: Boolean!
|
||||
details: String
|
||||
created_at: Time!
|
||||
updated_at: Time!
|
||||
|
@ -31,6 +32,7 @@ input StudioCreateInput {
|
|||
stash_ids: [StashIDInput!]
|
||||
# rating expressed as 1-100
|
||||
rating100: Int
|
||||
favorite: Boolean
|
||||
details: String
|
||||
aliases: [String!]
|
||||
ignore_auto_tag: Boolean
|
||||
|
@ -46,6 +48,7 @@ input StudioUpdateInput {
|
|||
stash_ids: [StashIDInput!]
|
||||
# rating expressed as 1-100
|
||||
rating100: Int
|
||||
favorite: Boolean
|
||||
details: String
|
||||
aliases: [String!]
|
||||
ignore_auto_tag: Boolean
|
||||
|
|
|
@ -35,6 +35,7 @@ func (r *mutationResolver) StudioCreate(ctx context.Context, input models.Studio
|
|||
newStudio.Name = input.Name
|
||||
newStudio.URL = translator.string(input.URL)
|
||||
newStudio.Rating = input.Rating100
|
||||
newStudio.Favorite = translator.bool(input.Favorite)
|
||||
newStudio.Details = translator.string(input.Details)
|
||||
newStudio.IgnoreAutoTag = translator.bool(input.IgnoreAutoTag)
|
||||
newStudio.Aliases = models.NewRelatedStrings(input.Aliases)
|
||||
|
@ -103,6 +104,7 @@ func (r *mutationResolver) StudioUpdate(ctx context.Context, input models.Studio
|
|||
updatedStudio.URL = translator.optionalString(input.URL, "url")
|
||||
updatedStudio.Details = translator.optionalString(input.Details, "details")
|
||||
updatedStudio.Rating = translator.optionalInt(input.Rating100, "rating100")
|
||||
updatedStudio.Favorite = translator.optionalBool(input.Favorite, "favorite")
|
||||
updatedStudio.IgnoreAutoTag = translator.optionalBool(input.IgnoreAutoTag, "ignore_auto_tag")
|
||||
updatedStudio.Aliases = translator.updateStrings(input.Aliases, "aliases")
|
||||
updatedStudio.StashIDs = translator.updateStashIDs(input.StashIds, "stash_ids")
|
||||
|
|
|
@ -18,6 +18,7 @@ type Studio struct {
|
|||
CreatedAt json.JSONTime `json:"created_at,omitempty"`
|
||||
UpdatedAt json.JSONTime `json:"updated_at,omitempty"`
|
||||
Rating int `json:"rating,omitempty"`
|
||||
Favorite bool `json:"favorite,omitempty"`
|
||||
Details string `json:"details,omitempty"`
|
||||
Aliases []string `json:"aliases,omitempty"`
|
||||
StashIDs []models.StashID `json:"stash_ids,omitempty"`
|
||||
|
|
|
@ -14,6 +14,7 @@ type Studio struct {
|
|||
UpdatedAt time.Time `json:"updated_at"`
|
||||
// Rating expressed in 1-100 scale
|
||||
Rating *int `json:"rating"`
|
||||
Favorite bool `json:"favorite"`
|
||||
Details string `json:"details"`
|
||||
IgnoreAutoTag bool `json:"ignore_auto_tag"`
|
||||
|
||||
|
@ -37,6 +38,7 @@ type StudioPartial struct {
|
|||
ParentID OptionalInt
|
||||
// Rating expressed in 1-100 scale
|
||||
Rating OptionalInt
|
||||
Favorite OptionalBool
|
||||
Details OptionalString
|
||||
CreatedAt OptionalTime
|
||||
UpdatedAt OptionalTime
|
||||
|
|
|
@ -16,6 +16,8 @@ type StudioFilterType struct {
|
|||
IsMissing *string `json:"is_missing"`
|
||||
// Filter by rating expressed as 1-100
|
||||
Rating100 *IntCriterionInput `json:"rating100"`
|
||||
// Filter by favorite
|
||||
Favorite *bool `json:"favorite"`
|
||||
// Filter by scene count
|
||||
SceneCount *IntCriterionInput `json:"scene_count"`
|
||||
// Filter by image count
|
||||
|
@ -44,6 +46,7 @@ type StudioCreateInput struct {
|
|||
Image *string `json:"image"`
|
||||
StashIds []StashID `json:"stash_ids"`
|
||||
Rating100 *int `json:"rating100"`
|
||||
Favorite *bool `json:"favorite"`
|
||||
Details *string `json:"details"`
|
||||
Aliases []string `json:"aliases"`
|
||||
IgnoreAutoTag *bool `json:"ignore_auto_tag"`
|
||||
|
@ -58,6 +61,7 @@ type StudioUpdateInput struct {
|
|||
Image *string `json:"image"`
|
||||
StashIds []StashID `json:"stash_ids"`
|
||||
Rating100 *int `json:"rating100"`
|
||||
Favorite *bool `json:"favorite"`
|
||||
Details *string `json:"details"`
|
||||
Aliases []string `json:"aliases"`
|
||||
IgnoreAutoTag *bool `json:"ignore_auto_tag"`
|
||||
|
|
|
@ -30,7 +30,7 @@ const (
|
|||
dbConnTimeout = 30
|
||||
)
|
||||
|
||||
var appSchemaVersion uint = 55
|
||||
var appSchemaVersion uint = 56
|
||||
|
||||
//go:embed migrations/*.sql
|
||||
var migrationsBox embed.FS
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE `studios` ADD COLUMN `favorite` boolean not null default '0';
|
|
@ -1610,6 +1610,11 @@ func createStudioFromModel(ctx context.Context, sqb *sqlite.StudioStore, studio
|
|||
return nil
|
||||
}
|
||||
|
||||
func getStudioBoolValue(index int) bool {
|
||||
index = index % 2
|
||||
return index == 1
|
||||
}
|
||||
|
||||
// createStudios creates n studios with plain Name and o studios with camel cased NaMe included
|
||||
func createStudios(ctx context.Context, n int, o int) error {
|
||||
sqb := db.Studio
|
||||
|
@ -1630,6 +1635,7 @@ func createStudios(ctx context.Context, n int, o int) error {
|
|||
studio := models.Studio{
|
||||
Name: name,
|
||||
URL: getStudioStringValue(index, urlField),
|
||||
Favorite: getStudioBoolValue(index),
|
||||
IgnoreAutoTag: getIgnoreAutoTag(i),
|
||||
}
|
||||
// only add aliases for some scenes
|
||||
|
|
|
@ -36,6 +36,7 @@ type studioRow struct {
|
|||
UpdatedAt Timestamp `db:"updated_at"`
|
||||
// expressed as 1-100
|
||||
Rating null.Int `db:"rating"`
|
||||
Favorite bool `db:"favorite"`
|
||||
Details zero.String `db:"details"`
|
||||
IgnoreAutoTag bool `db:"ignore_auto_tag"`
|
||||
|
||||
|
@ -51,6 +52,7 @@ func (r *studioRow) fromStudio(o models.Studio) {
|
|||
r.CreatedAt = Timestamp{Timestamp: o.CreatedAt}
|
||||
r.UpdatedAt = Timestamp{Timestamp: o.UpdatedAt}
|
||||
r.Rating = intFromPtr(o.Rating)
|
||||
r.Favorite = o.Favorite
|
||||
r.Details = zero.StringFrom(o.Details)
|
||||
r.IgnoreAutoTag = o.IgnoreAutoTag
|
||||
}
|
||||
|
@ -64,6 +66,7 @@ func (r *studioRow) resolve() *models.Studio {
|
|||
CreatedAt: r.CreatedAt.Timestamp,
|
||||
UpdatedAt: r.UpdatedAt.Timestamp,
|
||||
Rating: nullIntPtr(r.Rating),
|
||||
Favorite: r.Favorite,
|
||||
Details: r.Details.String,
|
||||
IgnoreAutoTag: r.IgnoreAutoTag,
|
||||
}
|
||||
|
@ -82,6 +85,7 @@ func (r *studioRowRecord) fromPartial(o models.StudioPartial) {
|
|||
r.setTimestamp("created_at", o.CreatedAt)
|
||||
r.setTimestamp("updated_at", o.UpdatedAt)
|
||||
r.setNullInt("rating", o.Rating)
|
||||
r.setBool("favorite", o.Favorite)
|
||||
r.setNullString("details", o.Details)
|
||||
r.setBool("ignore_auto_tag", o.IgnoreAutoTag)
|
||||
}
|
||||
|
@ -496,6 +500,7 @@ func (qb *StudioStore) makeFilter(ctx context.Context, studioFilter *models.Stud
|
|||
query.handleCriterion(ctx, stringCriterionHandler(studioFilter.Details, studioTable+".details"))
|
||||
query.handleCriterion(ctx, stringCriterionHandler(studioFilter.URL, studioTable+".url"))
|
||||
query.handleCriterion(ctx, intCriterionHandler(studioFilter.Rating100, studioTable+".rating", nil))
|
||||
query.handleCriterion(ctx, boolCriterionHandler(studioFilter.Favorite, studioTable+".favorite", nil))
|
||||
query.handleCriterion(ctx, boolCriterionHandler(studioFilter.IgnoreAutoTag, studioTable+".ignore_auto_tag", nil))
|
||||
|
||||
query.handleCriterion(ctx, criterionHandlerFunc(func(ctx context.Context, f *filterBuilder) {
|
||||
|
|
|
@ -24,6 +24,7 @@ func ToJSON(ctx context.Context, reader FinderImageStashIDGetter, studio *models
|
|||
Name: studio.Name,
|
||||
URL: studio.URL,
|
||||
Details: studio.Details,
|
||||
Favorite: studio.Favorite,
|
||||
IgnoreAutoTag: studio.IgnoreAutoTag,
|
||||
CreatedAt: json.JSONTime{Time: studio.CreatedAt},
|
||||
UpdatedAt: json.JSONTime{Time: studio.UpdatedAt},
|
||||
|
|
|
@ -62,6 +62,7 @@ func createFullStudio(id int, parentID int) models.Studio {
|
|||
Name: studioName,
|
||||
URL: url,
|
||||
Details: details,
|
||||
Favorite: true,
|
||||
CreatedAt: createTime,
|
||||
UpdatedAt: updateTime,
|
||||
Rating: &rating,
|
||||
|
@ -89,9 +90,10 @@ func createEmptyStudio(id int) models.Studio {
|
|||
|
||||
func createFullJSONStudio(parentStudio, image string, aliases []string) *jsonschema.Studio {
|
||||
return &jsonschema.Studio{
|
||||
Name: studioName,
|
||||
URL: url,
|
||||
Details: details,
|
||||
Name: studioName,
|
||||
URL: url,
|
||||
Details: details,
|
||||
Favorite: true,
|
||||
CreatedAt: json.JSONTime{
|
||||
Time: createTime,
|
||||
},
|
||||
|
|
|
@ -144,6 +144,7 @@ func studioJSONtoStudio(studioJSON jsonschema.Studio) models.Studio {
|
|||
URL: studioJSON.URL,
|
||||
Aliases: models.NewRelatedStrings(studioJSON.Aliases),
|
||||
Details: studioJSON.Details,
|
||||
Favorite: studioJSON.Favorite,
|
||||
IgnoreAutoTag: studioJSON.IgnoreAutoTag,
|
||||
CreatedAt: studioJSON.CreatedAt.GetTime(),
|
||||
UpdatedAt: studioJSON.UpdatedAt.GetTime(),
|
||||
|
|
|
@ -31,6 +31,7 @@ fragment StudioData on Studio {
|
|||
}
|
||||
details
|
||||
rating100
|
||||
favorite
|
||||
aliases
|
||||
}
|
||||
|
||||
|
|
|
@ -17,12 +17,12 @@ import {
|
|||
} from "src/models/list-filter/criteria/criterion";
|
||||
import { PopoverCountButton } from "../Shared/PopoverCountButton";
|
||||
import GenderIcon from "./GenderIcon";
|
||||
import { faHeart, faTag } from "@fortawesome/free-solid-svg-icons";
|
||||
import { faTag } from "@fortawesome/free-solid-svg-icons";
|
||||
import { RatingBanner } from "../Shared/RatingBanner";
|
||||
import cx from "classnames";
|
||||
import { usePerformerUpdate } from "src/core/StashService";
|
||||
import { ILabeledId } from "src/models/list-filter/types";
|
||||
import ScreenUtils from "src/utils/screen";
|
||||
import { FavoriteIcon } from "../Shared/FavoriteIcon";
|
||||
|
||||
export interface IPerformerCardExtraCriteria {
|
||||
scenes?: Criterion<CriterionValue>[];
|
||||
|
@ -82,24 +82,6 @@ export const PerformerCard: React.FC<IPerformerCardProps> = ({
|
|||
setCardWidth(fittedCardWidth);
|
||||
}, [containerWidth]);
|
||||
|
||||
function renderFavoriteIcon() {
|
||||
return (
|
||||
<Link to="" onClick={(e) => e.preventDefault()}>
|
||||
<Button
|
||||
className={cx(
|
||||
"minimal",
|
||||
"mousetrap",
|
||||
"favorite-button",
|
||||
performer.favorite ? "favorite" : "not-favorite"
|
||||
)}
|
||||
onClick={() => onToggleFavorite!(!performer.favorite)}
|
||||
>
|
||||
<Icon icon={faHeart} size="2x" />
|
||||
</Button>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
function onToggleFavorite(v: boolean) {
|
||||
if (performer.id) {
|
||||
updatePerformer({
|
||||
|
@ -292,7 +274,10 @@ export const PerformerCard: React.FC<IPerformerCardProps> = ({
|
|||
}
|
||||
overlays={
|
||||
<>
|
||||
{renderFavoriteIcon()}
|
||||
<FavoriteIcon
|
||||
favorite={performer.favorite}
|
||||
onToggleFavorite={onToggleFavorite}
|
||||
/>
|
||||
{maybeRenderRatingBanner()}
|
||||
{maybeRenderFlag()}
|
||||
</>
|
||||
|
|
|
@ -80,36 +80,15 @@
|
|||
}
|
||||
|
||||
button.btn.favorite-button {
|
||||
opacity: 1;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 10px;
|
||||
transition: opacity 0.5s;
|
||||
|
||||
svg.fa-icon {
|
||||
margin-left: 0.4rem;
|
||||
margin-right: 0.4rem;
|
||||
}
|
||||
|
||||
&.not-favorite {
|
||||
color: rgba(191, 204, 214, 0.5);
|
||||
filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.9));
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&.favorite {
|
||||
color: #ff7373;
|
||||
filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.9));
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus,
|
||||
&:active:focus {
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover button.btn.favorite-button.not-favorite {
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import React from "react";
|
||||
import { Icon } from "../Shared/Icon";
|
||||
import { Button } from "react-bootstrap";
|
||||
import { faHeart } from "@fortawesome/free-solid-svg-icons";
|
||||
import cx from "classnames";
|
||||
|
||||
export const FavoriteIcon: React.FC<{
|
||||
favorite: boolean;
|
||||
onToggleFavorite: (v: boolean) => void;
|
||||
}> = ({ favorite, onToggleFavorite }) => {
|
||||
return (
|
||||
<Button
|
||||
className={cx(
|
||||
"minimal",
|
||||
"mousetrap",
|
||||
"favorite-button",
|
||||
favorite ? "favorite" : "not-favorite"
|
||||
)}
|
||||
onClick={() => onToggleFavorite!(!favorite)}
|
||||
>
|
||||
<Icon icon={faHeart} size="2x" />
|
||||
</Button>
|
||||
);
|
||||
};
|
|
@ -528,3 +528,27 @@ div.react-datepicker {
|
|||
align-items: baseline;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
button.btn.favorite-button {
|
||||
opacity: 1;
|
||||
transition: opacity 0.5s;
|
||||
|
||||
&.not-favorite {
|
||||
color: rgba(191, 204, 214, 0.5);
|
||||
filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.9));
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&.favorite {
|
||||
color: #ff7373;
|
||||
filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.9));
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus,
|
||||
&:active:focus {
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ import { FormattedMessage } from "react-intl";
|
|||
import { PopoverCountButton } from "../Shared/PopoverCountButton";
|
||||
import { RatingBanner } from "../Shared/RatingBanner";
|
||||
import ScreenUtils from "src/utils/screen";
|
||||
import { FavoriteIcon } from "../Shared/FavoriteIcon";
|
||||
import { useStudioUpdate } from "src/core/StashService";
|
||||
|
||||
interface IProps {
|
||||
studio: GQL.StudioDataFragment;
|
||||
|
@ -70,6 +72,7 @@ export const StudioCard: React.FC<IProps> = ({
|
|||
selected,
|
||||
onSelectedChanged,
|
||||
}) => {
|
||||
const [updateStudio] = useStudioUpdate();
|
||||
const [cardWidth, setCardWidth] = useState<number>();
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -83,6 +86,19 @@ export const StudioCard: React.FC<IProps> = ({
|
|||
setCardWidth(fittedCardWidth);
|
||||
}, [containerWidth]);
|
||||
|
||||
function onToggleFavorite(v: boolean) {
|
||||
if (studio.id) {
|
||||
updateStudio({
|
||||
variables: {
|
||||
input: {
|
||||
id: studio.id,
|
||||
favorite: v,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function maybeRenderScenesPopoverButton() {
|
||||
if (!studio.scene_count) return;
|
||||
|
||||
|
@ -193,6 +209,12 @@ export const StudioCard: React.FC<IProps> = ({
|
|||
<RatingBanner rating={studio.rating100} />
|
||||
</div>
|
||||
}
|
||||
overlays={
|
||||
<FavoriteIcon
|
||||
favorite={studio.favorite}
|
||||
onToggleFavorite={(v) => onToggleFavorite(v)}
|
||||
/>
|
||||
}
|
||||
popovers={maybeRenderPopoverButtonGroup()}
|
||||
selected={selected}
|
||||
selecting={selecting}
|
||||
|
|
|
@ -37,6 +37,7 @@ import {
|
|||
faLink,
|
||||
faChevronDown,
|
||||
faChevronUp,
|
||||
faHeart,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import TextUtils from "src/utils/text";
|
||||
import { RatingSystem } from "src/components/Shared/Rating/RatingSystem";
|
||||
|
@ -154,6 +155,19 @@ const StudioPage: React.FC<IProps> = ({ studio, tabKey }) => {
|
|||
}
|
||||
}, [setTabKey, populatedDefaultTab, tabKey]);
|
||||
|
||||
function setFavorite(v: boolean) {
|
||||
if (studio.id) {
|
||||
updateStudio({
|
||||
variables: {
|
||||
input: {
|
||||
id: studio.id,
|
||||
favorite: v,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// set up hotkeys
|
||||
useEffect(() => {
|
||||
Mousetrap.bind("e", () => toggleEditing());
|
||||
|
@ -161,6 +175,7 @@ const StudioPage: React.FC<IProps> = ({ studio, tabKey }) => {
|
|||
setIsDeleteAlertOpen(true);
|
||||
});
|
||||
Mousetrap.bind(",", () => setCollapsed(!collapsed));
|
||||
Mousetrap.bind("f", () => setFavorite(!studio.favorite));
|
||||
|
||||
return () => {
|
||||
Mousetrap.unbind("e");
|
||||
|
@ -284,6 +299,12 @@ const StudioPage: React.FC<IProps> = ({ studio, tabKey }) => {
|
|||
|
||||
const renderClickableIcons = () => (
|
||||
<span className="name-icons">
|
||||
<Button
|
||||
className={cx("minimal", studio.favorite ? "favorite" : "not-favorite")}
|
||||
onClick={() => setFavorite(!studio.favorite)}
|
||||
>
|
||||
<Icon icon={faHeart} />
|
||||
</Button>
|
||||
{studio.url && (
|
||||
<Button
|
||||
as={ExternalLink}
|
||||
|
|
|
@ -5,3 +5,35 @@
|
|||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.studio-card {
|
||||
button.btn.favorite-button {
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 10px;
|
||||
|
||||
svg.fa-icon {
|
||||
margin-left: 0.4rem;
|
||||
margin-right: 0.4rem;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover button.btn.favorite-button.not-favorite {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
#studio-page {
|
||||
.studio-head {
|
||||
.name-icons {
|
||||
.not-favorite {
|
||||
color: rgba(191, 204, 214, 0.5);
|
||||
}
|
||||
|
||||
.favorite {
|
||||
color: #ff7373;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,26 @@
|
|||
import { BooleanCriterion, BooleanCriterionOption } from "./criterion";
|
||||
|
||||
export const FavoriteCriterionOption = new BooleanCriterionOption(
|
||||
export const FavoritePerformerCriterionOption = new BooleanCriterionOption(
|
||||
"favourite",
|
||||
"filter_favorites",
|
||||
() => new FavoriteCriterion()
|
||||
() => new FavoritePerformerCriterion()
|
||||
);
|
||||
|
||||
export class FavoriteCriterion extends BooleanCriterion {
|
||||
export class FavoritePerformerCriterion extends BooleanCriterion {
|
||||
constructor() {
|
||||
super(FavoriteCriterionOption);
|
||||
super(FavoritePerformerCriterionOption);
|
||||
}
|
||||
}
|
||||
|
||||
export const FavoriteStudioCriterionOption = new BooleanCriterionOption(
|
||||
"favourite",
|
||||
"favorite",
|
||||
() => new FavoriteStudioCriterion()
|
||||
);
|
||||
|
||||
export class FavoriteStudioCriterion extends BooleanCriterion {
|
||||
constructor() {
|
||||
super(FavoriteStudioCriterionOption);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
createDateCriterionOption,
|
||||
createMandatoryTimestampCriterionOption,
|
||||
} from "./criteria/criterion";
|
||||
import { FavoriteCriterionOption } from "./criteria/favorite";
|
||||
import { FavoritePerformerCriterionOption } from "./criteria/favorite";
|
||||
import { GenderCriterionOption } from "./criteria/gender";
|
||||
import { CircumcisedCriterionOption } from "./criteria/circumcised";
|
||||
import { PerformerIsMissingCriterionOption } from "./criteria/is-missing";
|
||||
|
@ -81,7 +81,7 @@ const stringCriteria: CriterionType[] = [
|
|||
];
|
||||
|
||||
const criterionOptions = [
|
||||
FavoriteCriterionOption,
|
||||
FavoritePerformerCriterionOption,
|
||||
GenderCriterionOption,
|
||||
CircumcisedCriterionOption,
|
||||
PerformerIsMissingCriterionOption,
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
createStringCriterionOption,
|
||||
createMandatoryTimestampCriterionOption,
|
||||
} from "./criteria/criterion";
|
||||
import { FavoriteStudioCriterionOption } from "./criteria/favorite";
|
||||
import { StudioIsMissingCriterionOption } from "./criteria/is-missing";
|
||||
import { RatingCriterionOption } from "./criteria/rating";
|
||||
import { StashIDCriterionOption } from "./criteria/stash-ids";
|
||||
|
@ -36,6 +37,7 @@ const sortByOptions = ["name", "random", "rating"]
|
|||
|
||||
const displayModeOptions = [DisplayMode.Grid, DisplayMode.Tagger];
|
||||
const criterionOptions = [
|
||||
FavoriteStudioCriterionOption,
|
||||
createMandatoryStringCriterionOption("name"),
|
||||
createStringCriterionOption("details"),
|
||||
ParentStudiosCriterionOption,
|
||||
|
|
|
@ -135,6 +135,7 @@ export type CriterionType =
|
|||
| "audio_codec"
|
||||
| "duration"
|
||||
| "filter_favorites"
|
||||
| "favorite"
|
||||
| "has_markers"
|
||||
| "is_missing"
|
||||
| "tags"
|
||||
|
|
Loading…
Reference in New Issue