diff --git a/api/graphql/generated.go b/api/graphql/generated.go
index 8c325750..8d130f6c 100644
--- a/api/graphql/generated.go
+++ b/api/graphql/generated.go
@@ -175,6 +175,7 @@ type ComplexityRoot struct {
Query struct {
Album func(childComplexity int, id int) int
+ FaceGroup func(childComplexity int, id int) int
MapboxToken func(childComplexity int) int
Media func(childComplexity int, id int) int
MediaList func(childComplexity int, ids []int) int
@@ -322,6 +323,7 @@ type QueryResolver interface {
ShareTokenValidatePassword(ctx context.Context, token string, password *string) (bool, error)
Search(ctx context.Context, query string, limitMedia *int, limitAlbums *int) (*models.SearchResult, error)
MyFaceGroups(ctx context.Context, paginate *models.Pagination) ([]*models.FaceGroup, error)
+ FaceGroup(ctx context.Context, id int) (*models.FaceGroup, error)
}
type ShareTokenResolver interface {
HasPassword(ctx context.Context, obj *models.ShareToken) (bool, error)
@@ -1073,6 +1075,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Query.Album(childComplexity, args["id"].(int)), true
+ case "Query.faceGroup":
+ if e.complexity.Query.FaceGroup == nil {
+ break
+ }
+
+ args, err := ec.field_Query_faceGroup_args(context.TODO(), rawArgs)
+ if err != nil {
+ return 0, false
+ }
+
+ return e.complexity.Query.FaceGroup(childComplexity, args["id"].(int)), true
+
case "Query.mapboxToken":
if e.complexity.Query.MapboxToken == nil {
break
@@ -1624,6 +1638,7 @@ type Query {
search(query: String!, limitMedia: Int, limitAlbums: Int): SearchResult!
myFaceGroups(paginate: Pagination): [FaceGroup!]!
+ faceGroup(id: ID!): FaceGroup!
}
type Mutation {
@@ -2479,6 +2494,21 @@ func (ec *executionContext) field_Query_album_args(ctx context.Context, rawArgs
return args, nil
}
+func (ec *executionContext) field_Query_faceGroup_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
+ var err error
+ args := map[string]interface{}{}
+ var arg0 int
+ if tmp, ok := rawArgs["id"]; ok {
+ ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("id"))
+ arg0, err = ec.unmarshalNID2int(ctx, tmp)
+ if err != nil {
+ return nil, err
+ }
+ }
+ args["id"] = arg0
+ return args, nil
+}
+
func (ec *executionContext) field_Query_mediaList_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
@@ -6567,6 +6597,48 @@ func (ec *executionContext) _Query_myFaceGroups(ctx context.Context, field graph
return ec.marshalNFaceGroup2ᚕᚖgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐFaceGroupᚄ(ctx, field.Selections, res)
}
+func (ec *executionContext) _Query_faceGroup(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ fc := &graphql.FieldContext{
+ Object: "Query",
+ Field: field,
+ Args: nil,
+ IsMethod: true,
+ IsResolver: true,
+ }
+
+ ctx = graphql.WithFieldContext(ctx, fc)
+ rawArgs := field.ArgumentMap(ec.Variables)
+ args, err := ec.field_Query_faceGroup_args(ctx, rawArgs)
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ fc.Args = args
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return ec.resolvers.Query().FaceGroup(rctx, args["id"].(int))
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ if !graphql.HasFieldError(ctx, fc) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ res := resTmp.(*models.FaceGroup)
+ fc.Result = res
+ return ec.marshalNFaceGroup2ᚖgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐFaceGroup(ctx, field.Selections, res)
+}
+
func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
@@ -10093,6 +10165,20 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
}
return res
})
+ case "faceGroup":
+ field := field
+ out.Concurrently(i, func() (res graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ }
+ }()
+ res = ec._Query_faceGroup(ctx, field)
+ if res == graphql.Null {
+ atomic.AddUint32(&invalids, 1)
+ }
+ return res
+ })
case "__type":
out.Values[i] = ec._Query___type(ctx, field)
case "__schema":
diff --git a/api/graphql/resolvers/faces.go b/api/graphql/resolvers/faces.go
index 6f863b6e..a712c023 100644
--- a/api/graphql/resolvers/faces.go
+++ b/api/graphql/resolvers/faces.go
@@ -63,6 +63,34 @@ func (r faceGroupResolver) ImageFaceCount(ctx context.Context, obj *models.FaceG
return int(count), nil
}
+func (r *queryResolver) FaceGroup(ctx context.Context, id int) (*models.FaceGroup, error) {
+ user := auth.UserFromContext(ctx)
+ if user == nil {
+ return nil, errors.New("unauthorized")
+ }
+
+ if err := user.FillAlbums(r.Database); err != nil {
+ return nil, err
+ }
+
+ userAlbumIDs := make([]int, len(user.Albums))
+ for i, album := range user.Albums {
+ userAlbumIDs[i] = album.ID
+ }
+
+ faceGroupQuery := r.Database.
+ Joins("LEFT JOIN image_faces ON image_faces.id = face_groups.id").
+ Where("face_groups.id = ?", id).
+ Where("image_faces.media_id IN (?)", r.Database.Select("media_id").Table("media").Where("media.album_id IN (?)", userAlbumIDs))
+
+ var faceGroup models.FaceGroup
+ if err := faceGroupQuery.Find(&faceGroup).Error; err != nil {
+ return nil, err
+ }
+
+ return &faceGroup, nil
+}
+
func (r *queryResolver) MyFaceGroups(ctx context.Context, paginate *models.Pagination) ([]*models.FaceGroup, error) {
user := auth.UserFromContext(ctx)
if user == nil {
@@ -80,7 +108,6 @@ func (r *queryResolver) MyFaceGroups(ctx context.Context, paginate *models.Pagin
faceGroupQuery := r.Database.
Joins("LEFT JOIN image_faces ON image_faces.id = face_groups.id").
- // Where("face_groups.id IN (?)", faceGroupIDs).
Where("image_faces.media_id IN (?)", r.Database.Select("media_id").Table("media").Where("media.album_id IN (?)", userAlbumIDs)).
Order("CASE WHEN label IS NULL THEN 1 ELSE 0 END")
@@ -91,10 +118,6 @@ func (r *queryResolver) MyFaceGroups(ctx context.Context, paginate *models.Pagin
return nil, err
}
- // for _, faceGroup := range faceGroups {
- // faceGroup.ImageFaces = faceGroupMap[faceGroup.ID]
- // }
-
return faceGroups, nil
}
diff --git a/api/graphql/schema.graphql b/api/graphql/schema.graphql
index 7a3d3816..6a845225 100644
--- a/api/graphql/schema.graphql
+++ b/api/graphql/schema.graphql
@@ -61,6 +61,7 @@ type Query {
search(query: String!, limitMedia: Int, limitAlbums: Int): SearchResult!
myFaceGroups(paginate: Pagination): [FaceGroup!]!
+ faceGroup(id: ID!): FaceGroup!
}
type Mutation {
diff --git a/ui/src/Pages/AlbumPage/AlbumPage.js b/ui/src/Pages/AlbumPage/AlbumPage.js
index 81a0279f..70f1d6ee 100644
--- a/ui/src/Pages/AlbumPage/AlbumPage.js
+++ b/ui/src/Pages/AlbumPage/AlbumPage.js
@@ -6,7 +6,7 @@ import PropTypes from 'prop-types'
import Layout from '../../Layout'
import useURLParameters from '../../hooks/useURLParameters'
import useScrollPagination from '../../hooks/useScrollPagination'
-import { Loader } from 'semantic-ui-react'
+import PaginateLoader from '../../components/PaginateLoader'
const albumQuery = gql`
query albumQuery(
@@ -140,13 +140,10 @@ function AlbumPage({ match }) {
setOrdering={setOrdering}
ordering={{ orderBy, orderDirection }}
/>
-