mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 20:34:37 +03:00
Add scene/image/gallery popover count buttons for performer/studio/tag cards (#1293)
* Add counts to graphql schema * Add count resolvers and query refactor * Add count popover buttons
This commit is contained in:
@@ -20,6 +20,8 @@ fragment PerformerData on Performer {
|
|||||||
favorite
|
favorite
|
||||||
image_path
|
image_path
|
||||||
scene_count
|
scene_count
|
||||||
|
image_count
|
||||||
|
gallery_count
|
||||||
|
|
||||||
tags {
|
tags {
|
||||||
...TagData
|
...TagData
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ fragment StudioData on Studio {
|
|||||||
url
|
url
|
||||||
image_path
|
image_path
|
||||||
scene_count
|
scene_count
|
||||||
|
image_count
|
||||||
|
gallery_count
|
||||||
}
|
}
|
||||||
child_studios {
|
child_studios {
|
||||||
id
|
id
|
||||||
@@ -18,9 +20,13 @@ fragment StudioData on Studio {
|
|||||||
url
|
url
|
||||||
image_path
|
image_path
|
||||||
scene_count
|
scene_count
|
||||||
|
image_count
|
||||||
|
gallery_count
|
||||||
}
|
}
|
||||||
image_path
|
image_path
|
||||||
scene_count
|
scene_count
|
||||||
|
image_count
|
||||||
|
gallery_count
|
||||||
stash_ids {
|
stash_ids {
|
||||||
stash_id
|
stash_id
|
||||||
endpoint
|
endpoint
|
||||||
|
|||||||
@@ -4,5 +4,7 @@ fragment TagData on Tag {
|
|||||||
image_path
|
image_path
|
||||||
scene_count
|
scene_count
|
||||||
scene_marker_count
|
scene_marker_count
|
||||||
|
image_count
|
||||||
|
gallery_count
|
||||||
performer_count
|
performer_count
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ type Performer {
|
|||||||
|
|
||||||
image_path: String # Resolver
|
image_path: String # Resolver
|
||||||
scene_count: Int # Resolver
|
scene_count: Int # Resolver
|
||||||
|
image_count: Int # Resolver
|
||||||
|
gallery_count: Int # Resolver
|
||||||
scenes: [Scene!]!
|
scenes: [Scene!]!
|
||||||
stash_ids: [StashID!]!
|
stash_ids: [StashID!]!
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ type Studio {
|
|||||||
|
|
||||||
image_path: String # Resolver
|
image_path: String # Resolver
|
||||||
scene_count: Int # Resolver
|
scene_count: Int # Resolver
|
||||||
|
image_count: Int # Resolver
|
||||||
|
gallery_count: Int # Resolver
|
||||||
stash_ids: [StashID!]!
|
stash_ids: [StashID!]!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ type Tag {
|
|||||||
image_path: String # Resolver
|
image_path: String # Resolver
|
||||||
scene_count: Int # Resolver
|
scene_count: Int # Resolver
|
||||||
scene_marker_count: Int # Resolver
|
scene_marker_count: Int # Resolver
|
||||||
|
image_count: Int # Resolver
|
||||||
|
gallery_count: Int # Resolver
|
||||||
performer_count: Int
|
performer_count: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/api/urlbuilders"
|
"github.com/stashapp/stash/pkg/api/urlbuilders"
|
||||||
|
"github.com/stashapp/stash/pkg/gallery"
|
||||||
|
"github.com/stashapp/stash/pkg/image"
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -161,6 +163,30 @@ func (r *performerResolver) SceneCount(ctx context.Context, obj *models.Performe
|
|||||||
return &res, nil
|
return &res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *performerResolver) ImageCount(ctx context.Context, obj *models.Performer) (ret *int, err error) {
|
||||||
|
var res int
|
||||||
|
if err := r.withReadTxn(ctx, func(repo models.ReaderRepository) error {
|
||||||
|
res, err = image.CountByPerformerID(repo.Image(), obj.ID)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *performerResolver) GalleryCount(ctx context.Context, obj *models.Performer) (ret *int, err error) {
|
||||||
|
var res int
|
||||||
|
if err := r.withReadTxn(ctx, func(repo models.ReaderRepository) error {
|
||||||
|
res, err = gallery.CountByPerformerID(repo.Gallery(), obj.ID)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *performerResolver) Scenes(ctx context.Context, obj *models.Performer) (ret []*models.Scene, err error) {
|
func (r *performerResolver) Scenes(ctx context.Context, obj *models.Performer) (ret []*models.Scene, err error) {
|
||||||
if err := r.withReadTxn(ctx, func(repo models.ReaderRepository) error {
|
if err := r.withReadTxn(ctx, func(repo models.ReaderRepository) error {
|
||||||
ret, err = repo.Scene().FindByPerformerID(obj.ID)
|
ret, err = repo.Scene().FindByPerformerID(obj.ID)
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/api/urlbuilders"
|
"github.com/stashapp/stash/pkg/api/urlbuilders"
|
||||||
|
"github.com/stashapp/stash/pkg/gallery"
|
||||||
|
"github.com/stashapp/stash/pkg/image"
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -54,6 +56,30 @@ func (r *studioResolver) SceneCount(ctx context.Context, obj *models.Studio) (re
|
|||||||
return &res, err
|
return &res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *studioResolver) ImageCount(ctx context.Context, obj *models.Studio) (ret *int, err error) {
|
||||||
|
var res int
|
||||||
|
if err := r.withReadTxn(ctx, func(repo models.ReaderRepository) error {
|
||||||
|
res, err = image.CountByStudioID(repo.Image(), obj.ID)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *studioResolver) GalleryCount(ctx context.Context, obj *models.Studio) (ret *int, err error) {
|
||||||
|
var res int
|
||||||
|
if err := r.withReadTxn(ctx, func(repo models.ReaderRepository) error {
|
||||||
|
res, err = gallery.CountByStudioID(repo.Gallery(), obj.ID)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *studioResolver) ParentStudio(ctx context.Context, obj *models.Studio) (ret *models.Studio, err error) {
|
func (r *studioResolver) ParentStudio(ctx context.Context, obj *models.Studio) (ret *models.Studio, err error) {
|
||||||
if !obj.ParentID.Valid {
|
if !obj.ParentID.Valid {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/api/urlbuilders"
|
"github.com/stashapp/stash/pkg/api/urlbuilders"
|
||||||
|
"github.com/stashapp/stash/pkg/gallery"
|
||||||
|
"github.com/stashapp/stash/pkg/image"
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -31,6 +33,30 @@ func (r *tagResolver) SceneMarkerCount(ctx context.Context, obj *models.Tag) (re
|
|||||||
return &count, err
|
return &count, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *tagResolver) ImageCount(ctx context.Context, obj *models.Tag) (ret *int, err error) {
|
||||||
|
var res int
|
||||||
|
if err := r.withReadTxn(ctx, func(repo models.ReaderRepository) error {
|
||||||
|
res, err = image.CountByTagID(repo.Image(), obj.ID)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *tagResolver) GalleryCount(ctx context.Context, obj *models.Tag) (ret *int, err error) {
|
||||||
|
var res int
|
||||||
|
if err := r.withReadTxn(ctx, func(repo models.ReaderRepository) error {
|
||||||
|
res, err = gallery.CountByTagID(repo.Gallery(), obj.ID)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *tagResolver) PerformerCount(ctx context.Context, obj *models.Tag) (ret *int, err error) {
|
func (r *tagResolver) PerformerCount(ctx context.Context, obj *models.Tag) (ret *int, err error) {
|
||||||
var count int
|
var count int
|
||||||
if err := r.withReadTxn(ctx, func(repo models.ReaderRepository) error {
|
if err := r.withReadTxn(ctx, func(repo models.ReaderRepository) error {
|
||||||
|
|||||||
40
pkg/gallery/query.go
Normal file
40
pkg/gallery/query.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package gallery
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/stashapp/stash/pkg/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CountByPerformerID(r models.GalleryReader, id int) (int, error) {
|
||||||
|
filter := &models.GalleryFilterType{
|
||||||
|
Performers: &models.MultiCriterionInput{
|
||||||
|
Value: []string{strconv.Itoa(id)},
|
||||||
|
Modifier: models.CriterionModifierIncludes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.QueryCount(filter, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CountByStudioID(r models.GalleryReader, id int) (int, error) {
|
||||||
|
filter := &models.GalleryFilterType{
|
||||||
|
Studios: &models.MultiCriterionInput{
|
||||||
|
Value: []string{strconv.Itoa(id)},
|
||||||
|
Modifier: models.CriterionModifierIncludes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.QueryCount(filter, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CountByTagID(r models.GalleryReader, id int) (int, error) {
|
||||||
|
filter := &models.GalleryFilterType{
|
||||||
|
Tags: &models.MultiCriterionInput{
|
||||||
|
Value: []string{strconv.Itoa(id)},
|
||||||
|
Modifier: models.CriterionModifierIncludes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.QueryCount(filter, nil)
|
||||||
|
}
|
||||||
40
pkg/image/query.go
Normal file
40
pkg/image/query.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package image
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/stashapp/stash/pkg/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CountByPerformerID(r models.ImageReader, id int) (int, error) {
|
||||||
|
filter := &models.ImageFilterType{
|
||||||
|
Performers: &models.MultiCriterionInput{
|
||||||
|
Value: []string{strconv.Itoa(id)},
|
||||||
|
Modifier: models.CriterionModifierIncludes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.QueryCount(filter, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CountByStudioID(r models.ImageReader, id int) (int, error) {
|
||||||
|
filter := &models.ImageFilterType{
|
||||||
|
Studios: &models.MultiCriterionInput{
|
||||||
|
Value: []string{strconv.Itoa(id)},
|
||||||
|
Modifier: models.CriterionModifierIncludes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.QueryCount(filter, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CountByTagID(r models.ImageReader, id int) (int, error) {
|
||||||
|
filter := &models.ImageFilterType{
|
||||||
|
Tags: &models.MultiCriterionInput{
|
||||||
|
Value: []string{strconv.Itoa(id)},
|
||||||
|
Modifier: models.CriterionModifierIncludes,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.QueryCount(filter, nil)
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ type GalleryReader interface {
|
|||||||
Count() (int, error)
|
Count() (int, error)
|
||||||
All() ([]*Gallery, error)
|
All() ([]*Gallery, error)
|
||||||
Query(galleryFilter *GalleryFilterType, findFilter *FindFilterType) ([]*Gallery, int, error)
|
Query(galleryFilter *GalleryFilterType, findFilter *FindFilterType) ([]*Gallery, int, error)
|
||||||
|
QueryCount(galleryFilter *GalleryFilterType, findFilter *FindFilterType) (int, error)
|
||||||
GetPerformerIDs(galleryID int) ([]int, error)
|
GetPerformerIDs(galleryID int) ([]int, error)
|
||||||
GetTagIDs(galleryID int) ([]int, error)
|
GetTagIDs(galleryID int) ([]int, error)
|
||||||
GetSceneIDs(galleryID int) ([]int, error)
|
GetSceneIDs(galleryID int) ([]int, error)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ type ImageReader interface {
|
|||||||
// CountByTagID(tagID int) (int, error)
|
// CountByTagID(tagID int) (int, error)
|
||||||
All() ([]*Image, error)
|
All() ([]*Image, error)
|
||||||
Query(imageFilter *ImageFilterType, findFilter *FindFilterType) ([]*Image, int, error)
|
Query(imageFilter *ImageFilterType, findFilter *FindFilterType) ([]*Image, int, error)
|
||||||
|
QueryCount(imageFilter *ImageFilterType, findFilter *FindFilterType) (int, error)
|
||||||
GetGalleryIDs(imageID int) ([]int, error)
|
GetGalleryIDs(imageID int) ([]int, error)
|
||||||
GetTagIDs(imageID int) ([]int, error)
|
GetTagIDs(imageID int) ([]int, error)
|
||||||
GetPerformerIDs(imageID int) ([]int, error)
|
GetPerformerIDs(imageID int) ([]int, error)
|
||||||
|
|||||||
@@ -376,6 +376,27 @@ func (_m *GalleryReaderWriter) Query(galleryFilter *models.GalleryFilterType, fi
|
|||||||
return r0, r1, r2
|
return r0, r1, r2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QueryCount provides a mock function with given fields: galleryFilter, findFilter
|
||||||
|
func (_m *GalleryReaderWriter) QueryCount(galleryFilter *models.GalleryFilterType, findFilter *models.FindFilterType) (int, error) {
|
||||||
|
ret := _m.Called(galleryFilter, findFilter)
|
||||||
|
|
||||||
|
var r0 int
|
||||||
|
if rf, ok := ret.Get(0).(func(*models.GalleryFilterType, *models.FindFilterType) int); ok {
|
||||||
|
r0 = rf(galleryFilter, findFilter)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(int)
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(*models.GalleryFilterType, *models.FindFilterType) error); ok {
|
||||||
|
r1 = rf(galleryFilter, findFilter)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
// Update provides a mock function with given fields: updatedGallery
|
// Update provides a mock function with given fields: updatedGallery
|
||||||
func (_m *GalleryReaderWriter) Update(updatedGallery models.Gallery) (*models.Gallery, error) {
|
func (_m *GalleryReaderWriter) Update(updatedGallery models.Gallery) (*models.Gallery, error) {
|
||||||
ret := _m.Called(updatedGallery)
|
ret := _m.Called(updatedGallery)
|
||||||
|
|||||||
@@ -370,6 +370,27 @@ func (_m *ImageReaderWriter) Query(imageFilter *models.ImageFilterType, findFilt
|
|||||||
return r0, r1, r2
|
return r0, r1, r2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QueryCount provides a mock function with given fields: imageFilter, findFilter
|
||||||
|
func (_m *ImageReaderWriter) QueryCount(imageFilter *models.ImageFilterType, findFilter *models.FindFilterType) (int, error) {
|
||||||
|
ret := _m.Called(imageFilter, findFilter)
|
||||||
|
|
||||||
|
var r0 int
|
||||||
|
if rf, ok := ret.Get(0).(func(*models.ImageFilterType, *models.FindFilterType) int); ok {
|
||||||
|
r0 = rf(imageFilter, findFilter)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(int)
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(*models.ImageFilterType, *models.FindFilterType) error); ok {
|
||||||
|
r1 = rf(imageFilter, findFilter)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
// ResetOCounter provides a mock function with given fields: id
|
// ResetOCounter provides a mock function with given fields: id
|
||||||
func (_m *ImageReaderWriter) ResetOCounter(id int) (int, error) {
|
func (_m *ImageReaderWriter) ResetOCounter(id int) (int, error) {
|
||||||
ret := _m.Called(id)
|
ret := _m.Called(id)
|
||||||
|
|||||||
@@ -415,6 +415,29 @@ func (_m *SceneReaderWriter) FindByPerformerID(performerID int) ([]*models.Scene
|
|||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindDuplicates provides a mock function with given fields: distance
|
||||||
|
func (_m *SceneReaderWriter) FindDuplicates(distance int) ([][]*models.Scene, error) {
|
||||||
|
ret := _m.Called(distance)
|
||||||
|
|
||||||
|
var r0 [][]*models.Scene
|
||||||
|
if rf, ok := ret.Get(0).(func(int) [][]*models.Scene); ok {
|
||||||
|
r0 = rf(distance)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([][]*models.Scene)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(int) error); ok {
|
||||||
|
r1 = rf(distance)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
// FindMany provides a mock function with given fields: ids
|
// FindMany provides a mock function with given fields: ids
|
||||||
func (_m *SceneReaderWriter) FindMany(ids []int) ([]*models.Scene, error) {
|
func (_m *SceneReaderWriter) FindMany(ids []int) ([]*models.Scene, error) {
|
||||||
ret := _m.Called(ids)
|
ret := _m.Called(ids)
|
||||||
@@ -438,30 +461,6 @@ func (_m *SceneReaderWriter) FindMany(ids []int) ([]*models.Scene, error) {
|
|||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindDuplicates provides a mock function with given fields: distance
|
|
||||||
func (_m *SceneReaderWriter) FindDuplicates(distance int) ([][]*models.Scene, error) {
|
|
||||||
ret := _m.Called(distance)
|
|
||||||
|
|
||||||
var r0 [][]*models.Scene
|
|
||||||
if rf, ok := ret.Get(0).(func(int) [][]*models.Scene); ok {
|
|
||||||
r0 = rf(distance)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if ret.Get(0) != nil {
|
|
||||||
r0 = ret.Get(0).([][]*models.Scene)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var r1 error
|
|
||||||
if rf, ok := ret.Get(1).(func(int) error); ok {
|
|
||||||
r1 = rf(distance)
|
|
||||||
} else {
|
|
||||||
r1 = ret.Error(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0, r1
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCover provides a mock function with given fields: sceneID
|
// GetCover provides a mock function with given fields: sceneID
|
||||||
func (_m *SceneReaderWriter) GetCover(sceneID int) ([]byte, error) {
|
func (_m *SceneReaderWriter) GetCover(sceneID int) ([]byte, error) {
|
||||||
ret := _m.Called(sceneID)
|
ret := _m.Called(sceneID)
|
||||||
@@ -651,29 +650,6 @@ func (_m *SceneReaderWriter) Query(sceneFilter *models.SceneFilterType, findFilt
|
|||||||
return r0, r1, r2
|
return r0, r1, r2
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryForAutoTag provides a mock function with given fields: regex, pathPrefixes
|
|
||||||
func (_m *SceneReaderWriter) QueryForAutoTag(regex string, pathPrefixes []string) ([]*models.Scene, error) {
|
|
||||||
ret := _m.Called(regex, pathPrefixes)
|
|
||||||
|
|
||||||
var r0 []*models.Scene
|
|
||||||
if rf, ok := ret.Get(0).(func(string, []string) []*models.Scene); ok {
|
|
||||||
r0 = rf(regex, pathPrefixes)
|
|
||||||
} else {
|
|
||||||
if ret.Get(0) != nil {
|
|
||||||
r0 = ret.Get(0).([]*models.Scene)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var r1 error
|
|
||||||
if rf, ok := ret.Get(1).(func(string, []string) error); ok {
|
|
||||||
r1 = rf(regex, pathPrefixes)
|
|
||||||
} else {
|
|
||||||
r1 = ret.Error(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0, r1
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResetOCounter provides a mock function with given fields: id
|
// ResetOCounter provides a mock function with given fields: id
|
||||||
func (_m *SceneReaderWriter) ResetOCounter(id int) (int, error) {
|
func (_m *SceneReaderWriter) ResetOCounter(id int) (int, error) {
|
||||||
ret := _m.Called(id)
|
ret := _m.Called(id)
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ func (qb *galleryQueryBuilder) All() ([]*models.Gallery, error) {
|
|||||||
return qb.queryGalleries(selectAll("galleries")+qb.getGallerySort(nil), nil)
|
return qb.queryGalleries(selectAll("galleries")+qb.getGallerySort(nil), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qb *galleryQueryBuilder) Query(galleryFilter *models.GalleryFilterType, findFilter *models.FindFilterType) ([]*models.Gallery, int, error) {
|
func (qb *galleryQueryBuilder) makeQuery(galleryFilter *models.GalleryFilterType, findFilter *models.FindFilterType) queryBuilder {
|
||||||
if galleryFilter == nil {
|
if galleryFilter == nil {
|
||||||
galleryFilter = &models.GalleryFilterType{}
|
galleryFilter = &models.GalleryFilterType{}
|
||||||
}
|
}
|
||||||
@@ -283,6 +283,13 @@ func (qb *galleryQueryBuilder) Query(galleryFilter *models.GalleryFilterType, fi
|
|||||||
handleGalleryPerformerTagsCriterion(&query, galleryFilter.PerformerTags)
|
handleGalleryPerformerTagsCriterion(&query, galleryFilter.PerformerTags)
|
||||||
|
|
||||||
query.sortAndPagination = qb.getGallerySort(findFilter) + getPagination(findFilter)
|
query.sortAndPagination = qb.getGallerySort(findFilter) + getPagination(findFilter)
|
||||||
|
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qb *galleryQueryBuilder) Query(galleryFilter *models.GalleryFilterType, findFilter *models.FindFilterType) ([]*models.Gallery, int, error) {
|
||||||
|
query := qb.makeQuery(galleryFilter, findFilter)
|
||||||
|
|
||||||
idsResult, countResult, err := query.executeFind()
|
idsResult, countResult, err := query.executeFind()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
@@ -301,6 +308,12 @@ func (qb *galleryQueryBuilder) Query(galleryFilter *models.GalleryFilterType, fi
|
|||||||
return galleries, countResult, nil
|
return galleries, countResult, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (qb *galleryQueryBuilder) QueryCount(galleryFilter *models.GalleryFilterType, findFilter *models.FindFilterType) (int, error) {
|
||||||
|
query := qb.makeQuery(galleryFilter, findFilter)
|
||||||
|
|
||||||
|
return query.executeCount()
|
||||||
|
}
|
||||||
|
|
||||||
func (qb *galleryQueryBuilder) handleAverageResolutionFilter(query *queryBuilder, resolutionFilter *models.ResolutionEnum) {
|
func (qb *galleryQueryBuilder) handleAverageResolutionFilter(query *queryBuilder, resolutionFilter *models.ResolutionEnum) {
|
||||||
if resolutionFilter == nil {
|
if resolutionFilter == nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ func (qb *imageQueryBuilder) All() ([]*models.Image, error) {
|
|||||||
return qb.queryImages(selectAll(imageTable)+qb.getImageSort(nil), nil)
|
return qb.queryImages(selectAll(imageTable)+qb.getImageSort(nil), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qb *imageQueryBuilder) Query(imageFilter *models.ImageFilterType, findFilter *models.FindFilterType) ([]*models.Image, int, error) {
|
func (qb *imageQueryBuilder) makeQuery(imageFilter *models.ImageFilterType, findFilter *models.FindFilterType) queryBuilder {
|
||||||
if imageFilter == nil {
|
if imageFilter == nil {
|
||||||
imageFilter = &models.ImageFilterType{}
|
imageFilter = &models.ImageFilterType{}
|
||||||
}
|
}
|
||||||
@@ -383,6 +383,13 @@ func (qb *imageQueryBuilder) Query(imageFilter *models.ImageFilterType, findFilt
|
|||||||
handleImagePerformerTagsCriterion(&query, imageFilter.PerformerTags)
|
handleImagePerformerTagsCriterion(&query, imageFilter.PerformerTags)
|
||||||
|
|
||||||
query.sortAndPagination = qb.getImageSort(findFilter) + getPagination(findFilter)
|
query.sortAndPagination = qb.getImageSort(findFilter) + getPagination(findFilter)
|
||||||
|
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qb *imageQueryBuilder) Query(imageFilter *models.ImageFilterType, findFilter *models.FindFilterType) ([]*models.Image, int, error) {
|
||||||
|
query := qb.makeQuery(imageFilter, findFilter)
|
||||||
|
|
||||||
idsResult, countResult, err := query.executeFind()
|
idsResult, countResult, err := query.executeFind()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
@@ -401,6 +408,12 @@ func (qb *imageQueryBuilder) Query(imageFilter *models.ImageFilterType, findFilt
|
|||||||
return images, countResult, nil
|
return images, countResult, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (qb *imageQueryBuilder) QueryCount(imageFilter *models.ImageFilterType, findFilter *models.FindFilterType) (int, error) {
|
||||||
|
query := qb.makeQuery(imageFilter, findFilter)
|
||||||
|
|
||||||
|
return query.executeCount()
|
||||||
|
}
|
||||||
|
|
||||||
func handleImagePerformerTagsCriterion(query *queryBuilder, performerTagsFilter *models.MultiCriterionInput) {
|
func handleImagePerformerTagsCriterion(query *queryBuilder, performerTagsFilter *models.MultiCriterionInput) {
|
||||||
if performerTagsFilter != nil && len(performerTagsFilter.Value) > 0 {
|
if performerTagsFilter != nil && len(performerTagsFilter.Value) > 0 {
|
||||||
for _, tagID := range performerTagsFilter.Value {
|
for _, tagID := range performerTagsFilter.Value {
|
||||||
|
|||||||
@@ -95,6 +95,12 @@ func imageQueryQ(t *testing.T, sqb models.ImageReader, q string, expectedImageId
|
|||||||
image := images[0]
|
image := images[0]
|
||||||
assert.Equal(t, imageIDs[expectedImageIdx], image.ID)
|
assert.Equal(t, imageIDs[expectedImageIdx], image.ID)
|
||||||
|
|
||||||
|
count, err := sqb.QueryCount(nil, &filter)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error querying image: %s", err.Error())
|
||||||
|
}
|
||||||
|
assert.Equal(t, len(images), count)
|
||||||
|
|
||||||
// no Q should return all results
|
// no Q should return all results
|
||||||
filter.Q = nil
|
filter.Q = nil
|
||||||
images, _, err = sqb.Query(nil, &filter)
|
images, _, err = sqb.Query(nil, &filter)
|
||||||
|
|||||||
@@ -33,6 +33,19 @@ func (qb queryBuilder) executeFind() ([]int, int, error) {
|
|||||||
return qb.repository.executeFindQuery(body, qb.args, qb.sortAndPagination, qb.whereClauses, qb.havingClauses)
|
return qb.repository.executeFindQuery(body, qb.args, qb.sortAndPagination, qb.whereClauses, qb.havingClauses)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (qb queryBuilder) executeCount() (int, error) {
|
||||||
|
if qb.err != nil {
|
||||||
|
return 0, qb.err
|
||||||
|
}
|
||||||
|
|
||||||
|
body := qb.body
|
||||||
|
body += qb.joins.toSQL()
|
||||||
|
|
||||||
|
body = qb.repository.buildQueryBody(body, qb.whereClauses, qb.havingClauses)
|
||||||
|
countQuery := qb.repository.buildCountQuery(body)
|
||||||
|
return qb.repository.runCountQuery(countQuery, qb.args)
|
||||||
|
}
|
||||||
|
|
||||||
func (qb *queryBuilder) addWhere(clauses ...string) {
|
func (qb *queryBuilder) addWhere(clauses ...string) {
|
||||||
for _, clause := range clauses {
|
for _, clause := range clauses {
|
||||||
if len(clause) > 0 {
|
if len(clause) > 0 {
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ func (r *repository) querySimple(query string, args []interface{}, out interface
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *repository) executeFindQuery(body string, args []interface{}, sortAndPagination string, whereClauses []string, havingClauses []string) ([]int, int, error) {
|
func (r *repository) buildQueryBody(body string, whereClauses []string, havingClauses []string) string {
|
||||||
if len(whereClauses) > 0 {
|
if len(whereClauses) > 0 {
|
||||||
body = body + " WHERE " + strings.Join(whereClauses, " AND ") // TODO handle AND or OR
|
body = body + " WHERE " + strings.Join(whereClauses, " AND ") // TODO handle AND or OR
|
||||||
}
|
}
|
||||||
@@ -243,6 +243,12 @@ func (r *repository) executeFindQuery(body string, args []interface{}, sortAndPa
|
|||||||
body = body + " HAVING " + strings.Join(havingClauses, " AND ") // TODO handle AND or OR
|
body = body + " HAVING " + strings.Join(havingClauses, " AND ") // TODO handle AND or OR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return body
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *repository) executeFindQuery(body string, args []interface{}, sortAndPagination string, whereClauses []string, havingClauses []string) ([]int, int, error) {
|
||||||
|
body = r.buildQueryBody(body, whereClauses, havingClauses)
|
||||||
|
|
||||||
countQuery := r.buildCountQuery(body)
|
countQuery := r.buildCountQuery(body)
|
||||||
idsQuery := body + sortAndPagination
|
idsQuery := body + sortAndPagination
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
* Added scene queue.
|
* Added scene queue.
|
||||||
|
|
||||||
### 🎨 Improvements
|
### 🎨 Improvements
|
||||||
|
* Add popover buttons for scenes/images/galleries on performer/studio/tag cards.
|
||||||
* Add slideshow to image wall view.
|
* Add slideshow to image wall view.
|
||||||
* Support API key via URL query parameter, and added API key to stream link in Scene File Info.
|
* Support API key via URL query parameter, and added API key to stream link in Scene File Info.
|
||||||
* Revamped setup wizard and migration UI.
|
* Revamped setup wizard and migration UI.
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
TruncatedText,
|
TruncatedText,
|
||||||
} from "src/components/Shared";
|
} from "src/components/Shared";
|
||||||
import { Button, ButtonGroup } from "react-bootstrap";
|
import { Button, ButtonGroup } from "react-bootstrap";
|
||||||
|
import { PopoverCountButton } from "../Shared/PopoverCountButton";
|
||||||
|
|
||||||
interface IPerformerCardProps {
|
interface IPerformerCardProps {
|
||||||
performer: GQL.PerformerDataFragment;
|
performer: GQL.PerformerDataFragment;
|
||||||
@@ -46,12 +47,35 @@ export const PerformerCard: React.FC<IPerformerCardProps> = ({
|
|||||||
if (!performer.scene_count) return;
|
if (!performer.scene_count) return;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to={NavUtils.makePerformerScenesUrl(performer)}>
|
<PopoverCountButton
|
||||||
<Button className="minimal">
|
type="scene"
|
||||||
<Icon icon="play-circle" />
|
count={performer.scene_count}
|
||||||
<span>{performer.scene_count}</span>
|
url={NavUtils.makePerformerScenesUrl(performer)}
|
||||||
</Button>
|
/>
|
||||||
</Link>
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function maybeRenderImagesPopoverButton() {
|
||||||
|
if (!performer.image_count) return;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PopoverCountButton
|
||||||
|
type="image"
|
||||||
|
count={performer.image_count}
|
||||||
|
url={NavUtils.makePerformerImagesUrl(performer)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function maybeRenderGalleriesPopoverButton() {
|
||||||
|
if (!performer.gallery_count) return;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PopoverCountButton
|
||||||
|
type="gallery"
|
||||||
|
count={performer.gallery_count}
|
||||||
|
url={NavUtils.makePerformerGalleriesUrl(performer)}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,12 +97,19 @@ export const PerformerCard: React.FC<IPerformerCardProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function maybeRenderPopoverButtonGroup() {
|
function maybeRenderPopoverButtonGroup() {
|
||||||
if (performer.scene_count || performer.tags.length > 0) {
|
if (
|
||||||
|
performer.scene_count ||
|
||||||
|
performer.image_count ||
|
||||||
|
performer.gallery_count ||
|
||||||
|
performer.tags.length > 0
|
||||||
|
) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<hr />
|
<hr />
|
||||||
<ButtonGroup className="card-popovers">
|
<ButtonGroup className="card-popovers">
|
||||||
{maybeRenderScenesPopoverButton()}
|
{maybeRenderScenesPopoverButton()}
|
||||||
|
{maybeRenderImagesPopoverButton()}
|
||||||
|
{maybeRenderGalleriesPopoverButton()}
|
||||||
{maybeRenderTagPopoverButton()}
|
{maybeRenderTagPopoverButton()}
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</>
|
</>
|
||||||
|
|||||||
64
ui/v2.5/src/components/Shared/PopoverCountButton.tsx
Normal file
64
ui/v2.5/src/components/Shared/PopoverCountButton.tsx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Button } from "react-bootstrap";
|
||||||
|
import { useIntl } from "react-intl";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import Icon from "./Icon";
|
||||||
|
|
||||||
|
type PopoverLinkType = "scene" | "image" | "gallery";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
url: string;
|
||||||
|
type: PopoverLinkType;
|
||||||
|
count: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PopoverCountButton: React.FC<IProps> = ({ url, type, count }) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
function getIcon() {
|
||||||
|
switch (type) {
|
||||||
|
case "scene":
|
||||||
|
return "play-circle";
|
||||||
|
case "image":
|
||||||
|
return "image";
|
||||||
|
case "gallery":
|
||||||
|
return "images";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPluralOptions() {
|
||||||
|
switch (type) {
|
||||||
|
case "scene":
|
||||||
|
return {
|
||||||
|
one: "scene",
|
||||||
|
other: "scenes",
|
||||||
|
};
|
||||||
|
case "image":
|
||||||
|
return {
|
||||||
|
one: "image",
|
||||||
|
other: "images",
|
||||||
|
};
|
||||||
|
case "gallery":
|
||||||
|
return {
|
||||||
|
one: "gallery",
|
||||||
|
other: "galleries",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTitle() {
|
||||||
|
const pluralCategory = intl.formatPlural(count);
|
||||||
|
const options = getPluralOptions();
|
||||||
|
const plural = options[pluralCategory as "one"] || options.other;
|
||||||
|
return `${count} ${plural}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link to={url} title={getTitle()}>
|
||||||
|
<Button className="minimal">
|
||||||
|
<Icon icon={getIcon()} />
|
||||||
|
<span>{count}</span>
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import * as GQL from "src/core/generated-graphql";
|
import * as GQL from "src/core/generated-graphql";
|
||||||
import { FormattedPlural } from "react-intl";
|
|
||||||
import { NavUtils } from "src/utils";
|
import { NavUtils } from "src/utils";
|
||||||
import { BasicCard, TruncatedText } from "src/components/Shared";
|
import { BasicCard, TruncatedText } from "src/components/Shared";
|
||||||
|
import { ButtonGroup } from "react-bootstrap";
|
||||||
|
import { PopoverCountButton } from "../Shared/PopoverCountButton";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
studio: GQL.StudioDataFragment;
|
studio: GQL.StudioDataFragment;
|
||||||
@@ -51,6 +52,57 @@ export const StudioCard: React.FC<IProps> = ({
|
|||||||
selected,
|
selected,
|
||||||
onSelectedChanged,
|
onSelectedChanged,
|
||||||
}) => {
|
}) => {
|
||||||
|
function maybeRenderScenesPopoverButton() {
|
||||||
|
if (!studio.scene_count) return;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PopoverCountButton
|
||||||
|
type="scene"
|
||||||
|
count={studio.scene_count}
|
||||||
|
url={NavUtils.makeStudioScenesUrl(studio)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function maybeRenderImagesPopoverButton() {
|
||||||
|
if (!studio.image_count) return;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PopoverCountButton
|
||||||
|
type="image"
|
||||||
|
count={studio.image_count}
|
||||||
|
url={NavUtils.makeStudioImagesUrl(studio)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function maybeRenderGalleriesPopoverButton() {
|
||||||
|
if (!studio.gallery_count) return;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PopoverCountButton
|
||||||
|
type="gallery"
|
||||||
|
count={studio.gallery_count}
|
||||||
|
url={NavUtils.makeStudioGalleriesUrl(studio)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function maybeRenderPopoverButtonGroup() {
|
||||||
|
if (studio.scene_count || studio.image_count || studio.gallery_count) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<hr />
|
||||||
|
<ButtonGroup className="card-popovers">
|
||||||
|
{maybeRenderScenesPopoverButton()}
|
||||||
|
{maybeRenderImagesPopoverButton()}
|
||||||
|
{maybeRenderGalleriesPopoverButton()}
|
||||||
|
</ButtonGroup>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BasicCard
|
<BasicCard
|
||||||
className="studio-card"
|
className="studio-card"
|
||||||
@@ -68,17 +120,9 @@ export const StudioCard: React.FC<IProps> = ({
|
|||||||
<h5>
|
<h5>
|
||||||
<TruncatedText text={studio.name} />
|
<TruncatedText text={studio.name} />
|
||||||
</h5>
|
</h5>
|
||||||
<span>
|
|
||||||
{studio.scene_count}
|
|
||||||
<FormattedPlural
|
|
||||||
value={studio.scene_count ?? 0}
|
|
||||||
one="scene"
|
|
||||||
other="scenes"
|
|
||||||
/>
|
|
||||||
.
|
|
||||||
</span>
|
|
||||||
{maybeRenderParent(studio, hideParent)}
|
{maybeRenderParent(studio, hideParent)}
|
||||||
{maybeRenderChildren(studio)}
|
{maybeRenderChildren(studio)}
|
||||||
|
{maybeRenderPopoverButtonGroup()}
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
selected={selected}
|
selected={selected}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import * as GQL from "src/core/generated-graphql";
|
|||||||
import { NavUtils } from "src/utils";
|
import { NavUtils } from "src/utils";
|
||||||
import { Icon, TruncatedText } from "../Shared";
|
import { Icon, TruncatedText } from "../Shared";
|
||||||
import { BasicCard } from "../Shared/BasicCard";
|
import { BasicCard } from "../Shared/BasicCard";
|
||||||
|
import { PopoverCountButton } from "../Shared/PopoverCountButton";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tag: GQL.TagDataFragment;
|
tag: GQL.TagDataFragment;
|
||||||
@@ -25,12 +26,11 @@ export const TagCard: React.FC<IProps> = ({
|
|||||||
if (!tag.scene_count) return;
|
if (!tag.scene_count) return;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to={NavUtils.makeTagScenesUrl(tag)}>
|
<PopoverCountButton
|
||||||
<Button className="minimal">
|
type="scene"
|
||||||
<Icon icon="play-circle" />
|
count={tag.scene_count}
|
||||||
<span>{tag.scene_count}</span>
|
url={NavUtils.makeTagScenesUrl(tag)}
|
||||||
</Button>
|
/>
|
||||||
</Link>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,6 +47,30 @@ export const TagCard: React.FC<IProps> = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function maybeRenderImagesPopoverButton() {
|
||||||
|
if (!tag.image_count) return;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PopoverCountButton
|
||||||
|
type="image"
|
||||||
|
count={tag.image_count}
|
||||||
|
url={NavUtils.makeTagImagesUrl(tag)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function maybeRenderGalleriesPopoverButton() {
|
||||||
|
if (!tag.gallery_count) return;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PopoverCountButton
|
||||||
|
type="gallery"
|
||||||
|
count={tag.gallery_count}
|
||||||
|
url={NavUtils.makeTagGalleriesUrl(tag)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function maybeRenderPerformersPopoverButton() {
|
function maybeRenderPerformersPopoverButton() {
|
||||||
if (!tag.performer_count) return;
|
if (!tag.performer_count) return;
|
||||||
|
|
||||||
@@ -67,6 +91,8 @@ export const TagCard: React.FC<IProps> = ({
|
|||||||
<hr />
|
<hr />
|
||||||
<ButtonGroup className="card-popovers">
|
<ButtonGroup className="card-popovers">
|
||||||
{maybeRenderScenesPopoverButton()}
|
{maybeRenderScenesPopoverButton()}
|
||||||
|
{maybeRenderImagesPopoverButton()}
|
||||||
|
{maybeRenderGalleriesPopoverButton()}
|
||||||
{maybeRenderSceneMarkersPopoverButton()}
|
{maybeRenderSceneMarkersPopoverButton()}
|
||||||
{maybeRenderPerformersPopoverButton()}
|
{maybeRenderPerformersPopoverButton()}
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
|
|||||||
@@ -23,6 +23,32 @@ const makePerformerScenesUrl = (
|
|||||||
return `/scenes?${filter.makeQueryParameters()}`;
|
return `/scenes?${filter.makeQueryParameters()}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const makePerformerImagesUrl = (
|
||||||
|
performer: Partial<GQL.PerformerDataFragment>
|
||||||
|
) => {
|
||||||
|
if (!performer.id) return "#";
|
||||||
|
const filter = new ListFilterModel(FilterMode.Images);
|
||||||
|
const criterion = new PerformersCriterion();
|
||||||
|
criterion.value = [
|
||||||
|
{ id: performer.id, label: performer.name || `Performer ${performer.id}` },
|
||||||
|
];
|
||||||
|
filter.criteria.push(criterion);
|
||||||
|
return `/images?${filter.makeQueryParameters()}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const makePerformerGalleriesUrl = (
|
||||||
|
performer: Partial<GQL.PerformerDataFragment>
|
||||||
|
) => {
|
||||||
|
if (!performer.id) return "#";
|
||||||
|
const filter = new ListFilterModel(FilterMode.Galleries);
|
||||||
|
const criterion = new PerformersCriterion();
|
||||||
|
criterion.value = [
|
||||||
|
{ id: performer.id, label: performer.name || `Performer ${performer.id}` },
|
||||||
|
];
|
||||||
|
filter.criteria.push(criterion);
|
||||||
|
return `/galleries?${filter.makeQueryParameters()}`;
|
||||||
|
};
|
||||||
|
|
||||||
const makePerformersCountryUrl = (
|
const makePerformersCountryUrl = (
|
||||||
performer: Partial<GQL.PerformerDataFragment>
|
performer: Partial<GQL.PerformerDataFragment>
|
||||||
) => {
|
) => {
|
||||||
@@ -45,6 +71,28 @@ const makeStudioScenesUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
|||||||
return `/scenes?${filter.makeQueryParameters()}`;
|
return `/scenes?${filter.makeQueryParameters()}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const makeStudioImagesUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
||||||
|
if (!studio.id) return "#";
|
||||||
|
const filter = new ListFilterModel(FilterMode.Images);
|
||||||
|
const criterion = new StudiosCriterion();
|
||||||
|
criterion.value = [
|
||||||
|
{ id: studio.id, label: studio.name || `Studio ${studio.id}` },
|
||||||
|
];
|
||||||
|
filter.criteria.push(criterion);
|
||||||
|
return `/images?${filter.makeQueryParameters()}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const makeStudioGalleriesUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
||||||
|
if (!studio.id) return "#";
|
||||||
|
const filter = new ListFilterModel(FilterMode.Galleries);
|
||||||
|
const criterion = new StudiosCriterion();
|
||||||
|
criterion.value = [
|
||||||
|
{ id: studio.id, label: studio.name || `Studio ${studio.id}` },
|
||||||
|
];
|
||||||
|
filter.criteria.push(criterion);
|
||||||
|
return `/galleries?${filter.makeQueryParameters()}`;
|
||||||
|
};
|
||||||
|
|
||||||
const makeChildStudiosUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
const makeChildStudiosUrl = (studio: Partial<GQL.StudioDataFragment>) => {
|
||||||
if (!studio.id) return "#";
|
if (!studio.id) return "#";
|
||||||
const filter = new ListFilterModel(FilterMode.Studios);
|
const filter = new ListFilterModel(FilterMode.Studios);
|
||||||
@@ -121,8 +169,12 @@ const makeSceneMarkerUrl = (
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
makePerformerScenesUrl,
|
makePerformerScenesUrl,
|
||||||
|
makePerformerImagesUrl,
|
||||||
|
makePerformerGalleriesUrl,
|
||||||
makePerformersCountryUrl,
|
makePerformersCountryUrl,
|
||||||
makeStudioScenesUrl,
|
makeStudioScenesUrl,
|
||||||
|
makeStudioImagesUrl,
|
||||||
|
makeStudioGalleriesUrl,
|
||||||
makeTagSceneMarkersUrl,
|
makeTagSceneMarkersUrl,
|
||||||
makeTagScenesUrl,
|
makeTagScenesUrl,
|
||||||
makeTagPerformersUrl,
|
makeTagPerformersUrl,
|
||||||
|
|||||||
Reference in New Issue
Block a user