Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Sort Name to Tags #5531

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions graphql/schema/types/filters.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,9 @@ input TagFilterType {
"Filter by tag name"
name: StringCriterionInput

"Filter by tag sort_name"
sort_name: StringCriterionInput

"Filter by tag aliases"
aliases: StringCriterionInput

Expand Down
6 changes: 6 additions & 0 deletions graphql/schema/types/tag.graphql
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
type Tag {
id: ID!
name: String!
"Value that does not appear in the UI but overrides name for sorting"
sort_name: String
description: String
aliases: [String!]!
ignore_auto_tag: Boolean!
Expand All @@ -25,6 +27,8 @@ type Tag {

input TagCreateInput {
name: String!
"Value that does not appear in the UI but overrides name for sorting"
sort_name: String
description: String
aliases: [String!]
ignore_auto_tag: Boolean
Expand All @@ -39,6 +43,8 @@ input TagCreateInput {
input TagUpdateInput {
id: ID!
name: String
"Value that does not appear in the UI but overrides name for sorting"
sort_name: String
description: String
aliases: [String!]
ignore_auto_tag: Boolean
Expand Down
2 changes: 2 additions & 0 deletions internal/api/resolver_mutation_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func (r *mutationResolver) TagCreate(ctx context.Context, input TagCreateInput)
newTag := models.NewTag()

newTag.Name = input.Name
newTag.SortName = translator.string(input.SortName)
newTag.Aliases = models.NewRelatedStrings(input.Aliases)
newTag.Favorite = translator.bool(input.Favorite)
newTag.Description = translator.string(input.Description)
Expand Down Expand Up @@ -102,6 +103,7 @@ func (r *mutationResolver) TagUpdate(ctx context.Context, input TagUpdateInput)
updatedTag := models.NewTagPartial()

updatedTag.Name = translator.optionalString(input.Name, "name")
updatedTag.SortName = translator.optionalString(input.SortName, "sort_name")
updatedTag.Favorite = translator.optionalBool(input.Favorite, "favorite")
updatedTag.IgnoreAutoTag = translator.optionalBool(input.IgnoreAutoTag, "ignore_auto_tag")
updatedTag.Description = translator.optionalString(input.Description, "description")
Expand Down
2 changes: 2 additions & 0 deletions pkg/models/model_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
type Tag struct {
ID int `json:"id"`
Name string `json:"name"`
SortName string `json:"sort_name"`
Favorite bool `json:"favorite"`
Description string `json:"description"`
IgnoreAutoTag bool `json:"ignore_auto_tag"`
Expand Down Expand Up @@ -47,6 +48,7 @@ func (s *Tag) LoadChildIDs(ctx context.Context, l TagRelationLoader) error {

type TagPartial struct {
Name OptionalString
SortName OptionalString
Description OptionalString
Favorite OptionalBool
IgnoreAutoTag OptionalBool
Expand Down
2 changes: 2 additions & 0 deletions pkg/models/tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ type TagFilterType struct {
OperatorFilter[TagFilterType]
// Filter by tag name
Name *StringCriterionInput `json:"name"`
// Filter by tag sort_name
SortName *StringCriterionInput `json:"sort_name"`
// Filter by tag aliases
Aliases *StringCriterionInput `json:"aliases"`
// Filter by tag favorites
Expand Down
2 changes: 1 addition & 1 deletion pkg/sqlite/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const (
cacheSizeEnv = "STASH_SQLITE_CACHE_SIZE"
)

var appSchemaVersion uint = 71
var appSchemaVersion uint = 72

//go:embed migrations/*.sql
var migrationsBox embed.FS
Expand Down
2 changes: 1 addition & 1 deletion pkg/sqlite/gallery.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ var (
},
fkColumn: "tag_id",
foreignTable: tagTable,
orderBy: "tags.name ASC",
orderBy: "COALESCE(tags.sort_name, tags.name) ASC",
},
images: joinRepository{
repository: repository{
Expand Down
2 changes: 1 addition & 1 deletion pkg/sqlite/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ var (
},
fkColumn: tagIDColumn,
foreignTable: tagTable,
orderBy: "tags.name ASC",
orderBy: "COALESCE(tags.sort_name, tags.name) ASC",
},
}
)
Expand Down
2 changes: 1 addition & 1 deletion pkg/sqlite/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ var (
},
fkColumn: tagIDColumn,
foreignTable: tagTable,
orderBy: "tags.name ASC",
orderBy: "COALESCE(tags.sort_name, tags.name) ASC",
},
}
)
Expand Down
2 changes: 2 additions & 0 deletions pkg/sqlite/migrations/72_tag_sort_name.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE `tags` ADD COLUMN `sort_name` varchar(255);

2 changes: 1 addition & 1 deletion pkg/sqlite/performer.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ var (
},
fkColumn: tagIDColumn,
foreignTable: tagTable,
orderBy: "tags.name ASC",
orderBy: "COALESCE(tags.sort_name, tags.name) ASC",
},
stashIDs: stashIDRepository{
repository{
Expand Down
2 changes: 1 addition & 1 deletion pkg/sqlite/scene.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ var (
},
fkColumn: tagIDColumn,
foreignTable: tagTable,
orderBy: "tags.name ASC",
orderBy: "COALESCE(tags.sort_name, tags.name) ASC",
},
performers: joinRepository{
repository: repository{
Expand Down
2 changes: 1 addition & 1 deletion pkg/sqlite/studio.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ var (
},
fkColumn: tagIDColumn,
foreignTable: tagTable,
orderBy: "tags.name ASC",
orderBy: "COALESCE(tags.sort_name, tags.name) ASC",
},
}
)
Expand Down
10 changes: 8 additions & 2 deletions pkg/sqlite/tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const (
type tagRow struct {
ID int `db:"id" goqu:"skipinsert"`
Name null.String `db:"name"` // TODO: make schema non-nullable
SortName zero.String `db:"sort_name"`
Favorite bool `db:"favorite"`
Description zero.String `db:"description"`
IgnoreAutoTag bool `db:"ignore_auto_tag"`
Expand All @@ -46,6 +47,7 @@ type tagRow struct {
func (r *tagRow) fromTag(o models.Tag) {
r.ID = o.ID
r.Name = null.StringFrom(o.Name)
r.SortName = zero.StringFrom((o.SortName))
r.Favorite = o.Favorite
r.Description = zero.StringFrom(o.Description)
r.IgnoreAutoTag = o.IgnoreAutoTag
Expand All @@ -57,6 +59,7 @@ func (r *tagRow) resolve() *models.Tag {
ret := &models.Tag{
ID: r.ID,
Name: r.Name.String,
SortName: r.SortName.String,
Favorite: r.Favorite,
Description: r.Description.String,
IgnoreAutoTag: r.IgnoreAutoTag,
Expand Down Expand Up @@ -87,6 +90,7 @@ type tagRowRecord struct {

func (r *tagRowRecord) fromPartial(o models.TagPartial) {
r.setString("name", o.Name)
r.setNullString("sort_name", o.SortName)
r.setNullString("description", o.Description)
r.setBool("favorite", o.Favorite)
r.setBool("ignore_auto_tag", o.IgnoreAutoTag)
Expand Down Expand Up @@ -672,6 +676,8 @@ func (qb *TagStore) getTagSort(query *queryBuilder, findFilter *models.FindFilte

sortQuery := ""
switch sort {
case "name":
sortQuery += fmt.Sprintf(" ORDER BY COALESCE(tags.sort_name, tags.name) COLLATE NATURAL_CI %s", getSortDirection(direction))
case "scenes_count":
sortQuery += getCountSort(tagTable, scenesTagsTable, tagIDColumn, direction)
case "scene_markers_count":
Expand All @@ -690,8 +696,8 @@ func (qb *TagStore) getTagSort(query *queryBuilder, findFilter *models.FindFilte
sortQuery += getSort(sort, direction, "tags")
}

// Whatever the sorting, always use name/id as a final sort
sortQuery += ", COALESCE(tags.name, tags.id) COLLATE NATURAL_CI ASC"
// Whatever the sorting, always use sort_name/name/id as a final sort
sortQuery += ", COALESCE(tags.sort_name, tags.name, tags.id) COLLATE NATURAL_CI ASC"
return sortQuery, nil
}

Expand Down
1 change: 1 addition & 0 deletions pkg/sqlite/tag_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ func (qb *tagFilterHandler) criterionHandler() criterionHandler {
tagFilter := qb.tagFilter
return compoundHandler{
stringCriterionHandler(tagFilter.Name, tagTable+".name"),
stringCriterionHandler(tagFilter.SortName, tagTable+".sort_name"),
qb.aliasCriterionHandler(tagFilter.Aliases),

boolCriterionHandler(tagFilter.Favorite, tagTable+".favorite", nil),
Expand Down
1 change: 1 addition & 0 deletions ui/v2.5/graphql/data/tag-slim.graphql
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
fragment SlimTagData on Tag {
id
name
sort_name
aliases
image_path
parent_count
Expand Down
3 changes: 3 additions & 0 deletions ui/v2.5/graphql/data/tag.graphql
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
fragment TagData on Tag {
id
name
sort_name
description
aliases
ignore_auto_tag
Expand Down Expand Up @@ -33,6 +34,7 @@ fragment TagData on Tag {
fragment SelectTagData on Tag {
id
name
sort_name
favorite
description
aliases
Expand All @@ -41,5 +43,6 @@ fragment SelectTagData on Tag {
parents {
id
name
sort_name
}
}
32 changes: 30 additions & 2 deletions ui/v2.5/src/components/Shared/TagLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,30 @@ type SceneMarkerFragment = Pick<GQL.SceneMarker, "id" | "title" | "seconds"> & {
primary_tag: Pick<GQL.Tag, "id" | "name">;
};

interface ISortNameLinkProps {
link: string;
className?: string;
sortName?: string;
}

const SortNameLinkComponent: React.FC<ISortNameLinkProps> = ({
link,
sortName,
className,
children,
}) => {
return (
<Badge
data-name={className}
data-sort-name={sortName}
className={cx("tag-item", className)}
variant="secondary"
>
<Link to={link}>{children}</Link>
</Badge>
);
};

interface ICommonLinkProps {
link: string;
className?: string;
Expand Down Expand Up @@ -263,7 +287,11 @@ export const TagLink: React.FC<ITagLinkProps> = ({
}, [hierarchyTooltipID]);

return (
<CommonLinkComponent link={link} className={className}>
<SortNameLinkComponent
sortName={tag.sort_name || title}
link={link}
className={className}
>
<TagPopover id={tag.id ?? ""} placement={hoverPlacement}>
{title}
{showHierarchyIcon && (
Expand All @@ -275,6 +303,6 @@ export const TagLink: React.FC<ITagLinkProps> = ({
</OverlayTrigger>
)}
</TagPopover>
</CommonLinkComponent>
</SortNameLinkComponent>
);
};
3 changes: 3 additions & 0 deletions ui/v2.5/src/components/Tags/TagDetails/TagEditPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const TagEditPanel: React.FC<ITagEditPanel> = ({

const schema = yup.object({
name: yup.string().required(),
sort_name: yup.string().ensure(),
aliases: yupUniqueAliases(intl, "name"),
description: yup.string().ensure(),
parent_ids: yup.array(yup.string().required()).defined(),
Expand All @@ -56,6 +57,7 @@ export const TagEditPanel: React.FC<ITagEditPanel> = ({

const initialValues = {
name: tag?.name ?? "",
sort_name: tag?.sort_name ?? "",
aliases: tag?.aliases ?? [],
description: tag?.description ?? "",
parent_ids: (tag?.parents ?? []).map((t) => t.id),
Expand Down Expand Up @@ -203,6 +205,7 @@ export const TagEditPanel: React.FC<ITagEditPanel> = ({

<Form noValidate onSubmit={formik.handleSubmit} id="tag-edit">
{renderInputField("name")}
{renderInputField("sort_name", "text")}
{renderStringListField("aliases")}
{renderInputField("description", "textarea")}
{renderParentTagsField()}
Expand Down
1 change: 1 addition & 0 deletions ui/v2.5/src/locales/en-GB.json
Original file line number Diff line number Diff line change
Expand Up @@ -1158,6 +1158,7 @@
"megabits_per_second": "{value} mbps",
"metadata": "Metadata",
"name": "Name",
"sort_name": "Sorting Name",
"new": "New",
"none": "None",
"o_count": "O Count",
Expand Down
1 change: 1 addition & 0 deletions ui/v2.5/src/models/list-filter/tags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ const displayModeOptions = [DisplayMode.Grid, DisplayMode.List];
const criterionOptions = [
FavoriteTagCriterionOption,
createMandatoryStringCriterionOption("name"),
createStringCriterionOption("sort_name"),
TagIsMissingCriterionOption,
createStringCriterionOption("aliases"),
createStringCriterionOption("description"),
Expand Down
3 changes: 2 additions & 1 deletion ui/v2.5/src/models/list-filter/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,4 +219,5 @@ export type CriterionType =
| "code"
| "photographer"
| "disambiguation"
| "has_chapters";
| "has_chapters"
| "sort_name";
1 change: 1 addition & 0 deletions ui/v2.5/src/utils/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ const makePerformerImagesUrl = (
export interface INamedObject {
id: string;
name?: string;
sort_name?: string | null;
}

const makePerformerGalleriesUrl = (
Expand Down
Loading