mirror of
https://github.com/stashapp/stash.git
synced 2026-05-09 05:05:29 +02:00
Merge f5651acd87 into 01a7583364
This commit is contained in:
commit
4b8b01d657
8 changed files with 119 additions and 34 deletions
|
|
@ -500,6 +500,7 @@ var groupSortOptions = sortOptions{
|
|||
"rating",
|
||||
"scenes_count",
|
||||
"o_counter",
|
||||
"sub_group_description",
|
||||
"sub_group_order",
|
||||
"tag_count",
|
||||
"updated_at",
|
||||
|
|
@ -532,6 +533,14 @@ func (qb *GroupStore) setGroupSort(query *queryBuilder, findFilter *models.FindF
|
|||
query.joinSort(groupRelationsTable, "", "groups.id = groups_relations.sub_id")
|
||||
query.sortAndPagination += getSort("order_index", direction, groupRelationsTable)
|
||||
}
|
||||
case "sub_group_description":
|
||||
// as above, we need to handle parent groups differently here
|
||||
if query.hasJoin("groups_parents") {
|
||||
query.sortAndPagination += getSort("description", direction, "groups_parents")
|
||||
} else {
|
||||
query.joinSort(groupRelationsTable, "", "groups.id = groups_relations.sub_id")
|
||||
query.sortAndPagination += getSort("description", direction, groupRelationsTable)
|
||||
}
|
||||
case "tag_count":
|
||||
query.sortAndPagination += getCountSort(groupTable, groupsTagsTable, groupIDColumn, direction)
|
||||
case "scenes_count": // generic getSort won't work for this
|
||||
|
|
|
|||
|
|
@ -1124,6 +1124,90 @@ func TestGroupQuerySortOrderIndex(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestGroupQuerySortSubGroupDescription(t *testing.T) {
|
||||
runWithRollbackTxn(t, "sort subgroup description", func(t *testing.T, ctx context.Context) {
|
||||
assert := assert.New(t)
|
||||
|
||||
cEmpty := models.Group{Name: "sort-desc-child-empty"}
|
||||
c01 := models.Group{Name: "sort-desc-child-01"}
|
||||
c2 := models.Group{Name: "sort-desc-child-2"}
|
||||
c10 := models.Group{Name: "sort-desc-child-10"}
|
||||
assert.NoError(db.Group.Create(ctx, &cEmpty))
|
||||
assert.NoError(db.Group.Create(ctx, &c01))
|
||||
assert.NoError(db.Group.Create(ctx, &c2))
|
||||
assert.NoError(db.Group.Create(ctx, &c10))
|
||||
|
||||
parent := models.Group{
|
||||
Name: "sort-desc-parent",
|
||||
SubGroups: models.NewRelatedGroupDescriptions([]models.GroupIDDescription{
|
||||
{GroupID: cEmpty.ID, Description: ""},
|
||||
{GroupID: c10.ID, Description: "10"},
|
||||
{GroupID: c2.ID, Description: "2"},
|
||||
{GroupID: c01.ID, Description: "01"},
|
||||
}),
|
||||
}
|
||||
assert.NoError(db.Group.Create(ctx, &parent))
|
||||
|
||||
sortKey := "sub_group_description"
|
||||
dirAsc := models.SortDirectionEnumAsc
|
||||
findFilter := models.FindFilterType{
|
||||
Sort: &sortKey,
|
||||
Direction: &dirAsc,
|
||||
}
|
||||
groupFilter := models.GroupFilterType{
|
||||
ContainingGroups: &models.HierarchicalMultiCriterionInput{
|
||||
Value: []string{strconv.Itoa(parent.ID)},
|
||||
Modifier: models.CriterionModifierIncludes,
|
||||
},
|
||||
}
|
||||
|
||||
groups, _, err := db.Group.Query(ctx, &groupFilter, &findFilter)
|
||||
assert.NoError(err)
|
||||
assert.Len(groups, 4)
|
||||
assert.Equal(cEmpty.ID, groups[0].ID)
|
||||
assert.Equal(c01.ID, groups[1].ID)
|
||||
assert.Equal(c2.ID, groups[2].ID)
|
||||
assert.Equal(c10.ID, groups[3].ID)
|
||||
|
||||
dirDesc := models.SortDirectionEnumDesc
|
||||
findFilter.Direction = &dirDesc
|
||||
groups, _, err = db.Group.Query(ctx, &groupFilter, &findFilter)
|
||||
assert.NoError(err)
|
||||
assert.Len(groups, 4)
|
||||
assert.Equal(c10.ID, groups[0].ID)
|
||||
assert.Equal(c2.ID, groups[1].ID)
|
||||
assert.Equal(c01.ID, groups[2].ID)
|
||||
assert.Equal(cEmpty.ID, groups[3].ID)
|
||||
|
||||
// Exercise the non-groups_parents code path by filtering on name only.
|
||||
nameCriterion := models.StringCriterionInput{
|
||||
Value: "sort-desc-child-",
|
||||
Modifier: models.CriterionModifierIncludes,
|
||||
}
|
||||
nameFilter := models.GroupFilterType{
|
||||
Name: &nameCriterion,
|
||||
}
|
||||
|
||||
findFilter.Direction = &dirAsc
|
||||
groups, _, err = db.Group.Query(ctx, &nameFilter, &findFilter)
|
||||
assert.NoError(err)
|
||||
assert.Len(groups, 4)
|
||||
assert.Equal(cEmpty.ID, groups[0].ID)
|
||||
assert.Equal(c01.ID, groups[1].ID)
|
||||
assert.Equal(c2.ID, groups[2].ID)
|
||||
assert.Equal(c10.ID, groups[3].ID)
|
||||
|
||||
findFilter.Direction = &dirDesc
|
||||
groups, _, err = db.Group.Query(ctx, &nameFilter, &findFilter)
|
||||
assert.NoError(err)
|
||||
assert.Len(groups, 4)
|
||||
assert.Equal(c10.ID, groups[0].ID)
|
||||
assert.Equal(c2.ID, groups[1].ID)
|
||||
assert.Equal(c01.ID, groups[2].ID)
|
||||
assert.Equal(cEmpty.ID, groups[3].ID)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGroupUpdateFrontImage(t *testing.T) {
|
||||
if err := withRollbackTxn(func(ctx context.Context) error {
|
||||
qb := db.Group
|
||||
|
|
|
|||
|
|
@ -88,6 +88,23 @@ func getSortDirection(direction string) string {
|
|||
return direction
|
||||
}
|
||||
}
|
||||
|
||||
func isNaturalSort(sort string) bool {
|
||||
switch sort {
|
||||
case "name", "title", "description":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func isCoalesceSort(column, sort string) string {
|
||||
if sort == "description" {
|
||||
return coalesce(column)
|
||||
}
|
||||
return column
|
||||
}
|
||||
|
||||
func getSort(sort string, direction string, tableName string) string {
|
||||
direction = getSortDirection(direction)
|
||||
|
||||
|
|
@ -115,11 +132,8 @@ func getSort(sort string, direction string, tableName string) string {
|
|||
if strings.Contains(sort, ".") {
|
||||
colName = sort
|
||||
}
|
||||
if strings.Compare(sort, "name") == 0 {
|
||||
return " ORDER BY " + colName + " COLLATE NATURAL_CI " + direction
|
||||
}
|
||||
if strings.Compare(sort, "title") == 0 {
|
||||
return " ORDER BY " + colName + " COLLATE NATURAL_CI " + direction
|
||||
if isNaturalSort(sort) {
|
||||
return " ORDER BY " + isCoalesceSort(colName, sort) + " COLLATE NATURAL_CI " + direction
|
||||
}
|
||||
|
||||
return " ORDER BY " + colName + " " + direction
|
||||
|
|
|
|||
|
|
@ -54,9 +54,6 @@ const useContainingGroupFilterHook = (
|
|||
filter.criteria.push(groupCriterion);
|
||||
}
|
||||
|
||||
filter.sortBy = "sub_group_order";
|
||||
filter.sortDirection = GQL.SortDirectionEnum.Asc;
|
||||
|
||||
return filter;
|
||||
};
|
||||
};
|
||||
|
|
@ -67,18 +64,6 @@ interface IGroupSubGroupsPanel {
|
|||
extraOperations?: IItemListOperation<GQL.FindGroupsQueryResult>[];
|
||||
}
|
||||
|
||||
const defaultFilter = (() => {
|
||||
const sortBy = "sub_group_order";
|
||||
const ret = new ListFilterModel(GQL.FilterMode.Groups, undefined, {
|
||||
defaultSortBy: sortBy,
|
||||
});
|
||||
|
||||
// unset the sort by so that its not included in the URL
|
||||
ret.sortBy = undefined;
|
||||
|
||||
return ret;
|
||||
})();
|
||||
|
||||
export const GroupSubGroupsPanel: React.FC<IGroupSubGroupsPanel> =
|
||||
PatchComponent(
|
||||
"GroupSubGroupsPanel",
|
||||
|
|
@ -163,7 +148,6 @@ export const GroupSubGroupsPanel: React.FC<IGroupSubGroupsPanel> =
|
|||
<>
|
||||
{modal}
|
||||
<FilteredGroupList
|
||||
defaultFilter={defaultFilter}
|
||||
filterHook={filterHook}
|
||||
alterQuery={active}
|
||||
fromGroupId={group.id}
|
||||
|
|
|
|||
|
|
@ -147,7 +147,6 @@ const SidebarContent: React.FC<{
|
|||
|
||||
interface IGroupListContext {
|
||||
filterHook?: (filter: ListFilterModel) => ListFilterModel;
|
||||
defaultFilter?: ListFilterModel;
|
||||
view?: View;
|
||||
alterQuery?: boolean;
|
||||
}
|
||||
|
|
@ -210,13 +209,8 @@ export const FilteredGroupList = PatchComponent(
|
|||
onMove,
|
||||
fromGroupId,
|
||||
otherOperations: providedOperations = [],
|
||||
defaultFilter,
|
||||
} = props;
|
||||
|
||||
const withSidebar = view !== View.GroupSubGroups;
|
||||
const filterable = view !== View.GroupSubGroups;
|
||||
const sortable = view !== View.GroupSubGroups;
|
||||
|
||||
// States
|
||||
const {
|
||||
showSidebar,
|
||||
|
|
@ -230,7 +224,6 @@ export const FilteredGroupList = PatchComponent(
|
|||
useFilteredItemList({
|
||||
filterStateProps: {
|
||||
filterMode: GQL.FilterMode.Groups,
|
||||
defaultFilter,
|
||||
view,
|
||||
useURL: alterQuery,
|
||||
},
|
||||
|
|
@ -402,8 +395,6 @@ export const FilteredGroupList = PatchComponent(
|
|||
operationComponent={operations}
|
||||
view={view}
|
||||
zoomable
|
||||
filterable={filterable}
|
||||
sortable={sortable}
|
||||
/>
|
||||
|
||||
<FilterTags
|
||||
|
|
@ -455,10 +446,6 @@ export const FilteredGroupList = PatchComponent(
|
|||
</>
|
||||
);
|
||||
|
||||
if (!withSidebar) {
|
||||
return content;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx("item-list-container group-list", {
|
||||
|
|
|
|||
|
|
@ -99,6 +99,10 @@ export const FilteredListToolbar: React.FC<IFilteredListToolbar> = ({
|
|||
sortable = true,
|
||||
}) => {
|
||||
const filterOptions = filter.options;
|
||||
// Something in the popper layout for groups.sub-groups tab to double calculates the offset
|
||||
// causing the dropdown to be misaligned. Portal to document.body to fix this.
|
||||
const menuPortalTarget =
|
||||
typeof document !== "undefined" ? document.body : undefined;
|
||||
const { setDisplayMode, setZoom } = useFilterOperations({
|
||||
filter,
|
||||
setFilter,
|
||||
|
|
@ -142,6 +146,7 @@ export const FilteredListToolbar: React.FC<IFilteredListToolbar> = ({
|
|||
filter={filter}
|
||||
onSetFilter={setFilter}
|
||||
view={view}
|
||||
menuPortalTarget={menuPortalTarget}
|
||||
/>
|
||||
<FilterButton
|
||||
onClick={() => showEditFilter()}
|
||||
|
|
|
|||
|
|
@ -1611,6 +1611,7 @@
|
|||
"sub_group_count": "Sub-Group Count",
|
||||
"sub_group_of": "Sub-group of {parent}",
|
||||
"sub_group_order": "Sub-Group Order",
|
||||
"sub_group_description": "Sub-Group Description",
|
||||
"sub_groups": "Sub-Groups",
|
||||
"sub_tag_count": "Sub-Tag Count",
|
||||
"sub_tag_of": "Sub-tag of {parent}",
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ const sortByOptions = [
|
|||
"duration",
|
||||
"rating",
|
||||
"tag_count",
|
||||
"sub_group_description",
|
||||
"sub_group_order",
|
||||
]
|
||||
.map(ListFilterOptions.createSortBy)
|
||||
|
|
|
|||
Loading…
Reference in a new issue