From 7ec57b293e21ed1262c9a7d5ee558d204eddf5a1 Mon Sep 17 00:00:00 2001 From: InfiniteStash <117855276+InfiniteStash@users.noreply.github.com> Date: Thu, 14 Jul 2022 08:10:27 +0000 Subject: [PATCH 1/8] WIP --- graphql/schema/schema.graphql | 5 +++++ graphql/schema/types/notifications.graphql | 12 ++++++++++++ pkg/database/database.go | 2 +- .../migrations/postgres/29_notifications.up.sql | 9 +++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 graphql/schema/types/notifications.graphql create mode 100644 pkg/database/migrations/postgres/29_notifications.up.sql diff --git a/graphql/schema/schema.graphql b/graphql/schema/schema.graphql index c1dfeef23..9569f8281 100644 --- a/graphql/schema/schema.graphql +++ b/graphql/schema/schema.graphql @@ -74,6 +74,8 @@ type Query { ### Instance Config ### getConfig: StashBoxConfig! + + getNotifications: Notifications! @hasRole(role: READ) } type Mutation { @@ -171,6 +173,9 @@ type Mutation { favoritePerformer(id: ID!, favorite: Boolean!): Boolean! @hasRole(role: READ) """Favorite or unfavorite a studio""" favoriteStudio(id: ID!, favorite: Boolean!): Boolean! @hasRole(role: READ) + + """Mark all of the current users notifications as read.""" + markNotificationsRead: Notifications! @hasRole(role: READ) } schema { diff --git a/graphql/schema/types/notifications.graphql b/graphql/schema/types/notifications.graphql new file mode 100644 index 000000000..447eb9317 --- /dev/null +++ b/graphql/schema/types/notifications.graphql @@ -0,0 +1,12 @@ +type Notification { + user: User! + edit: Edit! + type: NotificationType! + created: Time! + read: Boolean! +} + +type Notifications { + notifications: [Notification!]! + unreadCount: Int! +} diff --git a/pkg/database/database.go b/pkg/database/database.go index 52f6a8d72..edb8c99ec 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -4,7 +4,7 @@ import ( "github.com/jmoiron/sqlx" ) -var appSchemaVersion uint = 30 +var appSchemaVersion uint = 31 var databaseProviders map[string]databaseProvider diff --git a/pkg/database/migrations/postgres/29_notifications.up.sql b/pkg/database/migrations/postgres/29_notifications.up.sql new file mode 100644 index 000000000..339280009 --- /dev/null +++ b/pkg/database/migrations/postgres/29_notifications.up.sql @@ -0,0 +1,9 @@ +CREATE TABLE notifications ( + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + edit_id UUID REFERENCES edits(id) ON DELETE CASCADE, + type TEXT NOT NULL, + data JSONB, + created_at TIMESTAMP NOT NULL, + read_at TIMESTAMP NOT NULL +); +CREATE INDEX notifications_idx ON notifications (user_id, read_at); From 8e15cf08447cbc1e2e2f08bbe8ca864642782156 Mon Sep 17 00:00:00 2001 From: InfiniteStash <117855276+InfiniteStash@users.noreply.github.com> Date: Mon, 18 Jul 2022 16:57:01 +0000 Subject: [PATCH 2/8] WIP --- graphql/schema/types/notifications.graphql | 16 ++++++++- .../postgres/29_notifications.up.sql | 2 +- pkg/models/generated_models.go | 35 +++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/graphql/schema/types/notifications.graphql b/graphql/schema/types/notifications.graphql index 447eb9317..f17f7f2b2 100644 --- a/graphql/schema/types/notifications.graphql +++ b/graphql/schema/types/notifications.graphql @@ -1,7 +1,7 @@ type Notification { user: User! edit: Edit! - type: NotificationType! + data: NotificationData! created: Time! read: Boolean! } @@ -10,3 +10,17 @@ type Notifications { notifications: [Notification!]! unreadCount: Int! } + +type NegativeVoteNotification { + user: User! +} + +type CommentNotification { + comment: EditComment! +} + +type FailedVoteNotification { + message: String! +} + +union NotificationData = NegativeVoteNotification | CommentNotification | FailedVoteNotification diff --git a/pkg/database/migrations/postgres/29_notifications.up.sql b/pkg/database/migrations/postgres/29_notifications.up.sql index 339280009..53d6cbe6e 100644 --- a/pkg/database/migrations/postgres/29_notifications.up.sql +++ b/pkg/database/migrations/postgres/29_notifications.up.sql @@ -4,6 +4,6 @@ CREATE TABLE notifications ( type TEXT NOT NULL, data JSONB, created_at TIMESTAMP NOT NULL, - read_at TIMESTAMP NOT NULL + read_at TIMESTAMP ); CREATE INDEX notifications_idx ON notifications (user_id, read_at); diff --git a/pkg/models/generated_models.go b/pkg/models/generated_models.go index f1833a20c..7919a835e 100644 --- a/pkg/models/generated_models.go +++ b/pkg/models/generated_models.go @@ -24,6 +24,10 @@ type EditTarget interface { IsEditTarget() } +type NotificationData interface { + IsNotificationData() +} + type SceneDraftPerformer interface { IsSceneDraftPerformer() } @@ -62,6 +66,12 @@ type CancelEditInput struct { ID uuid.UUID `json:"id"` } +type CommentNotification struct { + Comment *EditComment `json:"comment"` +} + +func (CommentNotification) IsNotificationData() {} + type DateCriterionInput struct { Value string `json:"value"` Modifier CriterionModifier `json:"modifier"` @@ -135,6 +145,12 @@ type EyeColorCriterionInput struct { Modifier CriterionModifier `json:"modifier"` } +type FailedVoteNotification struct { + Message string `json:"message"` +} + +func (FailedVoteNotification) IsNotificationData() {} + type Fingerprint struct { Hash string `json:"hash"` Algorithm FingerprintAlgorithm `json:"algorithm"` @@ -230,11 +246,30 @@ type MultiStringCriterionInput struct { Modifier CriterionModifier `json:"modifier"` } +type NegativeVoteNotification struct { + User *User `json:"user"` +} + +func (NegativeVoteNotification) IsNotificationData() {} + type NewUserInput struct { Email string `json:"email"` InviteKey *string `json:"invite_key"` } +type Notification struct { + User *User `json:"user"` + Edit *Edit `json:"edit"` + Data NotificationData `json:"data"` + Created time.Time `json:"created"` + Read bool `json:"read"` +} + +type Notifications struct { + Notifications []*Notification `json:"notifications"` + UnreadCount int `json:"unreadCount"` +} + type PerformerAppearance struct { Performer *Performer `json:"performer"` // Performing as alias From 563e83765dd85add750665fb43c362335558f643 Mon Sep 17 00:00:00 2001 From: InfiniteStash <117855276+InfiniteStash@users.noreply.github.com> Date: Fri, 27 Oct 2023 20:48:38 +0000 Subject: [PATCH 3/8] WIP --- pkg/models/generated_exec.go | 1252 ++++++++++++++++++++++++++++++++-- 1 file changed, 1202 insertions(+), 50 deletions(-) diff --git a/pkg/models/generated_exec.go b/pkg/models/generated_exec.go index fa26086cb..412cc07bc 100644 --- a/pkg/models/generated_exec.go +++ b/pkg/models/generated_exec.go @@ -75,6 +75,10 @@ type ComplexityRoot struct { Location func(childComplexity int) int } + CommentNotification struct { + Comment func(childComplexity int) int + } + Draft struct { Created func(childComplexity int) int Data func(childComplexity int) int @@ -133,6 +137,10 @@ type ComplexityRoot struct { Vote func(childComplexity int) int } + FailedVoteNotification struct { + Message func(childComplexity int) int + } + Fingerprint struct { Algorithm func(childComplexity int) int Created func(childComplexity int) int @@ -163,56 +171,74 @@ type ComplexityRoot struct { } Mutation struct { - ActivateNewUser func(childComplexity int, input ActivateNewUserInput) int - ApplyEdit func(childComplexity int, input ApplyEditInput) int - CancelEdit func(childComplexity int, input CancelEditInput) int - ChangePassword func(childComplexity int, input UserChangePasswordInput) int - DestroyDraft func(childComplexity int, id uuid.UUID) int - EditComment func(childComplexity int, input EditCommentInput) int - EditVote func(childComplexity int, input EditVoteInput) int - FavoritePerformer func(childComplexity int, id uuid.UUID, favorite bool) int - FavoriteStudio func(childComplexity int, id uuid.UUID, favorite bool) int - GenerateInviteCode func(childComplexity int) int - GrantInvite func(childComplexity int, input GrantInviteInput) int - ImageCreate func(childComplexity int, input ImageCreateInput) int - ImageDestroy func(childComplexity int, input ImageDestroyInput) int - NewUser func(childComplexity int, input NewUserInput) int - PerformerCreate func(childComplexity int, input PerformerCreateInput) int - PerformerDestroy func(childComplexity int, input PerformerDestroyInput) int - PerformerEdit func(childComplexity int, input PerformerEditInput) int - PerformerEditUpdate func(childComplexity int, id uuid.UUID, input PerformerEditInput) int - PerformerUpdate func(childComplexity int, input PerformerUpdateInput) int - RegenerateAPIKey func(childComplexity int, userID *uuid.UUID) int - RescindInviteCode func(childComplexity int, code uuid.UUID) int - ResetPassword func(childComplexity int, input ResetPasswordInput) int - RevokeInvite func(childComplexity int, input RevokeInviteInput) int - SceneCreate func(childComplexity int, input SceneCreateInput) int - SceneDestroy func(childComplexity int, input SceneDestroyInput) int - SceneEdit func(childComplexity int, input SceneEditInput) int - SceneEditUpdate func(childComplexity int, id uuid.UUID, input SceneEditInput) int - SceneUpdate func(childComplexity int, input SceneUpdateInput) int - SiteCreate func(childComplexity int, input SiteCreateInput) int - SiteDestroy func(childComplexity int, input SiteDestroyInput) int - SiteUpdate func(childComplexity int, input SiteUpdateInput) int - StudioCreate func(childComplexity int, input StudioCreateInput) int - StudioDestroy func(childComplexity int, input StudioDestroyInput) int - StudioEdit func(childComplexity int, input StudioEditInput) int - StudioEditUpdate func(childComplexity int, id uuid.UUID, input StudioEditInput) int - StudioUpdate func(childComplexity int, input StudioUpdateInput) int - SubmitFingerprint func(childComplexity int, input FingerprintSubmission) int - SubmitPerformerDraft func(childComplexity int, input PerformerDraftInput) int - SubmitSceneDraft func(childComplexity int, input SceneDraftInput) int - TagCategoryCreate func(childComplexity int, input TagCategoryCreateInput) int - TagCategoryDestroy func(childComplexity int, input TagCategoryDestroyInput) int - TagCategoryUpdate func(childComplexity int, input TagCategoryUpdateInput) int - TagCreate func(childComplexity int, input TagCreateInput) int - TagDestroy func(childComplexity int, input TagDestroyInput) int - TagEdit func(childComplexity int, input TagEditInput) int - TagEditUpdate func(childComplexity int, id uuid.UUID, input TagEditInput) int - TagUpdate func(childComplexity int, input TagUpdateInput) int - UserCreate func(childComplexity int, input UserCreateInput) int - UserDestroy func(childComplexity int, input UserDestroyInput) int - UserUpdate func(childComplexity int, input UserUpdateInput) int + ActivateNewUser func(childComplexity int, input ActivateNewUserInput) int + ApplyEdit func(childComplexity int, input ApplyEditInput) int + CancelEdit func(childComplexity int, input CancelEditInput) int + ChangePassword func(childComplexity int, input UserChangePasswordInput) int + DestroyDraft func(childComplexity int, id uuid.UUID) int + EditComment func(childComplexity int, input EditCommentInput) int + EditVote func(childComplexity int, input EditVoteInput) int + FavoritePerformer func(childComplexity int, id uuid.UUID, favorite bool) int + FavoriteStudio func(childComplexity int, id uuid.UUID, favorite bool) int + GenerateInviteCode func(childComplexity int) int + GrantInvite func(childComplexity int, input GrantInviteInput) int + ImageCreate func(childComplexity int, input ImageCreateInput) int + ImageDestroy func(childComplexity int, input ImageDestroyInput) int + MarkNotificationsRead func(childComplexity int) int + NewUser func(childComplexity int, input NewUserInput) int + PerformerCreate func(childComplexity int, input PerformerCreateInput) int + PerformerDestroy func(childComplexity int, input PerformerDestroyInput) int + PerformerEdit func(childComplexity int, input PerformerEditInput) int + PerformerEditUpdate func(childComplexity int, id uuid.UUID, input PerformerEditInput) int + PerformerUpdate func(childComplexity int, input PerformerUpdateInput) int + RegenerateAPIKey func(childComplexity int, userID *uuid.UUID) int + RescindInviteCode func(childComplexity int, code uuid.UUID) int + ResetPassword func(childComplexity int, input ResetPasswordInput) int + RevokeInvite func(childComplexity int, input RevokeInviteInput) int + SceneCreate func(childComplexity int, input SceneCreateInput) int + SceneDestroy func(childComplexity int, input SceneDestroyInput) int + SceneEdit func(childComplexity int, input SceneEditInput) int + SceneEditUpdate func(childComplexity int, id uuid.UUID, input SceneEditInput) int + SceneUpdate func(childComplexity int, input SceneUpdateInput) int + SiteCreate func(childComplexity int, input SiteCreateInput) int + SiteDestroy func(childComplexity int, input SiteDestroyInput) int + SiteUpdate func(childComplexity int, input SiteUpdateInput) int + StudioCreate func(childComplexity int, input StudioCreateInput) int + StudioDestroy func(childComplexity int, input StudioDestroyInput) int + StudioEdit func(childComplexity int, input StudioEditInput) int + StudioEditUpdate func(childComplexity int, id uuid.UUID, input StudioEditInput) int + StudioUpdate func(childComplexity int, input StudioUpdateInput) int + SubmitFingerprint func(childComplexity int, input FingerprintSubmission) int + SubmitPerformerDraft func(childComplexity int, input PerformerDraftInput) int + SubmitSceneDraft func(childComplexity int, input SceneDraftInput) int + TagCategoryCreate func(childComplexity int, input TagCategoryCreateInput) int + TagCategoryDestroy func(childComplexity int, input TagCategoryDestroyInput) int + TagCategoryUpdate func(childComplexity int, input TagCategoryUpdateInput) int + TagCreate func(childComplexity int, input TagCreateInput) int + TagDestroy func(childComplexity int, input TagDestroyInput) int + TagEdit func(childComplexity int, input TagEditInput) int + TagEditUpdate func(childComplexity int, id uuid.UUID, input TagEditInput) int + TagUpdate func(childComplexity int, input TagUpdateInput) int + UserCreate func(childComplexity int, input UserCreateInput) int + UserDestroy func(childComplexity int, input UserDestroyInput) int + UserUpdate func(childComplexity int, input UserUpdateInput) int + } + + NegativeVoteNotification struct { + User func(childComplexity int) int + } + + Notification struct { + Created func(childComplexity int) int + Data func(childComplexity int) int + Edit func(childComplexity int) int + Read func(childComplexity int) int + User func(childComplexity int) int + } + + Notifications struct { + Notifications func(childComplexity int) int + UnreadCount func(childComplexity int) int } Performer struct { @@ -339,6 +365,7 @@ type ComplexityRoot struct { FindTagCategory func(childComplexity int, id uuid.UUID) int FindUser func(childComplexity int, id *uuid.UUID, username *string) int GetConfig func(childComplexity int) int + GetNotifications func(childComplexity int) int Me func(childComplexity int) int QueryEdits func(childComplexity int, input EditQueryInput) int QueryExistingScene func(childComplexity int, input QueryExistingSceneInput) int @@ -674,6 +701,7 @@ type MutationResolver interface { DestroyDraft(ctx context.Context, id uuid.UUID) (bool, error) FavoritePerformer(ctx context.Context, id uuid.UUID, favorite bool) (bool, error) FavoriteStudio(ctx context.Context, id uuid.UUID, favorite bool) (bool, error) + MarkNotificationsRead(ctx context.Context) (*Notifications, error) } type PerformerResolver interface { Disambiguation(ctx context.Context, obj *Performer) (*string, error) @@ -762,6 +790,7 @@ type QueryResolver interface { QueryExistingScene(ctx context.Context, input QueryExistingSceneInput) (*QueryExistingSceneResult, error) Version(ctx context.Context) (*Version, error) GetConfig(ctx context.Context) (*StashBoxConfig, error) + GetNotifications(ctx context.Context) (*Notifications, error) } type QueryEditsResultTypeResolver interface { Count(ctx context.Context, obj *EditQuery) (int, error) @@ -912,6 +941,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.BodyModification.Location(childComplexity), true + case "CommentNotification.comment": + if e.complexity.CommentNotification.Comment == nil { + break + } + + return e.complexity.CommentNotification.Comment(childComplexity), true + case "Draft.created": if e.complexity.Draft.Created == nil { break @@ -1171,6 +1207,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.EditVote.Vote(childComplexity), true + case "FailedVoteNotification.message": + if e.complexity.FailedVoteNotification.Message == nil { + break + } + + return e.complexity.FailedVoteNotification.Message(childComplexity), true + case "Fingerprint.algorithm": if e.complexity.Fingerprint.Algorithm == nil { break @@ -1441,6 +1484,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.ImageDestroy(childComplexity, args["input"].(ImageDestroyInput)), true + case "Mutation.markNotificationsRead": + if e.complexity.Mutation.MarkNotificationsRead == nil { + break + } + + return e.complexity.Mutation.MarkNotificationsRead(childComplexity), true + case "Mutation.newUser": if e.complexity.Mutation.NewUser == nil { break @@ -1885,6 +1935,62 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.UserUpdate(childComplexity, args["input"].(UserUpdateInput)), true + case "NegativeVoteNotification.user": + if e.complexity.NegativeVoteNotification.User == nil { + break + } + + return e.complexity.NegativeVoteNotification.User(childComplexity), true + + case "Notification.created": + if e.complexity.Notification.Created == nil { + break + } + + return e.complexity.Notification.Created(childComplexity), true + + case "Notification.data": + if e.complexity.Notification.Data == nil { + break + } + + return e.complexity.Notification.Data(childComplexity), true + + case "Notification.edit": + if e.complexity.Notification.Edit == nil { + break + } + + return e.complexity.Notification.Edit(childComplexity), true + + case "Notification.read": + if e.complexity.Notification.Read == nil { + break + } + + return e.complexity.Notification.Read(childComplexity), true + + case "Notification.user": + if e.complexity.Notification.User == nil { + break + } + + return e.complexity.Notification.User(childComplexity), true + + case "Notifications.notifications": + if e.complexity.Notifications.Notifications == nil { + break + } + + return e.complexity.Notifications.Notifications(childComplexity), true + + case "Notifications.unreadCount": + if e.complexity.Notifications.UnreadCount == nil { + break + } + + return e.complexity.Notifications.UnreadCount(childComplexity), true + case "Performer.age": if e.complexity.Performer.Age == nil { break @@ -2690,6 +2796,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.GetConfig(childComplexity), true + case "Query.getNotifications": + if e.complexity.Query.GetNotifications == nil { + break + } + + return e.complexity.Query.GetNotifications(childComplexity), true + case "Query.me": if e.complexity.Query.Me == nil { break @@ -4370,6 +4483,33 @@ input URLInput { url: String! site_id: ID! } +`, BuiltIn: false}, + {Name: "../../graphql/schema/types/notifications.graphql", Input: `type Notification { + user: User! + edit: Edit! + data: NotificationData! + created: Time! + read: Boolean! +} + +type Notifications { + notifications: [Notification!]! + unreadCount: Int! +} + +type NegativeVoteNotification { + user: User! +} + +type CommentNotification { + comment: EditComment! +} + +type FailedVoteNotification { + message: String! +} + +union NotificationData = NegativeVoteNotification | CommentNotification | FailedVoteNotification `, BuiltIn: false}, {Name: "../../graphql/schema/types/performer.graphql", Input: `enum GenderEnum { MALE @@ -5525,6 +5665,8 @@ type Query { ### Instance Config ### getConfig: StashBoxConfig! + + getNotifications: Notifications! @hasRole(role: READ) } type Mutation { @@ -5622,6 +5764,9 @@ type Mutation { favoritePerformer(id: ID!, favorite: Boolean!): Boolean! @hasRole(role: READ) """Favorite or unfavorite a studio""" favoriteStudio(id: ID!, favorite: Boolean!): Boolean! @hasRole(role: READ) + + """Mark all of the current users notifications as read.""" + markNotificationsRead: Notifications! @hasRole(role: READ) } schema { @@ -7022,6 +7167,60 @@ func (ec *executionContext) fieldContext_BodyModification_description(ctx contex return fc, nil } +func (ec *executionContext) _CommentNotification_comment(ctx context.Context, field graphql.CollectedField, obj *CommentNotification) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_CommentNotification_comment(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Comment, nil + }) + 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.(*EditComment) + fc.Result = res + return ec.marshalNEditComment2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐEditComment(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_CommentNotification_comment(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "CommentNotification", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_EditComment_id(ctx, field) + case "user": + return ec.fieldContext_EditComment_user(ctx, field) + case "date": + return ec.fieldContext_EditComment_date(ctx, field) + case "comment": + return ec.fieldContext_EditComment_comment(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type EditComment", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _Draft_id(ctx context.Context, field graphql.CollectedField, obj *Draft) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Draft_id(ctx, field) if err != nil { @@ -8710,6 +8909,50 @@ func (ec *executionContext) fieldContext_EditVote_vote(ctx context.Context, fiel return fc, nil } +func (ec *executionContext) _FailedVoteNotification_message(ctx context.Context, field graphql.CollectedField, obj *FailedVoteNotification) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_FailedVoteNotification_message(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Message, nil + }) + 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.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_FailedVoteNotification_message(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "FailedVoteNotification", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _Fingerprint_hash(ctx context.Context, field graphql.CollectedField, obj *Fingerprint) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Fingerprint_hash(ctx, field) if err != nil { @@ -14023,6 +14266,533 @@ func (ec *executionContext) fieldContext_Mutation_favoriteStudio(ctx context.Con return fc, nil } +func (ec *executionContext) _Mutation_markNotificationsRead(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_markNotificationsRead(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().MarkNotificationsRead(rctx) + } + directive1 := func(ctx context.Context) (interface{}, error) { + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐRoleEnum(ctx, "READ") + if err != nil { + return nil, err + } + if ec.directives.HasRole == nil { + return nil, errors.New("directive hasRole is not implemented") + } + return ec.directives.HasRole(ctx, nil, directive0, role) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(*Notifications); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/stashapp/stash-box/pkg/models.Notifications`, tmp) + }) + if err != nil { + ec.Error(ctx, err) + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*Notifications) + fc.Result = res + return ec.marshalNNotifications2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐNotifications(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_markNotificationsRead(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "notifications": + return ec.fieldContext_Notifications_notifications(ctx, field) + case "unreadCount": + return ec.fieldContext_Notifications_unreadCount(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Notifications", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _NegativeVoteNotification_user(ctx context.Context, field graphql.CollectedField, obj *NegativeVoteNotification) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_NegativeVoteNotification_user(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.User, nil + }) + 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.(*User) + fc.Result = res + return ec.marshalNUser2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐUser(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_NegativeVoteNotification_user(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "NegativeVoteNotification", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_User_id(ctx, field) + case "name": + return ec.fieldContext_User_name(ctx, field) + case "roles": + return ec.fieldContext_User_roles(ctx, field) + case "email": + return ec.fieldContext_User_email(ctx, field) + case "api_key": + return ec.fieldContext_User_api_key(ctx, field) + case "vote_count": + return ec.fieldContext_User_vote_count(ctx, field) + case "edit_count": + return ec.fieldContext_User_edit_count(ctx, field) + case "api_calls": + return ec.fieldContext_User_api_calls(ctx, field) + case "invited_by": + return ec.fieldContext_User_invited_by(ctx, field) + case "invite_tokens": + return ec.fieldContext_User_invite_tokens(ctx, field) + case "active_invite_codes": + return ec.fieldContext_User_active_invite_codes(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type User", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Notification_user(ctx context.Context, field graphql.CollectedField, obj *Notification) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Notification_user(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.User, nil + }) + 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.(*User) + fc.Result = res + return ec.marshalNUser2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐUser(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Notification_user(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Notification", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_User_id(ctx, field) + case "name": + return ec.fieldContext_User_name(ctx, field) + case "roles": + return ec.fieldContext_User_roles(ctx, field) + case "email": + return ec.fieldContext_User_email(ctx, field) + case "api_key": + return ec.fieldContext_User_api_key(ctx, field) + case "vote_count": + return ec.fieldContext_User_vote_count(ctx, field) + case "edit_count": + return ec.fieldContext_User_edit_count(ctx, field) + case "api_calls": + return ec.fieldContext_User_api_calls(ctx, field) + case "invited_by": + return ec.fieldContext_User_invited_by(ctx, field) + case "invite_tokens": + return ec.fieldContext_User_invite_tokens(ctx, field) + case "active_invite_codes": + return ec.fieldContext_User_active_invite_codes(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type User", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Notification_edit(ctx context.Context, field graphql.CollectedField, obj *Notification) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Notification_edit(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Edit, nil + }) + 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.(*Edit) + fc.Result = res + return ec.marshalNEdit2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐEdit(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Notification_edit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Notification", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Edit_id(ctx, field) + case "user": + return ec.fieldContext_Edit_user(ctx, field) + case "target": + return ec.fieldContext_Edit_target(ctx, field) + case "target_type": + return ec.fieldContext_Edit_target_type(ctx, field) + case "merge_sources": + return ec.fieldContext_Edit_merge_sources(ctx, field) + case "operation": + return ec.fieldContext_Edit_operation(ctx, field) + case "bot": + return ec.fieldContext_Edit_bot(ctx, field) + case "details": + return ec.fieldContext_Edit_details(ctx, field) + case "old_details": + return ec.fieldContext_Edit_old_details(ctx, field) + case "options": + return ec.fieldContext_Edit_options(ctx, field) + case "comments": + return ec.fieldContext_Edit_comments(ctx, field) + case "votes": + return ec.fieldContext_Edit_votes(ctx, field) + case "vote_count": + return ec.fieldContext_Edit_vote_count(ctx, field) + case "destructive": + return ec.fieldContext_Edit_destructive(ctx, field) + case "status": + return ec.fieldContext_Edit_status(ctx, field) + case "applied": + return ec.fieldContext_Edit_applied(ctx, field) + case "created": + return ec.fieldContext_Edit_created(ctx, field) + case "updated": + return ec.fieldContext_Edit_updated(ctx, field) + case "closed": + return ec.fieldContext_Edit_closed(ctx, field) + case "expires": + return ec.fieldContext_Edit_expires(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Edit", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Notification_data(ctx context.Context, field graphql.CollectedField, obj *Notification) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Notification_data(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Data, nil + }) + 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.(NotificationData) + fc.Result = res + return ec.marshalNNotificationData2githubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐNotificationData(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Notification_data(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Notification", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type NotificationData does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Notification_created(ctx context.Context, field graphql.CollectedField, obj *Notification) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Notification_created(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Created, nil + }) + 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.(time.Time) + fc.Result = res + return ec.marshalNTime2timeᚐTime(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Notification_created(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Notification", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Time does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Notification_read(ctx context.Context, field graphql.CollectedField, obj *Notification) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Notification_read(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Read, nil + }) + 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.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Notification_read(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Notification", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _Notifications_notifications(ctx context.Context, field graphql.CollectedField, obj *Notifications) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Notifications_notifications(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Notifications, nil + }) + 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.([]*Notification) + fc.Result = res + return ec.marshalNNotification2ᚕᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐNotificationᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Notifications_notifications(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Notifications", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "user": + return ec.fieldContext_Notification_user(ctx, field) + case "edit": + return ec.fieldContext_Notification_edit(ctx, field) + case "data": + return ec.fieldContext_Notification_data(ctx, field) + case "created": + return ec.fieldContext_Notification_created(ctx, field) + case "read": + return ec.fieldContext_Notification_read(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Notification", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _Notifications_unreadCount(ctx context.Context, field graphql.CollectedField, obj *Notifications) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Notifications_unreadCount(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.UnreadCount, nil + }) + 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.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Notifications_unreadCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Notifications", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _Performer_id(ctx context.Context, field graphql.CollectedField, obj *Performer) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Performer_id(ctx, field) if err != nil { @@ -20900,6 +21670,79 @@ func (ec *executionContext) fieldContext_Query_getConfig(ctx context.Context, fi return fc, nil } +func (ec *executionContext) _Query_getNotifications(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_getNotifications(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().GetNotifications(rctx) + } + directive1 := func(ctx context.Context) (interface{}, error) { + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐRoleEnum(ctx, "READ") + if err != nil { + return nil, err + } + if ec.directives.HasRole == nil { + return nil, errors.New("directive hasRole is not implemented") + } + return ec.directives.HasRole(ctx, nil, directive0, role) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(*Notifications); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/stashapp/stash-box/pkg/models.Notifications`, tmp) + }) + if err != nil { + ec.Error(ctx, err) + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*Notifications) + fc.Result = res + return ec.marshalNNotifications2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐNotifications(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_getNotifications(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "notifications": + return ec.fieldContext_Notifications_notifications(ctx, field) + case "unreadCount": + return ec.fieldContext_Notifications_unreadCount(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Notifications", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Query___type(ctx, field) if err != nil { @@ -35278,6 +36121,36 @@ func (ec *executionContext) _EditTarget(ctx context.Context, sel ast.SelectionSe } } +func (ec *executionContext) _NotificationData(ctx context.Context, sel ast.SelectionSet, obj NotificationData) graphql.Marshaler { + switch obj := (obj).(type) { + case nil: + return graphql.Null + case NegativeVoteNotification: + return ec._NegativeVoteNotification(ctx, sel, &obj) + case *NegativeVoteNotification: + if obj == nil { + return graphql.Null + } + return ec._NegativeVoteNotification(ctx, sel, obj) + case CommentNotification: + return ec._CommentNotification(ctx, sel, &obj) + case *CommentNotification: + if obj == nil { + return graphql.Null + } + return ec._CommentNotification(ctx, sel, obj) + case FailedVoteNotification: + return ec._FailedVoteNotification(ctx, sel, &obj) + case *FailedVoteNotification: + if obj == nil { + return graphql.Null + } + return ec._FailedVoteNotification(ctx, sel, obj) + default: + panic(fmt.Errorf("unexpected type %T", obj)) + } +} + func (ec *executionContext) _SceneDraftPerformer(ctx context.Context, sel ast.SelectionSet, obj SceneDraftPerformer) graphql.Marshaler { switch obj := (obj).(type) { case nil: @@ -35383,6 +36256,34 @@ func (ec *executionContext) _BodyModification(ctx context.Context, sel ast.Selec return out } +var commentNotificationImplementors = []string{"CommentNotification", "NotificationData"} + +func (ec *executionContext) _CommentNotification(ctx context.Context, sel ast.SelectionSet, obj *CommentNotification) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, commentNotificationImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("CommentNotification") + case "comment": + + out.Values[i] = ec._CommentNotification_comment(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + var draftImplementors = []string{"Draft"} func (ec *executionContext) _Draft(ctx context.Context, sel ast.SelectionSet, obj *Draft) graphql.Marshaler { @@ -36078,6 +36979,34 @@ func (ec *executionContext) _EditVote(ctx context.Context, sel ast.SelectionSet, return out } +var failedVoteNotificationImplementors = []string{"FailedVoteNotification", "NotificationData"} + +func (ec *executionContext) _FailedVoteNotification(ctx context.Context, sel ast.SelectionSet, obj *FailedVoteNotification) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, failedVoteNotificationImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("FailedVoteNotification") + case "message": + + out.Values[i] = ec._FailedVoteNotification_message(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + var fingerprintImplementors = []string{"Fingerprint"} func (ec *executionContext) _Fingerprint(ctx context.Context, sel ast.SelectionSet, obj *Fingerprint) graphql.Marshaler { @@ -36600,11 +37529,136 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) return ec._Mutation_favoriteStudio(ctx, field) }) + case "markNotificationsRead": + + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_markNotificationsRead(ctx, field) + }) + + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + return out +} + +var negativeVoteNotificationImplementors = []string{"NegativeVoteNotification", "NotificationData"} + +func (ec *executionContext) _NegativeVoteNotification(ctx context.Context, sel ast.SelectionSet, obj *NegativeVoteNotification) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, negativeVoteNotificationImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("NegativeVoteNotification") + case "user": + + out.Values[i] = ec._NegativeVoteNotification_user(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var notificationImplementors = []string{"Notification"} + +func (ec *executionContext) _Notification(ctx context.Context, sel ast.SelectionSet, obj *Notification) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, notificationImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Notification") + case "user": + + out.Values[i] = ec._Notification_user(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + case "edit": + + out.Values[i] = ec._Notification_edit(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + case "data": + + out.Values[i] = ec._Notification_data(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + case "created": + + out.Values[i] = ec._Notification_created(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + case "read": + + out.Values[i] = ec._Notification_read(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } default: panic("unknown field " + strconv.Quote(field.Name)) } } out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var notificationsImplementors = []string{"Notifications"} + +func (ec *executionContext) _Notifications(ctx context.Context, sel ast.SelectionSet, obj *Notifications) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, notificationsImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Notifications") + case "notifications": + + out.Values[i] = ec._Notifications_notifications(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + case "unreadCount": + + out.Values[i] = ec._Notifications_unreadCount(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } return out } @@ -38352,6 +39406,26 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) } + out.Concurrently(i, func() graphql.Marshaler { + return rrm(innerCtx) + }) + case "getNotifications": + field := field + + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_getNotifications(ctx, field) + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + } + out.Concurrently(i, func() graphql.Marshaler { return rrm(innerCtx) }) @@ -42003,6 +43077,84 @@ func (ec *executionContext) unmarshalNNewUserInput2githubᚗcomᚋstashappᚋsta return res, graphql.ErrorOnPath(ctx, err) } +func (ec *executionContext) marshalNNotification2ᚕᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐNotificationᚄ(ctx context.Context, sel ast.SelectionSet, v []*Notification) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNNotification2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐNotification(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNNotification2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐNotification(ctx context.Context, sel ast.SelectionSet, v *Notification) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._Notification(ctx, sel, v) +} + +func (ec *executionContext) marshalNNotificationData2githubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐNotificationData(ctx context.Context, sel ast.SelectionSet, v NotificationData) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._NotificationData(ctx, sel, v) +} + +func (ec *executionContext) marshalNNotifications2githubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐNotifications(ctx context.Context, sel ast.SelectionSet, v Notifications) graphql.Marshaler { + return ec._Notifications(ctx, sel, &v) +} + +func (ec *executionContext) marshalNNotifications2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐNotifications(ctx context.Context, sel ast.SelectionSet, v *Notifications) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._Notifications(ctx, sel, v) +} + func (ec *executionContext) unmarshalNOperationEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐOperationEnum(ctx context.Context, v interface{}) (OperationEnum, error) { var res OperationEnum err := res.UnmarshalGQL(v) From 56eb54354df6afb1ca10c38c00f84447b77a27de Mon Sep 17 00:00:00 2001 From: InfiniteStash <117855276+InfiniteStash@users.noreply.github.com> Date: Tue, 19 Dec 2023 07:48:50 +0000 Subject: [PATCH 4/8] WIP --- Makefile | 5 +- frontend/src/graphql/types.ts | 83 + graphql/schema/schema.graphql | 5 +- graphql/schema/types/notifications.graphql | 60 +- pkg/api/resolver.go | 4 + pkg/api/resolver_model_notification.go | 82 + pkg/api/resolver_mutation_edit.go | 58 +- pkg/api/resolver_mutation_notifications.go | 9 + pkg/api/resolver_query_find_scene.go | 4 +- pkg/api/resolver_query_notifications.go | 21 + .../postgres/29_notifications.up.sql | 9 - .../postgres/31_notifications.up.sql | 31 + pkg/dataloader/boolsloader_gen.go | 221 ++ pkg/dataloader/editcommentloader_gen.go | 225 ++ pkg/dataloader/editloader_gen.go | 225 ++ pkg/dataloader/loaders.go | 27 + pkg/dataloader/sceneloader_gen.go | 225 ++ pkg/manager/edit/edit.go | 9 + pkg/manager/notifications/notifications.go | 56 + pkg/models/edit.go | 2 + pkg/models/factory.go | 2 + pkg/models/generated_exec.go | 2064 ++++++++++++----- pkg/models/generated_models.go | 78 +- pkg/models/model_notification.go | 43 + pkg/models/notification.go | 17 + pkg/models/scene.go | 2 +- pkg/sqlx/factory.go | 4 + pkg/sqlx/querybuilder_edit.go | 42 + pkg/sqlx/querybuilder_notifications.go | 195 ++ pkg/sqlx/querybuilder_scene.go | 4 +- 30 files changed, 3116 insertions(+), 696 deletions(-) create mode 100644 pkg/api/resolver_model_notification.go create mode 100644 pkg/api/resolver_mutation_notifications.go create mode 100644 pkg/api/resolver_query_notifications.go delete mode 100644 pkg/database/migrations/postgres/29_notifications.up.sql create mode 100644 pkg/database/migrations/postgres/31_notifications.up.sql create mode 100644 pkg/dataloader/boolsloader_gen.go create mode 100644 pkg/dataloader/editcommentloader_gen.go create mode 100644 pkg/dataloader/editloader_gen.go create mode 100644 pkg/dataloader/sceneloader_gen.go create mode 100644 pkg/manager/notifications/notifications.go create mode 100644 pkg/models/model_notification.go create mode 100644 pkg/models/notification.go create mode 100644 pkg/sqlx/querybuilder_notifications.go diff --git a/Makefile b/Makefile index 74d5b3b08..4699996ed 100644 --- a/Makefile +++ b/Makefile @@ -74,7 +74,10 @@ generate-dataloaders: go run github.com/vektah/dataloaden BodyModificationsLoader github.com/gofrs/uuid.UUID "[]*github.com/stashapp/stash-box/pkg/models.BodyModification"; \ go run github.com/vektah/dataloaden TagCategoryLoader github.com/gofrs/uuid.UUID "*github.com/stashapp/stash-box/pkg/models.TagCategory"; \ go run github.com/vektah/dataloaden SiteLoader github.com/gofrs/uuid.UUID "*github.com/stashapp/stash-box/pkg/models.Site"; \ - go run github.com/vektah/dataloaden StudioLoader github.com/gofrs/uuid.UUID "*github.com/stashapp/stash-box/pkg/models.Studio"; + go run github.com/vektah/dataloaden StudioLoader github.com/gofrs/uuid.UUID "*github.com/stashapp/stash-box/pkg/models.Studio"; \ + go run github.com/vektah/dataloaden EditLoader github.com/gofrs/uuid.UUID "*github.com/stashapp/stash-box/pkg/models.Edit"; \ + go run github.com/vektah/dataloaden EditCommentLoader github.com/gofrs/uuid.UUID "*github.com/stashapp/stash-box/pkg/models.EditComment"; \ + go run github.com/vektah/dataloaden SceneLoader github.com/gofrs/uuid.UUID "*github.com/stashapp/stash-box/pkg/models.Scene"; test: go test ./... diff --git a/frontend/src/graphql/types.ts b/frontend/src/graphql/types.ts index 72f836bea..322e82529 100644 --- a/frontend/src/graphql/types.ts +++ b/frontend/src/graphql/types.ts @@ -66,6 +66,21 @@ export type CancelEditInput = { id: Scalars["ID"]; }; +export type CommentCommentedEdit = { + __typename: "CommentCommentedEdit"; + comment: EditComment; +}; + +export type CommentOwnEdit = { + __typename: "CommentOwnEdit"; + comment: EditComment; +}; + +export type CommentVotedEdit = { + __typename: "CommentVotedEdit"; + comment: EditComment; +}; + export enum CriterionModifier { /** = */ EQUALS = "EQUALS", @@ -96,6 +111,11 @@ export type DateCriterionInput = { value: Scalars["Date"]; }; +export type DownvoteOwnEdit = { + __typename: "DownvoteOwnEdit"; + edit: Edit; +}; + export type Draft = { __typename: "Draft"; created: Scalars["Time"]; @@ -269,12 +289,37 @@ export enum EyeColorEnum { RED = "RED", } +export type FailedOwnEdit = { + __typename: "FailedOwnEdit"; + edit: Edit; +}; + export enum FavoriteFilter { ALL = "ALL", PERFORMER = "PERFORMER", STUDIO = "STUDIO", } +export type FavoritePerformerEdit = { + __typename: "FavoritePerformerEdit"; + edit: Edit; +}; + +export type FavoritePerformerScene = { + __typename: "FavoritePerformerScene"; + scene: Scene; +}; + +export type FavoriteStudioEdit = { + __typename: "FavoriteStudioEdit"; + edit: Edit; +}; + +export type FavoriteStudioScene = { + __typename: "FavoriteStudioScene"; + scene: Scene; +}; + export type Fingerprint = { __typename: "Fingerprint"; algorithm: FingerprintAlgorithm; @@ -444,6 +489,8 @@ export type Mutation = { grantInvite: Scalars["Int"]; imageCreate?: Maybe; imageDestroy: Scalars["Boolean"]; + /** Mark all of the current users notifications as read. */ + markNotificationsRead: Scalars["Boolean"]; /** User interface for registering */ newUser?: Maybe; performerCreate?: Maybe; @@ -705,6 +752,30 @@ export type NewUserInput = { invite_key?: InputMaybe; }; +export type Notification = { + __typename: "Notification"; + created: Scalars["Time"]; + data: NotificationData; + read: Scalars["Boolean"]; +}; + +export type NotificationData = + | CommentCommentedEdit + | CommentOwnEdit + | CommentVotedEdit + | DownvoteOwnEdit + | FailedOwnEdit + | FavoritePerformerEdit + | FavoritePerformerScene + | FavoriteStudioEdit + | FavoriteStudioScene + | UpdatedEdit; + +export type NotificationQueryInput = { + page?: Scalars["Int"]; + per_page?: Scalars["Int"]; +}; + export enum OperationEnum { CREATE = "CREATE", DESTROY = "DESTROY", @@ -1049,10 +1120,12 @@ export type Query = { /** Find user by ID or username */ findUser?: Maybe; getConfig: StashBoxConfig; + getUnreadNotificationCount: Scalars["Int"]; /** Returns currently authenticated user */ me?: Maybe; queryEdits: QueryEditsResultType; queryExistingScene: QueryExistingSceneResult; + queryNotifications: Array; queryPerformers: QueryPerformersResultType; queryScenes: QueryScenesResultType; querySites: QuerySitesResultType; @@ -1144,6 +1217,11 @@ export type QueryQueryExistingSceneArgs = { input: QueryExistingSceneInput; }; +/** The query root for this schema */ +export type QueryQueryNotificationsArgs = { + input: NotificationQueryInput; +}; + /** The query root for this schema */ export type QueryQueryPerformersArgs = { input: PerformerQueryInput; @@ -1732,6 +1810,11 @@ export type UrlInput = { url: Scalars["String"]; }; +export type UpdatedEdit = { + __typename: "UpdatedEdit"; + edit: Edit; +}; + export type User = { __typename: "User"; active_invite_codes?: Maybe>; diff --git a/graphql/schema/schema.graphql b/graphql/schema/schema.graphql index 9569f8281..4112d153f 100644 --- a/graphql/schema/schema.graphql +++ b/graphql/schema/schema.graphql @@ -75,7 +75,8 @@ type Query { ### Instance Config ### getConfig: StashBoxConfig! - getNotifications: Notifications! @hasRole(role: READ) + queryNotifications(input: NotificationQueryInput!): [Notification!]! @hasRole(role: READ) + getUnreadNotificationCount: Int! @hasRole(role: READ) } type Mutation { @@ -175,7 +176,7 @@ type Mutation { favoriteStudio(id: ID!, favorite: Boolean!): Boolean! @hasRole(role: READ) """Mark all of the current users notifications as read.""" - markNotificationsRead: Notifications! @hasRole(role: READ) + markNotificationsRead: Boolean! @hasRole(role: READ) } schema { diff --git a/graphql/schema/types/notifications.graphql b/graphql/schema/types/notifications.graphql index f17f7f2b2..dae9af18d 100644 --- a/graphql/schema/types/notifications.graphql +++ b/graphql/schema/types/notifications.graphql @@ -1,26 +1,62 @@ type Notification { - user: User! - edit: Edit! - data: NotificationData! created: Time! read: Boolean! + data: NotificationData! +} + +union NotificationData = + | FavoritePerformerScene + | FavoritePerformerEdit + | FavoriteStudioScene + | FavoriteStudioEdit + | CommentOwnEdit + | CommentCommentedEdit + | CommentVotedEdit + | DownvoteOwnEdit + | FailedOwnEdit + | UpdatedEdit + +type FavoritePerformerScene { + scene: Scene! +} + +type FavoritePerformerEdit { + edit: Edit! +} + +type FavoriteStudioScene { + scene: Scene! +} + +type FavoriteStudioEdit { + edit: Edit! +} + +type CommentOwnEdit { + comment: EditComment! +} + +type DownvoteOwnEdit { + edit: Edit! } -type Notifications { - notifications: [Notification!]! - unreadCount: Int! +type FailedOwnEdit { + edit: Edit! } -type NegativeVoteNotification { - user: User! +type CommentCommentedEdit { + comment: EditComment! } -type CommentNotification { +type CommentVotedEdit { comment: EditComment! } -type FailedVoteNotification { - message: String! +type UpdatedEdit { + edit: Edit! } -union NotificationData = NegativeVoteNotification | CommentNotification | FailedVoteNotification +input NotificationQueryInput { + page: Int! = 1 + per_page: Int! = 25 +} diff --git a/pkg/api/resolver.go b/pkg/api/resolver.go index 1ed749cfa..1a79372ed 100644 --- a/pkg/api/resolver.go +++ b/pkg/api/resolver.go @@ -93,6 +93,10 @@ func (r *Resolver) QueryExistingSceneResult() models.QueryExistingSceneResultRes return &queryExistingSceneResolver{r} } +func (r *Resolver) Notification() models.NotificationResolver { + return ¬ificationResolver{r} +} + type mutationResolver struct{ *Resolver } type queryResolver struct{ *Resolver } diff --git a/pkg/api/resolver_model_notification.go b/pkg/api/resolver_model_notification.go new file mode 100644 index 000000000..c9694d665 --- /dev/null +++ b/pkg/api/resolver_model_notification.go @@ -0,0 +1,82 @@ +package api + +import ( + "context" + "time" + + "github.com/stashapp/stash-box/pkg/dataloader" + "github.com/stashapp/stash-box/pkg/models" +) + +type notificationResolver struct{ *Resolver } + +func (r *notificationResolver) Created(ctx context.Context, obj *models.Notification) (*time.Time, error) { + return &obj.CreatedAt, nil +} + +func (r *notificationResolver) Read(ctx context.Context, obj *models.Notification) (bool, error) { + return obj.ReadAt.Valid, nil +} + +func (r *notificationResolver) Data(ctx context.Context, obj *models.Notification) (models.NotificationData, error) { + switch obj.Type { + case models.NotificationEnumCommentCommentedEdit: + fallthrough + case models.NotificationEnumCommentOwnEdit: + fallthrough + case models.NotificationEnumCommentVotedEdit: + comment, err := dataloader.For(ctx).EditCommentById.Load(obj.TargetID) + if err != nil { + return nil, err + } + + if obj.Type == models.NotificationEnumCommentCommentedEdit { + return &models.CommentCommentedEdit{Comment: comment}, nil + } else if obj.Type == models.NotificationEnumCommentOwnEdit { + return &models.CommentOwnEdit{Comment: comment}, nil + } else { + return &models.CommentVotedEdit{Comment: comment}, nil + } + + case models.NotificationEnumFavoritePerformerScene: + fallthrough + case models.NotificationEnumFavoriteStudioScene: + scene, err := dataloader.For(ctx).SceneById.Load(obj.TargetID) + if err != nil { + return nil, err + } + + if obj.Type == models.NotificationEnumFavoritePerformerScene { + return &models.FavoritePerformerScene{Scene: scene}, nil + } else { + return &models.FavoriteStudioScene{Scene: scene}, nil + } + + case models.NotificationEnumFavoritePerformerEdit: + fallthrough + case models.NotificationEnumFavoriteStudioEdit: + fallthrough + case models.NotificationEnumDownvoteOwnEdit: + fallthrough + case models.NotificationEnumFailedOwnEdit: + fallthrough + case models.NotificationEnumUpdatedEdit: + edit, err := dataloader.For(ctx).EditById.Load(obj.TargetID) + if err != nil { + return nil, err + } + + if obj.Type == models.NotificationEnumFavoritePerformerEdit { + return &models.FavoritePerformerEdit{Edit: edit}, nil + } else if obj.Type == models.NotificationEnumFavoriteStudioEdit { + return &models.FavoriteStudioEdit{Edit: edit}, nil + } else if obj.Type == models.NotificationEnumDownvoteOwnEdit { + return &models.DownvoteOwnEdit{Edit: edit}, nil + } else if obj.Type == models.NotificationEnumFailedOwnEdit { + return &models.FailedOwnEdit{Edit: edit}, nil + } else if obj.Type == models.NotificationEnumUpdatedEdit { + return &models.UpdatedEdit{Edit: edit}, nil + } + } + return nil, nil +} diff --git a/pkg/api/resolver_mutation_edit.go b/pkg/api/resolver_mutation_edit.go index 3acbc9c8a..889e9e7df 100644 --- a/pkg/api/resolver_mutation_edit.go +++ b/pkg/api/resolver_mutation_edit.go @@ -7,6 +7,7 @@ import ( "github.com/gofrs/uuid" "github.com/stashapp/stash-box/pkg/manager/edit" + "github.com/stashapp/stash-box/pkg/manager/notifications" "github.com/stashapp/stash-box/pkg/models" "github.com/stashapp/stash-box/pkg/user" "github.com/stashapp/stash-box/pkg/utils" @@ -57,11 +58,11 @@ func (r *mutationResolver) SceneEdit(ctx context.Context, input models.SceneEdit return p.CreateComment(currentUser, input.Edit.Comment) }) - if err != nil { - return nil, err + if err == nil { + go notifications.OnCreateEdit(fac, newEdit) } - return newEdit, nil + return newEdit, err } func (r *mutationResolver) SceneEditUpdate(ctx context.Context, id uuid.UUID, input models.SceneEditInput) (*models.Edit, error) { @@ -97,6 +98,10 @@ func (r *mutationResolver) SceneEditUpdate(ctx context.Context, id uuid.UUID, in return p.CreateComment(currentUser, input.Edit.Comment) }) + if err == nil { + go notifications.OnUpdateEdit(fac, existingEdit) + } + return existingEdit, err } @@ -134,11 +139,11 @@ func (r *mutationResolver) StudioEdit(ctx context.Context, input models.StudioEd return p.CreateComment(currentUser, input.Edit.Comment) }) - if err != nil { - return nil, err + if err == nil { + go notifications.OnCreateEdit(fac, newEdit) } - return newEdit, nil + return newEdit, err } func (r *mutationResolver) StudioEditUpdate(ctx context.Context, id uuid.UUID, input models.StudioEditInput) (*models.Edit, error) { @@ -174,6 +179,10 @@ func (r *mutationResolver) StudioEditUpdate(ctx context.Context, id uuid.UUID, i return p.CreateComment(currentUser, input.Edit.Comment) }) + if err == nil { + go notifications.OnUpdateEdit(fac, existingEdit) + } + return existingEdit, err } @@ -211,11 +220,11 @@ func (r *mutationResolver) TagEdit(ctx context.Context, input models.TagEditInpu return p.CreateComment(currentUser, input.Edit.Comment) }) - if err != nil { - return nil, err + if err == nil { + go notifications.OnCreateEdit(fac, newEdit) } - return newEdit, nil + return newEdit, err } func (r *mutationResolver) TagEditUpdate(ctx context.Context, id uuid.UUID, input models.TagEditInput) (*models.Edit, error) { @@ -251,6 +260,10 @@ func (r *mutationResolver) TagEditUpdate(ctx context.Context, id uuid.UUID, inpu return p.CreateComment(currentUser, input.Edit.Comment) }) + if err == nil { + go notifications.OnUpdateEdit(fac, existingEdit) + } + return existingEdit, err } @@ -294,11 +307,11 @@ func (r *mutationResolver) PerformerEdit(ctx context.Context, input models.Perfo return p.CreateComment(currentUser, input.Edit.Comment) }) - if err != nil { - return nil, err + if err == nil { + go notifications.OnCreateEdit(fac, newEdit) } - return newEdit, nil + return newEdit, err } func (r *mutationResolver) PerformerEditUpdate(ctx context.Context, id uuid.UUID, input models.PerformerEditInput) (*models.Edit, error) { @@ -334,6 +347,10 @@ func (r *mutationResolver) PerformerEditUpdate(ctx context.Context, id uuid.UUID return p.CreateComment(currentUser, input.Edit.Comment) }) + if err == nil { + go notifications.OnUpdateEdit(fac, existingEdit) + } + return existingEdit, err } @@ -380,17 +397,18 @@ func (r *mutationResolver) EditVote(ctx context.Context, input models.EditVoteIn return err }) - if err != nil { - return nil, err + if err == nil && input.Vote == models.VoteTypeEnumReject { + go notifications.OnEditDownvote(fac, voteEdit) } - return voteEdit, nil + return voteEdit, err } func (r *mutationResolver) EditComment(ctx context.Context, input models.EditCommentInput) (*models.Edit, error) { fac := r.getRepoFactory(ctx) currentUser := getCurrentUser(ctx) var edit *models.Edit + var comment *models.EditComment err := fac.WithTxn(func() error { eqb := fac.Edit() @@ -401,15 +419,15 @@ func (r *mutationResolver) EditComment(ctx context.Context, input models.EditCom } commentID, _ := uuid.NewV4() - comment := models.NewEditComment(commentID, currentUser, edit, input.Comment) + comment = models.NewEditComment(commentID, currentUser, edit, input.Comment) return eqb.CreateComment(*comment) }) - if err != nil { - return nil, err + if err == nil { + go notifications.OnEditComment(fac, comment) } - return edit, nil + return edit, err } func (r *mutationResolver) CancelEdit(ctx context.Context, input models.CancelEditInput) (*models.Edit, error) { @@ -426,7 +444,7 @@ func (r *mutationResolver) CancelEdit(ctx context.Context, input models.CancelEd } else if err = validateAdmin(ctx); err == nil { currentUser := getCurrentUser(ctx) - err = fac.WithTxn(func() error { + err := fac.WithTxn(func() error { vote := models.NewEditVote(currentUser, e, models.VoteTypeEnumImmediateReject) return eqb.CreateVote(*vote) }) diff --git a/pkg/api/resolver_mutation_notifications.go b/pkg/api/resolver_mutation_notifications.go new file mode 100644 index 000000000..1745e8475 --- /dev/null +++ b/pkg/api/resolver_mutation_notifications.go @@ -0,0 +1,9 @@ +package api + +import ( + "context" +) + +func (r *mutationResolver) MarkNotificationsRead(ctx context.Context) (bool, error) { + return true, nil +} diff --git a/pkg/api/resolver_query_find_scene.go b/pkg/api/resolver_query_find_scene.go index 5ad6d6c66..7629a4ca9 100644 --- a/pkg/api/resolver_query_find_scene.go +++ b/pkg/api/resolver_query_find_scene.go @@ -86,8 +86,8 @@ func (r *queryResolver) FindScenesBySceneFingerprints(ctx context.Context, scene } // Fetch all scene ids - scenes, err := qb.FindByIds(ids) - if err != nil { + scenes, errs := qb.FindByIds(ids) + if errs[0] != nil { return nil, err } sceneMap := make(map[uuid.UUID]*models.Scene) diff --git a/pkg/api/resolver_query_notifications.go b/pkg/api/resolver_query_notifications.go new file mode 100644 index 000000000..2eee3b77b --- /dev/null +++ b/pkg/api/resolver_query_notifications.go @@ -0,0 +1,21 @@ +package api + +import ( + "context" + + "github.com/stashapp/stash-box/pkg/models" +) + +func (r *queryResolver) GetUnreadNotificationCount(ctx context.Context) (int, error) { + fac := r.getRepoFactory(ctx) + qb := fac.Notification() + currentUser := getCurrentUser(ctx) + return qb.GetUnreadCount(currentUser.ID) +} + +func (r *queryResolver) QueryNotifications(ctx context.Context, input models.NotificationQueryInput) ([]*models.Notification, error) { + fac := r.getRepoFactory(ctx) + qb := fac.Notification() + currentUser := getCurrentUser(ctx) + return qb.GetNotifications(input, currentUser.ID) +} diff --git a/pkg/database/migrations/postgres/29_notifications.up.sql b/pkg/database/migrations/postgres/29_notifications.up.sql deleted file mode 100644 index 53d6cbe6e..000000000 --- a/pkg/database/migrations/postgres/29_notifications.up.sql +++ /dev/null @@ -1,9 +0,0 @@ -CREATE TABLE notifications ( - user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, - edit_id UUID REFERENCES edits(id) ON DELETE CASCADE, - type TEXT NOT NULL, - data JSONB, - created_at TIMESTAMP NOT NULL, - read_at TIMESTAMP -); -CREATE INDEX notifications_idx ON notifications (user_id, read_at); diff --git a/pkg/database/migrations/postgres/31_notifications.up.sql b/pkg/database/migrations/postgres/31_notifications.up.sql new file mode 100644 index 000000000..e149a0634 --- /dev/null +++ b/pkg/database/migrations/postgres/31_notifications.up.sql @@ -0,0 +1,31 @@ +CREATE TYPE notification_type AS ENUM ( + 'FAVORITE_PERFORMER_SCENE', + 'FAVORITE_PERFORMER_EDIT', + 'FAVORITE_STUDIO_SCENE', + 'FAVORITE_STUDIO_EDIT', + 'COMMENT_OWN_EDIT', + 'DOWNVOTE_OWN_EDIT', + 'FAILED_OWN_EDIT', + 'COMMENT_COMMENTED_EDIT', + 'COMMENT_VOTED_EDIT', + 'UPDATED_EDIT' +); + +CREATE TABLE notifications ( + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + type notification_type NOT NULL, + id UUID NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + read_at TIMESTAMP +); +CREATE INDEX notifications_user_read_idx ON notifications (user_id, read_at); + +CREATE TABLE user_notifications ( + user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, + type notification_type NOT NULL +); +CREATE INDEX user_notifications_user_id_idx ON user_notifications (user_id); +CREATE INDEX user_notifications_type_idx ON user_notifications (type); + +INSERT INTO user_notifications +SELECT id, type FROM unnest(enum_range(NULL::notification_type)) AS type CROSS JOIN users; diff --git a/pkg/dataloader/boolsloader_gen.go b/pkg/dataloader/boolsloader_gen.go new file mode 100644 index 000000000..6b6feaf2b --- /dev/null +++ b/pkg/dataloader/boolsloader_gen.go @@ -0,0 +1,221 @@ +// Code generated by github.com/vektah/dataloaden, DO NOT EDIT. + +package dataloader + +import ( + "sync" + "time" + + "github.com/gofrs/uuid" +) + +// BoolsLoaderConfig captures the config to create a new BoolsLoader +type BoolsLoaderConfig struct { + // Fetch is a method that provides the data for the loader + Fetch func(keys []uuid.UUID) ([]bool, []error) + + // Wait is how long wait before sending a batch + Wait time.Duration + + // MaxBatch will limit the maximum number of keys to send in one batch, 0 = not limit + MaxBatch int +} + +// NewBoolsLoader creates a new BoolsLoader given a fetch, wait, and maxBatch +func NewBoolsLoader(config BoolsLoaderConfig) *BoolsLoader { + return &BoolsLoader{ + fetch: config.Fetch, + wait: config.Wait, + maxBatch: config.MaxBatch, + } +} + +// BoolsLoader batches and caches requests +type BoolsLoader struct { + // this method provides the data for the loader + fetch func(keys []uuid.UUID) ([]bool, []error) + + // how long to done before sending a batch + wait time.Duration + + // this will limit the maximum number of keys to send in one batch, 0 = no limit + maxBatch int + + // INTERNAL + + // lazily created cache + cache map[uuid.UUID]bool + + // the current batch. keys will continue to be collected until timeout is hit, + // then everything will be sent to the fetch method and out to the listeners + batch *boolsLoaderBatch + + // mutex to prevent races + mu sync.Mutex +} + +type boolsLoaderBatch struct { + keys []uuid.UUID + data []bool + error []error + closing bool + done chan struct{} +} + +// Load a bool by key, batching and caching will be applied automatically +func (l *BoolsLoader) Load(key uuid.UUID) (bool, error) { + return l.LoadThunk(key)() +} + +// LoadThunk returns a function that when called will block waiting for a bool. +// This method should be used if you want one goroutine to make requests to many +// different data loaders without blocking until the thunk is called. +func (l *BoolsLoader) LoadThunk(key uuid.UUID) func() (bool, error) { + l.mu.Lock() + if it, ok := l.cache[key]; ok { + l.mu.Unlock() + return func() (bool, error) { + return it, nil + } + } + if l.batch == nil { + l.batch = &boolsLoaderBatch{done: make(chan struct{})} + } + batch := l.batch + pos := batch.keyIndex(l, key) + l.mu.Unlock() + + return func() (bool, error) { + <-batch.done + + var data bool + if pos < len(batch.data) { + data = batch.data[pos] + } + + var err error + // its convenient to be able to return a single error for everything + if len(batch.error) == 1 { + err = batch.error[0] + } else if batch.error != nil { + err = batch.error[pos] + } + + if err == nil { + l.mu.Lock() + l.unsafeSet(key, data) + l.mu.Unlock() + } + + return data, err + } +} + +// LoadAll fetches many keys at once. It will be broken into appropriate sized +// sub batches depending on how the loader is configured +func (l *BoolsLoader) LoadAll(keys []uuid.UUID) ([]bool, []error) { + results := make([]func() (bool, error), len(keys)) + + for i, key := range keys { + results[i] = l.LoadThunk(key) + } + + bools := make([]bool, len(keys)) + errors := make([]error, len(keys)) + for i, thunk := range results { + bools[i], errors[i] = thunk() + } + return bools, errors +} + +// LoadAllThunk returns a function that when called will block waiting for a bools. +// This method should be used if you want one goroutine to make requests to many +// different data loaders without blocking until the thunk is called. +func (l *BoolsLoader) LoadAllThunk(keys []uuid.UUID) func() ([]bool, []error) { + results := make([]func() (bool, error), len(keys)) + for i, key := range keys { + results[i] = l.LoadThunk(key) + } + return func() ([]bool, []error) { + bools := make([]bool, len(keys)) + errors := make([]error, len(keys)) + for i, thunk := range results { + bools[i], errors[i] = thunk() + } + return bools, errors + } +} + +// Prime the cache with the provided key and value. If the key already exists, no change is made +// and false is returned. +// (To forcefully prime the cache, clear the key first with loader.clear(key).prime(key, value).) +func (l *BoolsLoader) Prime(key uuid.UUID, value bool) bool { + l.mu.Lock() + var found bool + if _, found = l.cache[key]; !found { + l.unsafeSet(key, value) + } + l.mu.Unlock() + return !found +} + +// Clear the value at key from the cache, if it exists +func (l *BoolsLoader) Clear(key uuid.UUID) { + l.mu.Lock() + delete(l.cache, key) + l.mu.Unlock() +} + +func (l *BoolsLoader) unsafeSet(key uuid.UUID, value bool) { + if l.cache == nil { + l.cache = map[uuid.UUID]bool{} + } + l.cache[key] = value +} + +// keyIndex will return the location of the key in the batch, if its not found +// it will add the key to the batch +func (b *boolsLoaderBatch) keyIndex(l *BoolsLoader, key uuid.UUID) int { + for i, existingKey := range b.keys { + if key == existingKey { + return i + } + } + + pos := len(b.keys) + b.keys = append(b.keys, key) + if pos == 0 { + go b.startTimer(l) + } + + if l.maxBatch != 0 && pos >= l.maxBatch-1 { + if !b.closing { + b.closing = true + l.batch = nil + go b.end(l) + } + } + + return pos +} + +func (b *boolsLoaderBatch) startTimer(l *BoolsLoader) { + time.Sleep(l.wait) + l.mu.Lock() + + // we must have hit a batch limit and are already finalizing this batch + if b.closing { + l.mu.Unlock() + return + } + + l.batch = nil + l.mu.Unlock() + + b.end(l) +} + +func (b *boolsLoaderBatch) end(l *BoolsLoader) { + b.data, b.error = l.fetch(b.keys) + close(b.done) +} diff --git a/pkg/dataloader/editcommentloader_gen.go b/pkg/dataloader/editcommentloader_gen.go new file mode 100644 index 000000000..19dfbdb2c --- /dev/null +++ b/pkg/dataloader/editcommentloader_gen.go @@ -0,0 +1,225 @@ +// Code generated by github.com/vektah/dataloaden, DO NOT EDIT. + +package dataloader + +import ( + "sync" + "time" + + "github.com/gofrs/uuid" + "github.com/stashapp/stash-box/pkg/models" +) + +// EditCommentLoaderConfig captures the config to create a new EditCommentLoader +type EditCommentLoaderConfig struct { + // Fetch is a method that provides the data for the loader + Fetch func(keys []uuid.UUID) ([]*models.EditComment, []error) + + // Wait is how long wait before sending a batch + Wait time.Duration + + // MaxBatch will limit the maximum number of keys to send in one batch, 0 = not limit + MaxBatch int +} + +// NewEditCommentLoader creates a new EditCommentLoader given a fetch, wait, and maxBatch +func NewEditCommentLoader(config EditCommentLoaderConfig) *EditCommentLoader { + return &EditCommentLoader{ + fetch: config.Fetch, + wait: config.Wait, + maxBatch: config.MaxBatch, + } +} + +// EditCommentLoader batches and caches requests +type EditCommentLoader struct { + // this method provides the data for the loader + fetch func(keys []uuid.UUID) ([]*models.EditComment, []error) + + // how long to done before sending a batch + wait time.Duration + + // this will limit the maximum number of keys to send in one batch, 0 = no limit + maxBatch int + + // INTERNAL + + // lazily created cache + cache map[uuid.UUID]*models.EditComment + + // the current batch. keys will continue to be collected until timeout is hit, + // then everything will be sent to the fetch method and out to the listeners + batch *editCommentLoaderBatch + + // mutex to prevent races + mu sync.Mutex +} + +type editCommentLoaderBatch struct { + keys []uuid.UUID + data []*models.EditComment + error []error + closing bool + done chan struct{} +} + +// Load a EditComment by key, batching and caching will be applied automatically +func (l *EditCommentLoader) Load(key uuid.UUID) (*models.EditComment, error) { + return l.LoadThunk(key)() +} + +// LoadThunk returns a function that when called will block waiting for a EditComment. +// This method should be used if you want one goroutine to make requests to many +// different data loaders without blocking until the thunk is called. +func (l *EditCommentLoader) LoadThunk(key uuid.UUID) func() (*models.EditComment, error) { + l.mu.Lock() + if it, ok := l.cache[key]; ok { + l.mu.Unlock() + return func() (*models.EditComment, error) { + return it, nil + } + } + if l.batch == nil { + l.batch = &editCommentLoaderBatch{done: make(chan struct{})} + } + batch := l.batch + pos := batch.keyIndex(l, key) + l.mu.Unlock() + + return func() (*models.EditComment, error) { + <-batch.done + + var data *models.EditComment + if pos < len(batch.data) { + data = batch.data[pos] + } + + var err error + // its convenient to be able to return a single error for everything + if len(batch.error) == 1 { + err = batch.error[0] + } else if batch.error != nil { + err = batch.error[pos] + } + + if err == nil { + l.mu.Lock() + l.unsafeSet(key, data) + l.mu.Unlock() + } + + return data, err + } +} + +// LoadAll fetches many keys at once. It will be broken into appropriate sized +// sub batches depending on how the loader is configured +func (l *EditCommentLoader) LoadAll(keys []uuid.UUID) ([]*models.EditComment, []error) { + results := make([]func() (*models.EditComment, error), len(keys)) + + for i, key := range keys { + results[i] = l.LoadThunk(key) + } + + editComments := make([]*models.EditComment, len(keys)) + errors := make([]error, len(keys)) + for i, thunk := range results { + editComments[i], errors[i] = thunk() + } + return editComments, errors +} + +// LoadAllThunk returns a function that when called will block waiting for a EditComments. +// This method should be used if you want one goroutine to make requests to many +// different data loaders without blocking until the thunk is called. +func (l *EditCommentLoader) LoadAllThunk(keys []uuid.UUID) func() ([]*models.EditComment, []error) { + results := make([]func() (*models.EditComment, error), len(keys)) + for i, key := range keys { + results[i] = l.LoadThunk(key) + } + return func() ([]*models.EditComment, []error) { + editComments := make([]*models.EditComment, len(keys)) + errors := make([]error, len(keys)) + for i, thunk := range results { + editComments[i], errors[i] = thunk() + } + return editComments, errors + } +} + +// Prime the cache with the provided key and value. If the key already exists, no change is made +// and false is returned. +// (To forcefully prime the cache, clear the key first with loader.clear(key).prime(key, value).) +func (l *EditCommentLoader) Prime(key uuid.UUID, value *models.EditComment) bool { + l.mu.Lock() + var found bool + if _, found = l.cache[key]; !found { + // make a copy when writing to the cache, its easy to pass a pointer in from a loop var + // and end up with the whole cache pointing to the same value. + cpy := *value + l.unsafeSet(key, &cpy) + } + l.mu.Unlock() + return !found +} + +// Clear the value at key from the cache, if it exists +func (l *EditCommentLoader) Clear(key uuid.UUID) { + l.mu.Lock() + delete(l.cache, key) + l.mu.Unlock() +} + +func (l *EditCommentLoader) unsafeSet(key uuid.UUID, value *models.EditComment) { + if l.cache == nil { + l.cache = map[uuid.UUID]*models.EditComment{} + } + l.cache[key] = value +} + +// keyIndex will return the location of the key in the batch, if its not found +// it will add the key to the batch +func (b *editCommentLoaderBatch) keyIndex(l *EditCommentLoader, key uuid.UUID) int { + for i, existingKey := range b.keys { + if key == existingKey { + return i + } + } + + pos := len(b.keys) + b.keys = append(b.keys, key) + if pos == 0 { + go b.startTimer(l) + } + + if l.maxBatch != 0 && pos >= l.maxBatch-1 { + if !b.closing { + b.closing = true + l.batch = nil + go b.end(l) + } + } + + return pos +} + +func (b *editCommentLoaderBatch) startTimer(l *EditCommentLoader) { + time.Sleep(l.wait) + l.mu.Lock() + + // we must have hit a batch limit and are already finalizing this batch + if b.closing { + l.mu.Unlock() + return + } + + l.batch = nil + l.mu.Unlock() + + b.end(l) +} + +func (b *editCommentLoaderBatch) end(l *EditCommentLoader) { + b.data, b.error = l.fetch(b.keys) + close(b.done) +} diff --git a/pkg/dataloader/editloader_gen.go b/pkg/dataloader/editloader_gen.go new file mode 100644 index 000000000..4b725cd2a --- /dev/null +++ b/pkg/dataloader/editloader_gen.go @@ -0,0 +1,225 @@ +// Code generated by github.com/vektah/dataloaden, DO NOT EDIT. + +package dataloader + +import ( + "sync" + "time" + + "github.com/gofrs/uuid" + "github.com/stashapp/stash-box/pkg/models" +) + +// EditLoaderConfig captures the config to create a new EditLoader +type EditLoaderConfig struct { + // Fetch is a method that provides the data for the loader + Fetch func(keys []uuid.UUID) ([]*models.Edit, []error) + + // Wait is how long wait before sending a batch + Wait time.Duration + + // MaxBatch will limit the maximum number of keys to send in one batch, 0 = not limit + MaxBatch int +} + +// NewEditLoader creates a new EditLoader given a fetch, wait, and maxBatch +func NewEditLoader(config EditLoaderConfig) *EditLoader { + return &EditLoader{ + fetch: config.Fetch, + wait: config.Wait, + maxBatch: config.MaxBatch, + } +} + +// EditLoader batches and caches requests +type EditLoader struct { + // this method provides the data for the loader + fetch func(keys []uuid.UUID) ([]*models.Edit, []error) + + // how long to done before sending a batch + wait time.Duration + + // this will limit the maximum number of keys to send in one batch, 0 = no limit + maxBatch int + + // INTERNAL + + // lazily created cache + cache map[uuid.UUID]*models.Edit + + // the current batch. keys will continue to be collected until timeout is hit, + // then everything will be sent to the fetch method and out to the listeners + batch *editLoaderBatch + + // mutex to prevent races + mu sync.Mutex +} + +type editLoaderBatch struct { + keys []uuid.UUID + data []*models.Edit + error []error + closing bool + done chan struct{} +} + +// Load a Edit by key, batching and caching will be applied automatically +func (l *EditLoader) Load(key uuid.UUID) (*models.Edit, error) { + return l.LoadThunk(key)() +} + +// LoadThunk returns a function that when called will block waiting for a Edit. +// This method should be used if you want one goroutine to make requests to many +// different data loaders without blocking until the thunk is called. +func (l *EditLoader) LoadThunk(key uuid.UUID) func() (*models.Edit, error) { + l.mu.Lock() + if it, ok := l.cache[key]; ok { + l.mu.Unlock() + return func() (*models.Edit, error) { + return it, nil + } + } + if l.batch == nil { + l.batch = &editLoaderBatch{done: make(chan struct{})} + } + batch := l.batch + pos := batch.keyIndex(l, key) + l.mu.Unlock() + + return func() (*models.Edit, error) { + <-batch.done + + var data *models.Edit + if pos < len(batch.data) { + data = batch.data[pos] + } + + var err error + // its convenient to be able to return a single error for everything + if len(batch.error) == 1 { + err = batch.error[0] + } else if batch.error != nil { + err = batch.error[pos] + } + + if err == nil { + l.mu.Lock() + l.unsafeSet(key, data) + l.mu.Unlock() + } + + return data, err + } +} + +// LoadAll fetches many keys at once. It will be broken into appropriate sized +// sub batches depending on how the loader is configured +func (l *EditLoader) LoadAll(keys []uuid.UUID) ([]*models.Edit, []error) { + results := make([]func() (*models.Edit, error), len(keys)) + + for i, key := range keys { + results[i] = l.LoadThunk(key) + } + + edits := make([]*models.Edit, len(keys)) + errors := make([]error, len(keys)) + for i, thunk := range results { + edits[i], errors[i] = thunk() + } + return edits, errors +} + +// LoadAllThunk returns a function that when called will block waiting for a Edits. +// This method should be used if you want one goroutine to make requests to many +// different data loaders without blocking until the thunk is called. +func (l *EditLoader) LoadAllThunk(keys []uuid.UUID) func() ([]*models.Edit, []error) { + results := make([]func() (*models.Edit, error), len(keys)) + for i, key := range keys { + results[i] = l.LoadThunk(key) + } + return func() ([]*models.Edit, []error) { + edits := make([]*models.Edit, len(keys)) + errors := make([]error, len(keys)) + for i, thunk := range results { + edits[i], errors[i] = thunk() + } + return edits, errors + } +} + +// Prime the cache with the provided key and value. If the key already exists, no change is made +// and false is returned. +// (To forcefully prime the cache, clear the key first with loader.clear(key).prime(key, value).) +func (l *EditLoader) Prime(key uuid.UUID, value *models.Edit) bool { + l.mu.Lock() + var found bool + if _, found = l.cache[key]; !found { + // make a copy when writing to the cache, its easy to pass a pointer in from a loop var + // and end up with the whole cache pointing to the same value. + cpy := *value + l.unsafeSet(key, &cpy) + } + l.mu.Unlock() + return !found +} + +// Clear the value at key from the cache, if it exists +func (l *EditLoader) Clear(key uuid.UUID) { + l.mu.Lock() + delete(l.cache, key) + l.mu.Unlock() +} + +func (l *EditLoader) unsafeSet(key uuid.UUID, value *models.Edit) { + if l.cache == nil { + l.cache = map[uuid.UUID]*models.Edit{} + } + l.cache[key] = value +} + +// keyIndex will return the location of the key in the batch, if its not found +// it will add the key to the batch +func (b *editLoaderBatch) keyIndex(l *EditLoader, key uuid.UUID) int { + for i, existingKey := range b.keys { + if key == existingKey { + return i + } + } + + pos := len(b.keys) + b.keys = append(b.keys, key) + if pos == 0 { + go b.startTimer(l) + } + + if l.maxBatch != 0 && pos >= l.maxBatch-1 { + if !b.closing { + b.closing = true + l.batch = nil + go b.end(l) + } + } + + return pos +} + +func (b *editLoaderBatch) startTimer(l *EditLoader) { + time.Sleep(l.wait) + l.mu.Lock() + + // we must have hit a batch limit and are already finalizing this batch + if b.closing { + l.mu.Unlock() + return + } + + l.batch = nil + l.mu.Unlock() + + b.end(l) +} + +func (b *editLoaderBatch) end(l *EditLoader) { + b.data, b.error = l.fetch(b.keys) + close(b.done) +} diff --git a/pkg/dataloader/loaders.go b/pkg/dataloader/loaders.go index d06d04592..3eb2ec255 100644 --- a/pkg/dataloader/loaders.go +++ b/pkg/dataloader/loaders.go @@ -27,6 +27,7 @@ type Loaders struct { PerformerPiercingsByID BodyModificationsLoader PerformerTattoosByID BodyModificationsLoader PerformerUrlsByID URLLoader + SceneById SceneLoader SceneImageIDsByID UUIDsLoader SceneAppearancesByID SceneAppearancesLoader SceneUrlsByID URLLoader @@ -37,6 +38,8 @@ type Loaders struct { StudioByID StudioLoader TagByID TagLoader TagCategoryByID TagCategoryLoader + EditById EditLoader + EditCommentById EditCommentLoader } func Middleware(fac models.Repo) func(next http.Handler) http.Handler { @@ -220,5 +223,29 @@ func GetLoaders(ctx context.Context, fac models.Repo) *Loaders { return qb.FindByIds(ids) }, }, + EditById: EditLoader{ + maxBatch: 1000, + wait: 1 * time.Millisecond, + fetch: func(ids []uuid.UUID) ([]*models.Edit, []error) { + qb := fac.Edit() + return qb.FindByIds(ids) + }, + }, + EditCommentById: EditCommentLoader{ + maxBatch: 1000, + wait: 1 * time.Millisecond, + fetch: func(ids []uuid.UUID) ([]*models.EditComment, []error) { + qb := fac.Edit() + return qb.FindCommentsByIds(ids) + }, + }, + SceneById: SceneLoader{ + maxBatch: 1000, + wait: 1 * time.Millisecond, + fetch: func(ids []uuid.UUID) ([]*models.Scene, []error) { + qb := fac.Scene() + return qb.FindByIds(ids) + }, + }, } } diff --git a/pkg/dataloader/sceneloader_gen.go b/pkg/dataloader/sceneloader_gen.go new file mode 100644 index 000000000..6bc742c20 --- /dev/null +++ b/pkg/dataloader/sceneloader_gen.go @@ -0,0 +1,225 @@ +// Code generated by github.com/vektah/dataloaden, DO NOT EDIT. + +package dataloader + +import ( + "sync" + "time" + + "github.com/gofrs/uuid" + "github.com/stashapp/stash-box/pkg/models" +) + +// SceneLoaderConfig captures the config to create a new SceneLoader +type SceneLoaderConfig struct { + // Fetch is a method that provides the data for the loader + Fetch func(keys []uuid.UUID) ([]*models.Scene, []error) + + // Wait is how long wait before sending a batch + Wait time.Duration + + // MaxBatch will limit the maximum number of keys to send in one batch, 0 = not limit + MaxBatch int +} + +// NewSceneLoader creates a new SceneLoader given a fetch, wait, and maxBatch +func NewSceneLoader(config SceneLoaderConfig) *SceneLoader { + return &SceneLoader{ + fetch: config.Fetch, + wait: config.Wait, + maxBatch: config.MaxBatch, + } +} + +// SceneLoader batches and caches requests +type SceneLoader struct { + // this method provides the data for the loader + fetch func(keys []uuid.UUID) ([]*models.Scene, []error) + + // how long to done before sending a batch + wait time.Duration + + // this will limit the maximum number of keys to send in one batch, 0 = no limit + maxBatch int + + // INTERNAL + + // lazily created cache + cache map[uuid.UUID]*models.Scene + + // the current batch. keys will continue to be collected until timeout is hit, + // then everything will be sent to the fetch method and out to the listeners + batch *sceneLoaderBatch + + // mutex to prevent races + mu sync.Mutex +} + +type sceneLoaderBatch struct { + keys []uuid.UUID + data []*models.Scene + error []error + closing bool + done chan struct{} +} + +// Load a Scene by key, batching and caching will be applied automatically +func (l *SceneLoader) Load(key uuid.UUID) (*models.Scene, error) { + return l.LoadThunk(key)() +} + +// LoadThunk returns a function that when called will block waiting for a Scene. +// This method should be used if you want one goroutine to make requests to many +// different data loaders without blocking until the thunk is called. +func (l *SceneLoader) LoadThunk(key uuid.UUID) func() (*models.Scene, error) { + l.mu.Lock() + if it, ok := l.cache[key]; ok { + l.mu.Unlock() + return func() (*models.Scene, error) { + return it, nil + } + } + if l.batch == nil { + l.batch = &sceneLoaderBatch{done: make(chan struct{})} + } + batch := l.batch + pos := batch.keyIndex(l, key) + l.mu.Unlock() + + return func() (*models.Scene, error) { + <-batch.done + + var data *models.Scene + if pos < len(batch.data) { + data = batch.data[pos] + } + + var err error + // its convenient to be able to return a single error for everything + if len(batch.error) == 1 { + err = batch.error[0] + } else if batch.error != nil { + err = batch.error[pos] + } + + if err == nil { + l.mu.Lock() + l.unsafeSet(key, data) + l.mu.Unlock() + } + + return data, err + } +} + +// LoadAll fetches many keys at once. It will be broken into appropriate sized +// sub batches depending on how the loader is configured +func (l *SceneLoader) LoadAll(keys []uuid.UUID) ([]*models.Scene, []error) { + results := make([]func() (*models.Scene, error), len(keys)) + + for i, key := range keys { + results[i] = l.LoadThunk(key) + } + + scenes := make([]*models.Scene, len(keys)) + errors := make([]error, len(keys)) + for i, thunk := range results { + scenes[i], errors[i] = thunk() + } + return scenes, errors +} + +// LoadAllThunk returns a function that when called will block waiting for a Scenes. +// This method should be used if you want one goroutine to make requests to many +// different data loaders without blocking until the thunk is called. +func (l *SceneLoader) LoadAllThunk(keys []uuid.UUID) func() ([]*models.Scene, []error) { + results := make([]func() (*models.Scene, error), len(keys)) + for i, key := range keys { + results[i] = l.LoadThunk(key) + } + return func() ([]*models.Scene, []error) { + scenes := make([]*models.Scene, len(keys)) + errors := make([]error, len(keys)) + for i, thunk := range results { + scenes[i], errors[i] = thunk() + } + return scenes, errors + } +} + +// Prime the cache with the provided key and value. If the key already exists, no change is made +// and false is returned. +// (To forcefully prime the cache, clear the key first with loader.clear(key).prime(key, value).) +func (l *SceneLoader) Prime(key uuid.UUID, value *models.Scene) bool { + l.mu.Lock() + var found bool + if _, found = l.cache[key]; !found { + // make a copy when writing to the cache, its easy to pass a pointer in from a loop var + // and end up with the whole cache pointing to the same value. + cpy := *value + l.unsafeSet(key, &cpy) + } + l.mu.Unlock() + return !found +} + +// Clear the value at key from the cache, if it exists +func (l *SceneLoader) Clear(key uuid.UUID) { + l.mu.Lock() + delete(l.cache, key) + l.mu.Unlock() +} + +func (l *SceneLoader) unsafeSet(key uuid.UUID, value *models.Scene) { + if l.cache == nil { + l.cache = map[uuid.UUID]*models.Scene{} + } + l.cache[key] = value +} + +// keyIndex will return the location of the key in the batch, if its not found +// it will add the key to the batch +func (b *sceneLoaderBatch) keyIndex(l *SceneLoader, key uuid.UUID) int { + for i, existingKey := range b.keys { + if key == existingKey { + return i + } + } + + pos := len(b.keys) + b.keys = append(b.keys, key) + if pos == 0 { + go b.startTimer(l) + } + + if l.maxBatch != 0 && pos >= l.maxBatch-1 { + if !b.closing { + b.closing = true + l.batch = nil + go b.end(l) + } + } + + return pos +} + +func (b *sceneLoaderBatch) startTimer(l *SceneLoader) { + time.Sleep(l.wait) + l.mu.Lock() + + // we must have hit a batch limit and are already finalizing this batch + if b.closing { + l.mu.Unlock() + return + } + + l.batch = nil + l.mu.Unlock() + + b.end(l) +} + +func (b *sceneLoaderBatch) end(l *SceneLoader) { + b.data, b.error = l.fetch(b.keys) + close(b.done) +} diff --git a/pkg/manager/edit/edit.go b/pkg/manager/edit/edit.go index b66fda1d9..b3d27f9f1 100644 --- a/pkg/manager/edit/edit.go +++ b/pkg/manager/edit/edit.go @@ -9,6 +9,7 @@ import ( "github.com/gofrs/uuid" "github.com/stashapp/stash-box/pkg/manager/config" + "github.com/stashapp/stash-box/pkg/manager/notifications" "github.com/stashapp/stash-box/pkg/models" "github.com/stashapp/stash-box/pkg/user" "github.com/stashapp/stash-box/pkg/utils" @@ -176,6 +177,10 @@ func ApplyEdit(fac models.Repo, editID uuid.UUID, immediate bool) (*models.Edit, return nil }) + if err == nil { + go notifications.OnApplyEdit(fac, updatedEdit) + } + return updatedEdit, err } @@ -212,6 +217,10 @@ func CloseEdit(fac models.Repo, editID uuid.UUID, status models.VoteStatusEnum) return err }) + if err == nil && status != models.VoteStatusEnumCanceled { + go notifications.OnCancelEdit(fac, updatedEdit) + } + return updatedEdit, err } diff --git a/pkg/manager/notifications/notifications.go b/pkg/manager/notifications/notifications.go new file mode 100644 index 000000000..03240e3f9 --- /dev/null +++ b/pkg/manager/notifications/notifications.go @@ -0,0 +1,56 @@ +package notifications + +import ( + "fmt" + + "github.com/stashapp/stash-box/pkg/models" +) + +func OnApplyEdit(fac models.Repo, edit *models.Edit) { + fmt.Println("onApplyEdit") + nqb := fac.Notification() + eqb := fac.Edit() + if edit.Status == models.VoteStatusEnumAccepted.String() || edit.Status == models.VoteStatusEnumImmediateAccepted.String() { + if edit.TargetType == models.TargetTypeEnumScene.String() { + sceneID, err := eqb.FindSceneID(edit.ID) + if err != nil || sceneID == nil { + return + } + + nqb.TriggerSceneCreationNotifications(*sceneID) + } + } else if edit.Status == models.VoteStatusEnumImmediateRejected.String() || edit.Status == models.VoteStatusEnumRejected.String() || edit.Status == models.VoteStatusEnumFailed.String() { + nqb.TriggerFailedEditNotifications(edit.ID) + } +} + +func OnCancelEdit(fac models.Repo, edit *models.Edit) { + fmt.Println("onCancelEdit") + fac.Notification().TriggerFailedEditNotifications(edit.ID) +} + +func OnCreateEdit(fac models.Repo, edit *models.Edit) { + fmt.Println("onCreateEdit") + if edit.TargetType == models.TargetTypeEnumPerformer.String() { + fac.Notification().TriggerPerformerEditNotifications(edit.ID) + } else if edit.TargetType == models.TargetTypeEnumScene.String() { + fac.Notification().TriggerSceneEditNotifications(edit.ID) + } else if edit.TargetType == models.TargetTypeEnumStudio.String() { + fac.Notification().TriggerStudioEditNotifications(edit.ID) + } +} + +func OnUpdateEdit(fac models.Repo, edit *models.Edit) { + fmt.Println("onUpdateEdit") + fac.Notification().TriggerUpdatedEditNotifications(edit.ID) +} + +func OnEditDownvote(fac models.Repo, edit *models.Edit) { + fmt.Println("onDownvoteEdit") + fac.Notification().TriggerDownvoteEditNotifications(edit.ID) +} + +func OnEditComment(fac models.Repo, comment *models.EditComment) { + fmt.Println("onEditComment") + fac.Notification().TriggerEditCommentNotifications(comment.ID) +} diff --git a/pkg/models/edit.go b/pkg/models/edit.go index e77f8fc18..024cdf75d 100644 --- a/pkg/models/edit.go +++ b/pkg/models/edit.go @@ -7,6 +7,7 @@ type EditRepo interface { Update(updatedEdit Edit) (*Edit, error) Destroy(id uuid.UUID) error Find(id uuid.UUID) (*Edit, error) + FindByIds(ids []uuid.UUID) ([]*Edit, []error) CreateEditTag(newJoin EditTag) error CreateEditPerformer(newJoin EditPerformer) error CreateEditStudio(newJoin EditStudio) error @@ -29,4 +30,5 @@ type EditRepo interface { FindCompletedEdits(int, int, int) ([]*Edit, error) FindPendingSceneCreation(input QueryExistingSceneInput) ([]*Edit, error) CancelUserEdits(userID uuid.UUID) error + FindCommentsByIds(ids []uuid.UUID) ([]*EditComment, []error) } diff --git a/pkg/models/factory.go b/pkg/models/factory.go index 67075f6a5..5dce488f3 100644 --- a/pkg/models/factory.go +++ b/pkg/models/factory.go @@ -23,4 +23,6 @@ type Repo interface { User() UserRepo Site() SiteRepo Draft() DraftRepo + + Notification() NotificationRepo } diff --git a/pkg/models/generated_exec.go b/pkg/models/generated_exec.go index 412cc07bc..18e117bf7 100644 --- a/pkg/models/generated_exec.go +++ b/pkg/models/generated_exec.go @@ -43,6 +43,7 @@ type ResolverRoot interface { EditVote() EditVoteResolver Image() ImageResolver Mutation() MutationResolver + Notification() NotificationResolver Performer() PerformerResolver PerformerDraft() PerformerDraftResolver PerformerEdit() PerformerEditResolver @@ -75,10 +76,22 @@ type ComplexityRoot struct { Location func(childComplexity int) int } - CommentNotification struct { + CommentCommentedEdit struct { Comment func(childComplexity int) int } + CommentOwnEdit struct { + Comment func(childComplexity int) int + } + + CommentVotedEdit struct { + Comment func(childComplexity int) int + } + + DownvoteOwnEdit struct { + Edit func(childComplexity int) int + } + Draft struct { Created func(childComplexity int) int Data func(childComplexity int) int @@ -137,8 +150,24 @@ type ComplexityRoot struct { Vote func(childComplexity int) int } - FailedVoteNotification struct { - Message func(childComplexity int) int + FailedOwnEdit struct { + Edit func(childComplexity int) int + } + + FavoritePerformerEdit struct { + Edit func(childComplexity int) int + } + + FavoritePerformerScene struct { + Scene func(childComplexity int) int + } + + FavoriteStudioEdit struct { + Edit func(childComplexity int) int + } + + FavoriteStudioScene struct { + Scene func(childComplexity int) int } Fingerprint struct { @@ -224,21 +253,10 @@ type ComplexityRoot struct { UserUpdate func(childComplexity int, input UserUpdateInput) int } - NegativeVoteNotification struct { - User func(childComplexity int) int - } - Notification struct { Created func(childComplexity int) int Data func(childComplexity int) int - Edit func(childComplexity int) int Read func(childComplexity int) int - User func(childComplexity int) int - } - - Notifications struct { - Notifications func(childComplexity int) int - UnreadCount func(childComplexity int) int } Performer struct { @@ -365,10 +383,11 @@ type ComplexityRoot struct { FindTagCategory func(childComplexity int, id uuid.UUID) int FindUser func(childComplexity int, id *uuid.UUID, username *string) int GetConfig func(childComplexity int) int - GetNotifications func(childComplexity int) int + GetUnreadNotificationCount func(childComplexity int) int Me func(childComplexity int) int QueryEdits func(childComplexity int, input EditQueryInput) int QueryExistingScene func(childComplexity int, input QueryExistingSceneInput) int + QueryNotifications func(childComplexity int, input NotificationQueryInput) int QueryPerformers func(childComplexity int, input PerformerQueryInput) int QueryScenes func(childComplexity int, input SceneQueryInput) int QuerySites func(childComplexity int) int @@ -571,6 +590,10 @@ type ComplexityRoot struct { URL func(childComplexity int) int } + UpdatedEdit struct { + Edit func(childComplexity int) int + } + User struct { APICalls func(childComplexity int) int APIKey func(childComplexity int) int @@ -701,7 +724,12 @@ type MutationResolver interface { DestroyDraft(ctx context.Context, id uuid.UUID) (bool, error) FavoritePerformer(ctx context.Context, id uuid.UUID, favorite bool) (bool, error) FavoriteStudio(ctx context.Context, id uuid.UUID, favorite bool) (bool, error) - MarkNotificationsRead(ctx context.Context) (*Notifications, error) + MarkNotificationsRead(ctx context.Context) (bool, error) +} +type NotificationResolver interface { + Created(ctx context.Context, obj *Notification) (*time.Time, error) + Read(ctx context.Context, obj *Notification) (bool, error) + Data(ctx context.Context, obj *Notification) (NotificationData, error) } type PerformerResolver interface { Disambiguation(ctx context.Context, obj *Performer) (*string, error) @@ -790,7 +818,8 @@ type QueryResolver interface { QueryExistingScene(ctx context.Context, input QueryExistingSceneInput) (*QueryExistingSceneResult, error) Version(ctx context.Context) (*Version, error) GetConfig(ctx context.Context) (*StashBoxConfig, error) - GetNotifications(ctx context.Context) (*Notifications, error) + QueryNotifications(ctx context.Context, input NotificationQueryInput) ([]*Notification, error) + GetUnreadNotificationCount(ctx context.Context) (int, error) } type QueryEditsResultTypeResolver interface { Count(ctx context.Context, obj *EditQuery) (int, error) @@ -941,12 +970,33 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.BodyModification.Location(childComplexity), true - case "CommentNotification.comment": - if e.complexity.CommentNotification.Comment == nil { + case "CommentCommentedEdit.comment": + if e.complexity.CommentCommentedEdit.Comment == nil { break } - return e.complexity.CommentNotification.Comment(childComplexity), true + return e.complexity.CommentCommentedEdit.Comment(childComplexity), true + + case "CommentOwnEdit.comment": + if e.complexity.CommentOwnEdit.Comment == nil { + break + } + + return e.complexity.CommentOwnEdit.Comment(childComplexity), true + + case "CommentVotedEdit.comment": + if e.complexity.CommentVotedEdit.Comment == nil { + break + } + + return e.complexity.CommentVotedEdit.Comment(childComplexity), true + + case "DownvoteOwnEdit.edit": + if e.complexity.DownvoteOwnEdit.Edit == nil { + break + } + + return e.complexity.DownvoteOwnEdit.Edit(childComplexity), true case "Draft.created": if e.complexity.Draft.Created == nil { @@ -1207,12 +1257,40 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.EditVote.Vote(childComplexity), true - case "FailedVoteNotification.message": - if e.complexity.FailedVoteNotification.Message == nil { + case "FailedOwnEdit.edit": + if e.complexity.FailedOwnEdit.Edit == nil { break } - return e.complexity.FailedVoteNotification.Message(childComplexity), true + return e.complexity.FailedOwnEdit.Edit(childComplexity), true + + case "FavoritePerformerEdit.edit": + if e.complexity.FavoritePerformerEdit.Edit == nil { + break + } + + return e.complexity.FavoritePerformerEdit.Edit(childComplexity), true + + case "FavoritePerformerScene.scene": + if e.complexity.FavoritePerformerScene.Scene == nil { + break + } + + return e.complexity.FavoritePerformerScene.Scene(childComplexity), true + + case "FavoriteStudioEdit.edit": + if e.complexity.FavoriteStudioEdit.Edit == nil { + break + } + + return e.complexity.FavoriteStudioEdit.Edit(childComplexity), true + + case "FavoriteStudioScene.scene": + if e.complexity.FavoriteStudioScene.Scene == nil { + break + } + + return e.complexity.FavoriteStudioScene.Scene(childComplexity), true case "Fingerprint.algorithm": if e.complexity.Fingerprint.Algorithm == nil { @@ -1935,13 +2013,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.UserUpdate(childComplexity, args["input"].(UserUpdateInput)), true - case "NegativeVoteNotification.user": - if e.complexity.NegativeVoteNotification.User == nil { - break - } - - return e.complexity.NegativeVoteNotification.User(childComplexity), true - case "Notification.created": if e.complexity.Notification.Created == nil { break @@ -1956,13 +2027,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Notification.Data(childComplexity), true - case "Notification.edit": - if e.complexity.Notification.Edit == nil { - break - } - - return e.complexity.Notification.Edit(childComplexity), true - case "Notification.read": if e.complexity.Notification.Read == nil { break @@ -1970,27 +2034,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Notification.Read(childComplexity), true - case "Notification.user": - if e.complexity.Notification.User == nil { - break - } - - return e.complexity.Notification.User(childComplexity), true - - case "Notifications.notifications": - if e.complexity.Notifications.Notifications == nil { - break - } - - return e.complexity.Notifications.Notifications(childComplexity), true - - case "Notifications.unreadCount": - if e.complexity.Notifications.UnreadCount == nil { - break - } - - return e.complexity.Notifications.UnreadCount(childComplexity), true - case "Performer.age": if e.complexity.Performer.Age == nil { break @@ -2796,12 +2839,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.GetConfig(childComplexity), true - case "Query.getNotifications": - if e.complexity.Query.GetNotifications == nil { + case "Query.getUnreadNotificationCount": + if e.complexity.Query.GetUnreadNotificationCount == nil { break } - return e.complexity.Query.GetNotifications(childComplexity), true + return e.complexity.Query.GetUnreadNotificationCount(childComplexity), true case "Query.me": if e.complexity.Query.Me == nil { @@ -2834,6 +2877,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.QueryExistingScene(childComplexity, args["input"].(QueryExistingSceneInput)), true + case "Query.queryNotifications": + if e.complexity.Query.QueryNotifications == nil { + break + } + + args, err := ec.field_Query_queryNotifications_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.QueryNotifications(childComplexity, args["input"].(NotificationQueryInput)), true + case "Query.queryPerformers": if e.complexity.Query.QueryPerformers == nil { break @@ -3864,6 +3919,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.URL.URL(childComplexity), true + case "UpdatedEdit.edit": + if e.complexity.UpdatedEdit.Edit == nil { + break + } + + return e.complexity.UpdatedEdit.Edit(childComplexity), true + case "User.api_calls": if e.complexity.User.APICalls == nil { break @@ -4088,6 +4150,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputMultiIDCriterionInput, ec.unmarshalInputMultiStringCriterionInput, ec.unmarshalInputNewUserInput, + ec.unmarshalInputNotificationQueryInput, ec.unmarshalInputPerformerAppearanceInput, ec.unmarshalInputPerformerCreateInput, ec.unmarshalInputPerformerDestroyInput, @@ -4485,31 +4548,67 @@ input URLInput { } `, BuiltIn: false}, {Name: "../../graphql/schema/types/notifications.graphql", Input: `type Notification { - user: User! - edit: Edit! - data: NotificationData! created: Time! read: Boolean! + data: NotificationData! +} + +union NotificationData = + | FavoritePerformerScene + | FavoritePerformerEdit + | FavoriteStudioScene + | FavoriteStudioEdit + | CommentOwnEdit + | CommentCommentedEdit + | CommentVotedEdit + | DownvoteOwnEdit + | FailedOwnEdit + | UpdatedEdit + +type FavoritePerformerScene { + scene: Scene! +} + +type FavoritePerformerEdit { + edit: Edit! +} + +type FavoriteStudioScene { + scene: Scene! } -type Notifications { - notifications: [Notification!]! - unreadCount: Int! +type FavoriteStudioEdit { + edit: Edit! +} + +type CommentOwnEdit { + comment: EditComment! +} + +type DownvoteOwnEdit { + edit: Edit! } -type NegativeVoteNotification { - user: User! +type FailedOwnEdit { + edit: Edit! } -type CommentNotification { +type CommentCommentedEdit { comment: EditComment! } -type FailedVoteNotification { - message: String! +type CommentVotedEdit { + comment: EditComment! } -union NotificationData = NegativeVoteNotification | CommentNotification | FailedVoteNotification +type UpdatedEdit { + edit: Edit! +} + +input NotificationQueryInput { + page: Int! = 1 + per_page: Int! = 25 +} `, BuiltIn: false}, {Name: "../../graphql/schema/types/performer.graphql", Input: `enum GenderEnum { MALE @@ -5666,7 +5765,8 @@ type Query { ### Instance Config ### getConfig: StashBoxConfig! - getNotifications: Notifications! @hasRole(role: READ) + queryNotifications(input: NotificationQueryInput!): [Notification!]! @hasRole(role: READ) + getUnreadNotificationCount: Int! @hasRole(role: READ) } type Mutation { @@ -5766,7 +5866,7 @@ type Mutation { favoriteStudio(id: ID!, favorite: Boolean!): Boolean! @hasRole(role: READ) """Mark all of the current users notifications as read.""" - markNotificationsRead: Notifications! @hasRole(role: READ) + markNotificationsRead: Boolean! @hasRole(role: READ) } schema { @@ -6867,6 +6967,21 @@ func (ec *executionContext) field_Query_queryExistingScene_args(ctx context.Cont return args, nil } +func (ec *executionContext) field_Query_queryNotifications_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 NotificationQueryInput + if tmp, ok := rawArgs["input"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("input")) + arg0, err = ec.unmarshalNNotificationQueryInput2githubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐNotificationQueryInput(ctx, tmp) + if err != nil { + return nil, err + } + } + args["input"] = arg0 + return args, nil +} + func (ec *executionContext) field_Query_queryPerformers_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -7167,8 +7282,8 @@ func (ec *executionContext) fieldContext_BodyModification_description(ctx contex return fc, nil } -func (ec *executionContext) _CommentNotification_comment(ctx context.Context, field graphql.CollectedField, obj *CommentNotification) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_CommentNotification_comment(ctx, field) +func (ec *executionContext) _CommentCommentedEdit_comment(ctx context.Context, field graphql.CollectedField, obj *CommentCommentedEdit) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_CommentCommentedEdit_comment(ctx, field) if err != nil { return graphql.Null } @@ -7198,9 +7313,9 @@ func (ec *executionContext) _CommentNotification_comment(ctx context.Context, fi return ec.marshalNEditComment2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐEditComment(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_CommentNotification_comment(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_CommentCommentedEdit_comment(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "CommentNotification", + Object: "CommentCommentedEdit", Field: field, IsMethod: false, IsResolver: false, @@ -7221,6 +7336,200 @@ func (ec *executionContext) fieldContext_CommentNotification_comment(ctx context return fc, nil } +func (ec *executionContext) _CommentOwnEdit_comment(ctx context.Context, field graphql.CollectedField, obj *CommentOwnEdit) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_CommentOwnEdit_comment(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Comment, nil + }) + 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.(*EditComment) + fc.Result = res + return ec.marshalNEditComment2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐEditComment(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_CommentOwnEdit_comment(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "CommentOwnEdit", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_EditComment_id(ctx, field) + case "user": + return ec.fieldContext_EditComment_user(ctx, field) + case "date": + return ec.fieldContext_EditComment_date(ctx, field) + case "comment": + return ec.fieldContext_EditComment_comment(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type EditComment", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _CommentVotedEdit_comment(ctx context.Context, field graphql.CollectedField, obj *CommentVotedEdit) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_CommentVotedEdit_comment(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Comment, nil + }) + 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.(*EditComment) + fc.Result = res + return ec.marshalNEditComment2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐEditComment(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_CommentVotedEdit_comment(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "CommentVotedEdit", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_EditComment_id(ctx, field) + case "user": + return ec.fieldContext_EditComment_user(ctx, field) + case "date": + return ec.fieldContext_EditComment_date(ctx, field) + case "comment": + return ec.fieldContext_EditComment_comment(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type EditComment", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _DownvoteOwnEdit_edit(ctx context.Context, field graphql.CollectedField, obj *DownvoteOwnEdit) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DownvoteOwnEdit_edit(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Edit, nil + }) + 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.(*Edit) + fc.Result = res + return ec.marshalNEdit2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐEdit(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_DownvoteOwnEdit_edit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "DownvoteOwnEdit", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Edit_id(ctx, field) + case "user": + return ec.fieldContext_Edit_user(ctx, field) + case "target": + return ec.fieldContext_Edit_target(ctx, field) + case "target_type": + return ec.fieldContext_Edit_target_type(ctx, field) + case "merge_sources": + return ec.fieldContext_Edit_merge_sources(ctx, field) + case "operation": + return ec.fieldContext_Edit_operation(ctx, field) + case "bot": + return ec.fieldContext_Edit_bot(ctx, field) + case "details": + return ec.fieldContext_Edit_details(ctx, field) + case "old_details": + return ec.fieldContext_Edit_old_details(ctx, field) + case "options": + return ec.fieldContext_Edit_options(ctx, field) + case "comments": + return ec.fieldContext_Edit_comments(ctx, field) + case "votes": + return ec.fieldContext_Edit_votes(ctx, field) + case "vote_count": + return ec.fieldContext_Edit_vote_count(ctx, field) + case "destructive": + return ec.fieldContext_Edit_destructive(ctx, field) + case "status": + return ec.fieldContext_Edit_status(ctx, field) + case "applied": + return ec.fieldContext_Edit_applied(ctx, field) + case "created": + return ec.fieldContext_Edit_created(ctx, field) + case "updated": + return ec.fieldContext_Edit_updated(ctx, field) + case "closed": + return ec.fieldContext_Edit_closed(ctx, field) + case "expires": + return ec.fieldContext_Edit_expires(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Edit", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _Draft_id(ctx context.Context, field graphql.CollectedField, obj *Draft) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Draft_id(ctx, field) if err != nil { @@ -8909,8 +9218,8 @@ func (ec *executionContext) fieldContext_EditVote_vote(ctx context.Context, fiel return fc, nil } -func (ec *executionContext) _FailedVoteNotification_message(ctx context.Context, field graphql.CollectedField, obj *FailedVoteNotification) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_FailedVoteNotification_message(ctx, field) +func (ec *executionContext) _FailedOwnEdit_edit(ctx context.Context, field graphql.CollectedField, obj *FailedOwnEdit) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_FailedOwnEdit_edit(ctx, field) if err != nil { return graphql.Null } @@ -8923,7 +9232,7 @@ func (ec *executionContext) _FailedVoteNotification_message(ctx context.Context, }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Message, nil + return obj.Edit, nil }) if err != nil { ec.Error(ctx, err) @@ -8935,19 +9244,397 @@ func (ec *executionContext) _FailedVoteNotification_message(ctx context.Context, } return graphql.Null } - res := resTmp.(string) + res := resTmp.(*Edit) fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) + return ec.marshalNEdit2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐEdit(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_FailedVoteNotification_message(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_FailedOwnEdit_edit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "FailedVoteNotification", + Object: "FailedOwnEdit", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") + switch field.Name { + case "id": + return ec.fieldContext_Edit_id(ctx, field) + case "user": + return ec.fieldContext_Edit_user(ctx, field) + case "target": + return ec.fieldContext_Edit_target(ctx, field) + case "target_type": + return ec.fieldContext_Edit_target_type(ctx, field) + case "merge_sources": + return ec.fieldContext_Edit_merge_sources(ctx, field) + case "operation": + return ec.fieldContext_Edit_operation(ctx, field) + case "bot": + return ec.fieldContext_Edit_bot(ctx, field) + case "details": + return ec.fieldContext_Edit_details(ctx, field) + case "old_details": + return ec.fieldContext_Edit_old_details(ctx, field) + case "options": + return ec.fieldContext_Edit_options(ctx, field) + case "comments": + return ec.fieldContext_Edit_comments(ctx, field) + case "votes": + return ec.fieldContext_Edit_votes(ctx, field) + case "vote_count": + return ec.fieldContext_Edit_vote_count(ctx, field) + case "destructive": + return ec.fieldContext_Edit_destructive(ctx, field) + case "status": + return ec.fieldContext_Edit_status(ctx, field) + case "applied": + return ec.fieldContext_Edit_applied(ctx, field) + case "created": + return ec.fieldContext_Edit_created(ctx, field) + case "updated": + return ec.fieldContext_Edit_updated(ctx, field) + case "closed": + return ec.fieldContext_Edit_closed(ctx, field) + case "expires": + return ec.fieldContext_Edit_expires(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Edit", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _FavoritePerformerEdit_edit(ctx context.Context, field graphql.CollectedField, obj *FavoritePerformerEdit) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_FavoritePerformerEdit_edit(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Edit, nil + }) + 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.(*Edit) + fc.Result = res + return ec.marshalNEdit2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐEdit(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_FavoritePerformerEdit_edit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "FavoritePerformerEdit", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Edit_id(ctx, field) + case "user": + return ec.fieldContext_Edit_user(ctx, field) + case "target": + return ec.fieldContext_Edit_target(ctx, field) + case "target_type": + return ec.fieldContext_Edit_target_type(ctx, field) + case "merge_sources": + return ec.fieldContext_Edit_merge_sources(ctx, field) + case "operation": + return ec.fieldContext_Edit_operation(ctx, field) + case "bot": + return ec.fieldContext_Edit_bot(ctx, field) + case "details": + return ec.fieldContext_Edit_details(ctx, field) + case "old_details": + return ec.fieldContext_Edit_old_details(ctx, field) + case "options": + return ec.fieldContext_Edit_options(ctx, field) + case "comments": + return ec.fieldContext_Edit_comments(ctx, field) + case "votes": + return ec.fieldContext_Edit_votes(ctx, field) + case "vote_count": + return ec.fieldContext_Edit_vote_count(ctx, field) + case "destructive": + return ec.fieldContext_Edit_destructive(ctx, field) + case "status": + return ec.fieldContext_Edit_status(ctx, field) + case "applied": + return ec.fieldContext_Edit_applied(ctx, field) + case "created": + return ec.fieldContext_Edit_created(ctx, field) + case "updated": + return ec.fieldContext_Edit_updated(ctx, field) + case "closed": + return ec.fieldContext_Edit_closed(ctx, field) + case "expires": + return ec.fieldContext_Edit_expires(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Edit", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _FavoritePerformerScene_scene(ctx context.Context, field graphql.CollectedField, obj *FavoritePerformerScene) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_FavoritePerformerScene_scene(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Scene, nil + }) + 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.(*Scene) + fc.Result = res + return ec.marshalNScene2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐScene(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_FavoritePerformerScene_scene(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "FavoritePerformerScene", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Scene_id(ctx, field) + case "title": + return ec.fieldContext_Scene_title(ctx, field) + case "details": + return ec.fieldContext_Scene_details(ctx, field) + case "date": + return ec.fieldContext_Scene_date(ctx, field) + case "release_date": + return ec.fieldContext_Scene_release_date(ctx, field) + case "urls": + return ec.fieldContext_Scene_urls(ctx, field) + case "studio": + return ec.fieldContext_Scene_studio(ctx, field) + case "tags": + return ec.fieldContext_Scene_tags(ctx, field) + case "images": + return ec.fieldContext_Scene_images(ctx, field) + case "performers": + return ec.fieldContext_Scene_performers(ctx, field) + case "fingerprints": + return ec.fieldContext_Scene_fingerprints(ctx, field) + case "duration": + return ec.fieldContext_Scene_duration(ctx, field) + case "director": + return ec.fieldContext_Scene_director(ctx, field) + case "code": + return ec.fieldContext_Scene_code(ctx, field) + case "deleted": + return ec.fieldContext_Scene_deleted(ctx, field) + case "edits": + return ec.fieldContext_Scene_edits(ctx, field) + case "created": + return ec.fieldContext_Scene_created(ctx, field) + case "updated": + return ec.fieldContext_Scene_updated(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Scene", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _FavoriteStudioEdit_edit(ctx context.Context, field graphql.CollectedField, obj *FavoriteStudioEdit) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_FavoriteStudioEdit_edit(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Edit, nil + }) + 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.(*Edit) + fc.Result = res + return ec.marshalNEdit2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐEdit(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_FavoriteStudioEdit_edit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "FavoriteStudioEdit", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Edit_id(ctx, field) + case "user": + return ec.fieldContext_Edit_user(ctx, field) + case "target": + return ec.fieldContext_Edit_target(ctx, field) + case "target_type": + return ec.fieldContext_Edit_target_type(ctx, field) + case "merge_sources": + return ec.fieldContext_Edit_merge_sources(ctx, field) + case "operation": + return ec.fieldContext_Edit_operation(ctx, field) + case "bot": + return ec.fieldContext_Edit_bot(ctx, field) + case "details": + return ec.fieldContext_Edit_details(ctx, field) + case "old_details": + return ec.fieldContext_Edit_old_details(ctx, field) + case "options": + return ec.fieldContext_Edit_options(ctx, field) + case "comments": + return ec.fieldContext_Edit_comments(ctx, field) + case "votes": + return ec.fieldContext_Edit_votes(ctx, field) + case "vote_count": + return ec.fieldContext_Edit_vote_count(ctx, field) + case "destructive": + return ec.fieldContext_Edit_destructive(ctx, field) + case "status": + return ec.fieldContext_Edit_status(ctx, field) + case "applied": + return ec.fieldContext_Edit_applied(ctx, field) + case "created": + return ec.fieldContext_Edit_created(ctx, field) + case "updated": + return ec.fieldContext_Edit_updated(ctx, field) + case "closed": + return ec.fieldContext_Edit_closed(ctx, field) + case "expires": + return ec.fieldContext_Edit_expires(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Edit", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _FavoriteStudioScene_scene(ctx context.Context, field graphql.CollectedField, obj *FavoriteStudioScene) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_FavoriteStudioScene_scene(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Scene, nil + }) + 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.(*Scene) + fc.Result = res + return ec.marshalNScene2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐScene(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_FavoriteStudioScene_scene(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "FavoriteStudioScene", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Scene_id(ctx, field) + case "title": + return ec.fieldContext_Scene_title(ctx, field) + case "details": + return ec.fieldContext_Scene_details(ctx, field) + case "date": + return ec.fieldContext_Scene_date(ctx, field) + case "release_date": + return ec.fieldContext_Scene_release_date(ctx, field) + case "urls": + return ec.fieldContext_Scene_urls(ctx, field) + case "studio": + return ec.fieldContext_Scene_studio(ctx, field) + case "tags": + return ec.fieldContext_Scene_tags(ctx, field) + case "images": + return ec.fieldContext_Scene_images(ctx, field) + case "performers": + return ec.fieldContext_Scene_performers(ctx, field) + case "fingerprints": + return ec.fieldContext_Scene_fingerprints(ctx, field) + case "duration": + return ec.fieldContext_Scene_duration(ctx, field) + case "director": + return ec.fieldContext_Scene_director(ctx, field) + case "code": + return ec.fieldContext_Scene_code(ctx, field) + case "deleted": + return ec.fieldContext_Scene_deleted(ctx, field) + case "edits": + return ec.fieldContext_Scene_edits(ctx, field) + case "created": + return ec.fieldContext_Scene_created(ctx, field) + case "updated": + return ec.fieldContext_Scene_updated(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Scene", field.Name) }, } return fc, nil @@ -14301,10 +14988,10 @@ func (ec *executionContext) _Mutation_markNotificationsRead(ctx context.Context, if tmp == nil { return nil, nil } - if data, ok := tmp.(*Notifications); ok { + if data, ok := tmp.(bool); ok { return data, nil } - return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/stashapp/stash-box/pkg/models.Notifications`, tmp) + return nil, fmt.Errorf(`unexpected type %T from directive, should be bool`, tmp) }) if err != nil { ec.Error(ctx, err) @@ -14315,9 +15002,9 @@ func (ec *executionContext) _Mutation_markNotificationsRead(ctx context.Context, } return graphql.Null } - res := resTmp.(*Notifications) + res := resTmp.(bool) fc.Result = res - return ec.marshalNNotifications2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐNotifications(ctx, field.Selections, res) + return ec.marshalNBoolean2bool(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_Mutation_markNotificationsRead(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -14327,88 +15014,14 @@ func (ec *executionContext) fieldContext_Mutation_markNotificationsRead(ctx cont IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "notifications": - return ec.fieldContext_Notifications_notifications(ctx, field) - case "unreadCount": - return ec.fieldContext_Notifications_unreadCount(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Notifications", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _NegativeVoteNotification_user(ctx context.Context, field graphql.CollectedField, obj *NegativeVoteNotification) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_NegativeVoteNotification_user(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.User, nil - }) - 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.(*User) - fc.Result = res - return ec.marshalNUser2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐUser(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_NegativeVoteNotification_user(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "NegativeVoteNotification", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "id": - return ec.fieldContext_User_id(ctx, field) - case "name": - return ec.fieldContext_User_name(ctx, field) - case "roles": - return ec.fieldContext_User_roles(ctx, field) - case "email": - return ec.fieldContext_User_email(ctx, field) - case "api_key": - return ec.fieldContext_User_api_key(ctx, field) - case "vote_count": - return ec.fieldContext_User_vote_count(ctx, field) - case "edit_count": - return ec.fieldContext_User_edit_count(ctx, field) - case "api_calls": - return ec.fieldContext_User_api_calls(ctx, field) - case "invited_by": - return ec.fieldContext_User_invited_by(ctx, field) - case "invite_tokens": - return ec.fieldContext_User_invite_tokens(ctx, field) - case "active_invite_codes": - return ec.fieldContext_User_active_invite_codes(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type User", field.Name) + return nil, errors.New("field of type Boolean does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Notification_user(ctx context.Context, field graphql.CollectedField, obj *Notification) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Notification_user(ctx, field) +func (ec *executionContext) _Notification_created(ctx context.Context, field graphql.CollectedField, obj *Notification) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Notification_created(ctx, field) if err != nil { return graphql.Null } @@ -14421,7 +15034,7 @@ func (ec *executionContext) _Notification_user(ctx context.Context, field graphq }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.User, nil + return ec.resolvers.Notification().Created(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -14433,50 +15046,26 @@ func (ec *executionContext) _Notification_user(ctx context.Context, field graphq } return graphql.Null } - res := resTmp.(*User) + res := resTmp.(*time.Time) fc.Result = res - return ec.marshalNUser2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐUser(ctx, field.Selections, res) + return ec.marshalNTime2ᚖtimeᚐTime(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Notification_user(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Notification_created(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Notification", Field: field, - IsMethod: false, - IsResolver: false, + IsMethod: true, + IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "id": - return ec.fieldContext_User_id(ctx, field) - case "name": - return ec.fieldContext_User_name(ctx, field) - case "roles": - return ec.fieldContext_User_roles(ctx, field) - case "email": - return ec.fieldContext_User_email(ctx, field) - case "api_key": - return ec.fieldContext_User_api_key(ctx, field) - case "vote_count": - return ec.fieldContext_User_vote_count(ctx, field) - case "edit_count": - return ec.fieldContext_User_edit_count(ctx, field) - case "api_calls": - return ec.fieldContext_User_api_calls(ctx, field) - case "invited_by": - return ec.fieldContext_User_invited_by(ctx, field) - case "invite_tokens": - return ec.fieldContext_User_invite_tokens(ctx, field) - case "active_invite_codes": - return ec.fieldContext_User_active_invite_codes(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type User", field.Name) + return nil, errors.New("field of type Time does not have child fields") }, } return fc, nil } -func (ec *executionContext) _Notification_edit(ctx context.Context, field graphql.CollectedField, obj *Notification) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Notification_edit(ctx, field) +func (ec *executionContext) _Notification_read(ctx context.Context, field graphql.CollectedField, obj *Notification) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Notification_read(ctx, field) if err != nil { return graphql.Null } @@ -14489,7 +15078,7 @@ func (ec *executionContext) _Notification_edit(ctx context.Context, field graphq }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Edit, nil + return ec.resolvers.Notification().Read(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -14501,61 +15090,19 @@ func (ec *executionContext) _Notification_edit(ctx context.Context, field graphq } return graphql.Null } - res := resTmp.(*Edit) + res := resTmp.(bool) fc.Result = res - return ec.marshalNEdit2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐEdit(ctx, field.Selections, res) + return ec.marshalNBoolean2bool(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Notification_edit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Notification_read(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Notification", Field: field, - IsMethod: false, - IsResolver: false, + IsMethod: true, + IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "id": - return ec.fieldContext_Edit_id(ctx, field) - case "user": - return ec.fieldContext_Edit_user(ctx, field) - case "target": - return ec.fieldContext_Edit_target(ctx, field) - case "target_type": - return ec.fieldContext_Edit_target_type(ctx, field) - case "merge_sources": - return ec.fieldContext_Edit_merge_sources(ctx, field) - case "operation": - return ec.fieldContext_Edit_operation(ctx, field) - case "bot": - return ec.fieldContext_Edit_bot(ctx, field) - case "details": - return ec.fieldContext_Edit_details(ctx, field) - case "old_details": - return ec.fieldContext_Edit_old_details(ctx, field) - case "options": - return ec.fieldContext_Edit_options(ctx, field) - case "comments": - return ec.fieldContext_Edit_comments(ctx, field) - case "votes": - return ec.fieldContext_Edit_votes(ctx, field) - case "vote_count": - return ec.fieldContext_Edit_vote_count(ctx, field) - case "destructive": - return ec.fieldContext_Edit_destructive(ctx, field) - case "status": - return ec.fieldContext_Edit_status(ctx, field) - case "applied": - return ec.fieldContext_Edit_applied(ctx, field) - case "created": - return ec.fieldContext_Edit_created(ctx, field) - case "updated": - return ec.fieldContext_Edit_updated(ctx, field) - case "closed": - return ec.fieldContext_Edit_closed(ctx, field) - case "expires": - return ec.fieldContext_Edit_expires(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Edit", field.Name) + return nil, errors.New("field of type Boolean does not have child fields") }, } return fc, nil @@ -14575,7 +15122,7 @@ func (ec *executionContext) _Notification_data(ctx context.Context, field graphq }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.Data, nil + return ec.resolvers.Notification().Data(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -14596,8 +15143,8 @@ func (ec *executionContext) fieldContext_Notification_data(ctx context.Context, fc = &graphql.FieldContext{ Object: "Notification", Field: field, - IsMethod: false, - IsResolver: false, + IsMethod: true, + IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { return nil, errors.New("field of type NotificationData does not have child fields") }, @@ -14605,194 +15152,6 @@ func (ec *executionContext) fieldContext_Notification_data(ctx context.Context, return fc, nil } -func (ec *executionContext) _Notification_created(ctx context.Context, field graphql.CollectedField, obj *Notification) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Notification_created(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Created, nil - }) - 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.(time.Time) - fc.Result = res - return ec.marshalNTime2timeᚐTime(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_Notification_created(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Notification", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Time does not have child fields") - }, - } - return fc, nil -} - -func (ec *executionContext) _Notification_read(ctx context.Context, field graphql.CollectedField, obj *Notification) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Notification_read(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Read, nil - }) - 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.(bool) - fc.Result = res - return ec.marshalNBoolean2bool(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_Notification_read(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Notification", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Boolean does not have child fields") - }, - } - return fc, nil -} - -func (ec *executionContext) _Notifications_notifications(ctx context.Context, field graphql.CollectedField, obj *Notifications) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Notifications_notifications(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.Notifications, nil - }) - 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.([]*Notification) - fc.Result = res - return ec.marshalNNotification2ᚕᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐNotificationᚄ(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_Notifications_notifications(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Notifications", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "user": - return ec.fieldContext_Notification_user(ctx, field) - case "edit": - return ec.fieldContext_Notification_edit(ctx, field) - case "data": - return ec.fieldContext_Notification_data(ctx, field) - case "created": - return ec.fieldContext_Notification_created(ctx, field) - case "read": - return ec.fieldContext_Notification_read(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Notification", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _Notifications_unreadCount(ctx context.Context, field graphql.CollectedField, obj *Notifications) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Notifications_unreadCount(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return obj.UnreadCount, nil - }) - 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.(int) - fc.Result = res - return ec.marshalNInt2int(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_Notifications_unreadCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Notifications", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type Int does not have child fields") - }, - } - return fc, nil -} - func (ec *executionContext) _Performer_id(ctx context.Context, field graphql.CollectedField, obj *Performer) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Performer_id(ctx, field) if err != nil { @@ -21670,8 +22029,8 @@ func (ec *executionContext) fieldContext_Query_getConfig(ctx context.Context, fi return fc, nil } -func (ec *executionContext) _Query_getNotifications(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_getNotifications(ctx, field) +func (ec *executionContext) _Query_queryNotifications(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_queryNotifications(ctx, field) if err != nil { return graphql.Null } @@ -21685,7 +22044,7 @@ func (ec *executionContext) _Query_getNotifications(ctx context.Context, field g resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { directive0 := func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().GetNotifications(rctx) + return ec.resolvers.Query().QueryNotifications(rctx, fc.Args["input"].(NotificationQueryInput)) } directive1 := func(ctx context.Context) (interface{}, error) { role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐRoleEnum(ctx, "READ") @@ -21705,10 +22064,10 @@ func (ec *executionContext) _Query_getNotifications(ctx context.Context, field g if tmp == nil { return nil, nil } - if data, ok := tmp.(*Notifications); ok { + if data, ok := tmp.([]*Notification); ok { return data, nil } - return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/stashapp/stash-box/pkg/models.Notifications`, tmp) + return nil, fmt.Errorf(`unexpected type %T from directive, should be []*github.com/stashapp/stash-box/pkg/models.Notification`, tmp) }) if err != nil { ec.Error(ctx, err) @@ -21719,12 +22078,12 @@ func (ec *executionContext) _Query_getNotifications(ctx context.Context, field g } return graphql.Null } - res := resTmp.(*Notifications) + res := resTmp.([]*Notification) fc.Result = res - return ec.marshalNNotifications2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐNotifications(ctx, field.Selections, res) + return ec.marshalNNotification2ᚕᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐNotificationᚄ(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_getNotifications(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Query_queryNotifications(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Query", Field: field, @@ -21732,12 +22091,92 @@ func (ec *executionContext) fieldContext_Query_getNotifications(ctx context.Cont IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "notifications": - return ec.fieldContext_Notifications_notifications(ctx, field) - case "unreadCount": - return ec.fieldContext_Notifications_unreadCount(ctx, field) + case "created": + return ec.fieldContext_Notification_created(ctx, field) + case "read": + return ec.fieldContext_Notification_read(ctx, field) + case "data": + return ec.fieldContext_Notification_data(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Notification", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_queryNotifications_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return + } + return fc, nil +} + +func (ec *executionContext) _Query_getUnreadNotificationCount(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_getUnreadNotificationCount(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().GetUnreadNotificationCount(rctx) + } + directive1 := func(ctx context.Context) (interface{}, error) { + role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐRoleEnum(ctx, "READ") + if err != nil { + return nil, err + } + if ec.directives.HasRole == nil { + return nil, errors.New("directive hasRole is not implemented") } - return nil, fmt.Errorf("no field named %q was found under type Notifications", field.Name) + return ec.directives.HasRole(ctx, nil, directive0, role) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(int); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be int`, tmp) + }) + if err != nil { + ec.Error(ctx, err) + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_getUnreadNotificationCount(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") }, } return fc, nil @@ -28319,6 +28758,92 @@ func (ec *executionContext) fieldContext_URL_site(ctx context.Context, field gra return fc, nil } +func (ec *executionContext) _UpdatedEdit_edit(ctx context.Context, field graphql.CollectedField, obj *UpdatedEdit) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_UpdatedEdit_edit(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Edit, nil + }) + 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.(*Edit) + fc.Result = res + return ec.marshalNEdit2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐEdit(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_UpdatedEdit_edit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "UpdatedEdit", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Edit_id(ctx, field) + case "user": + return ec.fieldContext_Edit_user(ctx, field) + case "target": + return ec.fieldContext_Edit_target(ctx, field) + case "target_type": + return ec.fieldContext_Edit_target_type(ctx, field) + case "merge_sources": + return ec.fieldContext_Edit_merge_sources(ctx, field) + case "operation": + return ec.fieldContext_Edit_operation(ctx, field) + case "bot": + return ec.fieldContext_Edit_bot(ctx, field) + case "details": + return ec.fieldContext_Edit_details(ctx, field) + case "old_details": + return ec.fieldContext_Edit_old_details(ctx, field) + case "options": + return ec.fieldContext_Edit_options(ctx, field) + case "comments": + return ec.fieldContext_Edit_comments(ctx, field) + case "votes": + return ec.fieldContext_Edit_votes(ctx, field) + case "vote_count": + return ec.fieldContext_Edit_vote_count(ctx, field) + case "destructive": + return ec.fieldContext_Edit_destructive(ctx, field) + case "status": + return ec.fieldContext_Edit_status(ctx, field) + case "applied": + return ec.fieldContext_Edit_applied(ctx, field) + case "created": + return ec.fieldContext_Edit_created(ctx, field) + case "updated": + return ec.fieldContext_Edit_updated(ctx, field) + case "closed": + return ec.fieldContext_Edit_closed(ctx, field) + case "expires": + return ec.fieldContext_Edit_expires(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Edit", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _User_id(ctx context.Context, field graphql.CollectedField, obj *User) (ret graphql.Marshaler) { fc, err := ec.fieldContext_User_id(ctx, field) if err != nil { @@ -32623,6 +33148,49 @@ func (ec *executionContext) unmarshalInputNewUserInput(ctx context.Context, obj return it, nil } +func (ec *executionContext) unmarshalInputNotificationQueryInput(ctx context.Context, obj interface{}) (NotificationQueryInput, error) { + var it NotificationQueryInput + asMap := map[string]interface{}{} + for k, v := range obj.(map[string]interface{}) { + asMap[k] = v + } + + if _, present := asMap["page"]; !present { + asMap["page"] = 1 + } + if _, present := asMap["per_page"]; !present { + asMap["per_page"] = 25 + } + + fieldsInOrder := [...]string{"page", "per_page"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "page": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) + it.Page, err = ec.unmarshalNInt2int(ctx, v) + if err != nil { + return it, err + } + case "per_page": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("per_page")) + it.PerPage, err = ec.unmarshalNInt2int(ctx, v) + if err != nil { + return it, err + } + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputPerformerAppearanceInput(ctx context.Context, obj interface{}) (PerformerAppearanceInput, error) { var it PerformerAppearanceInput asMap := map[string]interface{}{} @@ -36125,27 +36693,76 @@ func (ec *executionContext) _NotificationData(ctx context.Context, sel ast.Selec switch obj := (obj).(type) { case nil: return graphql.Null - case NegativeVoteNotification: - return ec._NegativeVoteNotification(ctx, sel, &obj) - case *NegativeVoteNotification: + case FavoritePerformerScene: + return ec._FavoritePerformerScene(ctx, sel, &obj) + case *FavoritePerformerScene: + if obj == nil { + return graphql.Null + } + return ec._FavoritePerformerScene(ctx, sel, obj) + case FavoritePerformerEdit: + return ec._FavoritePerformerEdit(ctx, sel, &obj) + case *FavoritePerformerEdit: + if obj == nil { + return graphql.Null + } + return ec._FavoritePerformerEdit(ctx, sel, obj) + case FavoriteStudioScene: + return ec._FavoriteStudioScene(ctx, sel, &obj) + case *FavoriteStudioScene: + if obj == nil { + return graphql.Null + } + return ec._FavoriteStudioScene(ctx, sel, obj) + case FavoriteStudioEdit: + return ec._FavoriteStudioEdit(ctx, sel, &obj) + case *FavoriteStudioEdit: + if obj == nil { + return graphql.Null + } + return ec._FavoriteStudioEdit(ctx, sel, obj) + case CommentOwnEdit: + return ec._CommentOwnEdit(ctx, sel, &obj) + case *CommentOwnEdit: + if obj == nil { + return graphql.Null + } + return ec._CommentOwnEdit(ctx, sel, obj) + case CommentCommentedEdit: + return ec._CommentCommentedEdit(ctx, sel, &obj) + case *CommentCommentedEdit: if obj == nil { return graphql.Null } - return ec._NegativeVoteNotification(ctx, sel, obj) - case CommentNotification: - return ec._CommentNotification(ctx, sel, &obj) - case *CommentNotification: + return ec._CommentCommentedEdit(ctx, sel, obj) + case CommentVotedEdit: + return ec._CommentVotedEdit(ctx, sel, &obj) + case *CommentVotedEdit: if obj == nil { return graphql.Null } - return ec._CommentNotification(ctx, sel, obj) - case FailedVoteNotification: - return ec._FailedVoteNotification(ctx, sel, &obj) - case *FailedVoteNotification: + return ec._CommentVotedEdit(ctx, sel, obj) + case DownvoteOwnEdit: + return ec._DownvoteOwnEdit(ctx, sel, &obj) + case *DownvoteOwnEdit: if obj == nil { return graphql.Null } - return ec._FailedVoteNotification(ctx, sel, obj) + return ec._DownvoteOwnEdit(ctx, sel, obj) + case FailedOwnEdit: + return ec._FailedOwnEdit(ctx, sel, &obj) + case *FailedOwnEdit: + if obj == nil { + return graphql.Null + } + return ec._FailedOwnEdit(ctx, sel, obj) + case UpdatedEdit: + return ec._UpdatedEdit(ctx, sel, &obj) + case *UpdatedEdit: + if obj == nil { + return graphql.Null + } + return ec._UpdatedEdit(ctx, sel, obj) default: panic(fmt.Errorf("unexpected type %T", obj)) } @@ -36256,19 +36873,103 @@ func (ec *executionContext) _BodyModification(ctx context.Context, sel ast.Selec return out } -var commentNotificationImplementors = []string{"CommentNotification", "NotificationData"} +var commentCommentedEditImplementors = []string{"CommentCommentedEdit", "NotificationData"} + +func (ec *executionContext) _CommentCommentedEdit(ctx context.Context, sel ast.SelectionSet, obj *CommentCommentedEdit) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, commentCommentedEditImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("CommentCommentedEdit") + case "comment": + + out.Values[i] = ec._CommentCommentedEdit_comment(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var commentOwnEditImplementors = []string{"CommentOwnEdit", "NotificationData"} -func (ec *executionContext) _CommentNotification(ctx context.Context, sel ast.SelectionSet, obj *CommentNotification) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, commentNotificationImplementors) +func (ec *executionContext) _CommentOwnEdit(ctx context.Context, sel ast.SelectionSet, obj *CommentOwnEdit) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, commentOwnEditImplementors) out := graphql.NewFieldSet(fields) var invalids uint32 for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("CommentNotification") + out.Values[i] = graphql.MarshalString("CommentOwnEdit") case "comment": - out.Values[i] = ec._CommentNotification_comment(ctx, field, obj) + out.Values[i] = ec._CommentOwnEdit_comment(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var commentVotedEditImplementors = []string{"CommentVotedEdit", "NotificationData"} + +func (ec *executionContext) _CommentVotedEdit(ctx context.Context, sel ast.SelectionSet, obj *CommentVotedEdit) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, commentVotedEditImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("CommentVotedEdit") + case "comment": + + out.Values[i] = ec._CommentVotedEdit_comment(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var downvoteOwnEditImplementors = []string{"DownvoteOwnEdit", "NotificationData"} + +func (ec *executionContext) _DownvoteOwnEdit(ctx context.Context, sel ast.SelectionSet, obj *DownvoteOwnEdit) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, downvoteOwnEditImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("DownvoteOwnEdit") + case "edit": + + out.Values[i] = ec._DownvoteOwnEdit_edit(ctx, field, obj) if out.Values[i] == graphql.Null { invalids++ @@ -36901,73 +37602,185 @@ func (ec *executionContext) _EditComment(ctx context.Context, sel ast.SelectionS return out } -var editVoteImplementors = []string{"EditVote"} +var editVoteImplementors = []string{"EditVote"} + +func (ec *executionContext) _EditVote(ctx context.Context, sel ast.SelectionSet, obj *EditVote) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, editVoteImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("EditVote") + case "user": + field := field + + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._EditVote_user(ctx, field, obj) + return res + } + + out.Concurrently(i, func() graphql.Marshaler { + return innerFunc(ctx) + + }) + case "date": + field := field + + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._EditVote_date(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + } + + out.Concurrently(i, func() graphql.Marshaler { + return innerFunc(ctx) + + }) + case "vote": + field := field + + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._EditVote_vote(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + } + + out.Concurrently(i, func() graphql.Marshaler { + return innerFunc(ctx) + + }) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + +var failedOwnEditImplementors = []string{"FailedOwnEdit", "NotificationData"} -func (ec *executionContext) _EditVote(ctx context.Context, sel ast.SelectionSet, obj *EditVote) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, editVoteImplementors) +func (ec *executionContext) _FailedOwnEdit(ctx context.Context, sel ast.SelectionSet, obj *FailedOwnEdit) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, failedOwnEditImplementors) out := graphql.NewFieldSet(fields) var invalids uint32 for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("EditVote") - case "user": - field := field + out.Values[i] = graphql.MarshalString("FailedOwnEdit") + case "edit": - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._EditVote_user(ctx, field, obj) - return res + out.Values[i] = ec._FailedOwnEdit_edit(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) +var favoritePerformerEditImplementors = []string{"FavoritePerformerEdit", "NotificationData"} - }) - case "date": - field := field +func (ec *executionContext) _FavoritePerformerEdit(ctx context.Context, sel ast.SelectionSet, obj *FavoritePerformerEdit) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, favoritePerformerEditImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("FavoritePerformerEdit") + case "edit": - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._EditVote_date(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&invalids, 1) - } - return res + out.Values[i] = ec._FavoritePerformerEdit_edit(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) +var favoritePerformerSceneImplementors = []string{"FavoritePerformerScene", "NotificationData"} - }) - case "vote": - field := field +func (ec *executionContext) _FavoritePerformerScene(ctx context.Context, sel ast.SelectionSet, obj *FavoritePerformerScene) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, favoritePerformerSceneImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("FavoritePerformerScene") + case "scene": - innerFunc := func(ctx context.Context) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._EditVote_vote(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&invalids, 1) - } - return res + out.Values[i] = ec._FavoritePerformerScene_scene(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} - out.Concurrently(i, func() graphql.Marshaler { - return innerFunc(ctx) +var favoriteStudioEditImplementors = []string{"FavoriteStudioEdit", "NotificationData"} - }) +func (ec *executionContext) _FavoriteStudioEdit(ctx context.Context, sel ast.SelectionSet, obj *FavoriteStudioEdit) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, favoriteStudioEditImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("FavoriteStudioEdit") + case "edit": + + out.Values[i] = ec._FavoriteStudioEdit_edit(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -36979,19 +37792,19 @@ func (ec *executionContext) _EditVote(ctx context.Context, sel ast.SelectionSet, return out } -var failedVoteNotificationImplementors = []string{"FailedVoteNotification", "NotificationData"} +var favoriteStudioSceneImplementors = []string{"FavoriteStudioScene", "NotificationData"} -func (ec *executionContext) _FailedVoteNotification(ctx context.Context, sel ast.SelectionSet, obj *FailedVoteNotification) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, failedVoteNotificationImplementors) +func (ec *executionContext) _FavoriteStudioScene(ctx context.Context, sel ast.SelectionSet, obj *FavoriteStudioScene) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, favoriteStudioSceneImplementors) out := graphql.NewFieldSet(fields) var invalids uint32 for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("FailedVoteNotification") - case "message": + out.Values[i] = graphql.MarshalString("FavoriteStudioScene") + case "scene": - out.Values[i] = ec._FailedVoteNotification_message(ctx, field, obj) + out.Values[i] = ec._FavoriteStudioScene_scene(ctx, field, obj) if out.Values[i] == graphql.Null { invalids++ @@ -37543,34 +38356,6 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) return out } -var negativeVoteNotificationImplementors = []string{"NegativeVoteNotification", "NotificationData"} - -func (ec *executionContext) _NegativeVoteNotification(ctx context.Context, sel ast.SelectionSet, obj *NegativeVoteNotification) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, negativeVoteNotificationImplementors) - out := graphql.NewFieldSet(fields) - var invalids uint32 - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("NegativeVoteNotification") - case "user": - - out.Values[i] = ec._NegativeVoteNotification_user(ctx, field, obj) - - if out.Values[i] == graphql.Null { - invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch() - if invalids > 0 { - return graphql.Null - } - return out -} - var notificationImplementors = []string{"Notification"} func (ec *executionContext) _Notification(ctx context.Context, sel ast.SelectionSet, obj *Notification) graphql.Marshaler { @@ -37581,76 +38366,66 @@ func (ec *executionContext) _Notification(ctx context.Context, sel ast.Selection switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("Notification") - case "user": - - out.Values[i] = ec._Notification_user(ctx, field, obj) - - if out.Values[i] == graphql.Null { - invalids++ - } - case "edit": - - out.Values[i] = ec._Notification_edit(ctx, field, obj) - - if out.Values[i] == graphql.Null { - invalids++ - } - case "data": - - out.Values[i] = ec._Notification_data(ctx, field, obj) + case "created": + field := field - if out.Values[i] == graphql.Null { - invalids++ + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Notification_created(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res } - case "created": - out.Values[i] = ec._Notification_created(ctx, field, obj) + out.Concurrently(i, func() graphql.Marshaler { + return innerFunc(ctx) - if out.Values[i] == graphql.Null { - invalids++ - } + }) case "read": + field := field - out.Values[i] = ec._Notification_read(ctx, field, obj) - - if out.Values[i] == graphql.Null { - invalids++ + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Notification_read(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch() - if invalids > 0 { - return graphql.Null - } - return out -} - -var notificationsImplementors = []string{"Notifications"} -func (ec *executionContext) _Notifications(ctx context.Context, sel ast.SelectionSet, obj *Notifications) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, notificationsImplementors) - out := graphql.NewFieldSet(fields) - var invalids uint32 - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("Notifications") - case "notifications": + out.Concurrently(i, func() graphql.Marshaler { + return innerFunc(ctx) - out.Values[i] = ec._Notifications_notifications(ctx, field, obj) + }) + case "data": + field := field - if out.Values[i] == graphql.Null { - invalids++ + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Notification_data(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res } - case "unreadCount": - out.Values[i] = ec._Notifications_unreadCount(ctx, field, obj) + out.Concurrently(i, func() graphql.Marshaler { + return innerFunc(ctx) - if out.Values[i] == graphql.Null { - invalids++ - } + }) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -39409,7 +40184,27 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr out.Concurrently(i, func() graphql.Marshaler { return rrm(innerCtx) }) - case "getNotifications": + case "queryNotifications": + field := field + + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_queryNotifications(ctx, field) + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, innerFunc) + } + + out.Concurrently(i, func() graphql.Marshaler { + return rrm(innerCtx) + }) + case "getUnreadNotificationCount": field := field innerFunc := func(ctx context.Context) (res graphql.Marshaler) { @@ -39418,7 +40213,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Query_getNotifications(ctx, field) + res = ec._Query_getUnreadNotificationCount(ctx, field) return res } @@ -41595,6 +42390,34 @@ func (ec *executionContext) _URL(ctx context.Context, sel ast.SelectionSet, obj return out } +var updatedEditImplementors = []string{"UpdatedEdit", "NotificationData"} + +func (ec *executionContext) _UpdatedEdit(ctx context.Context, sel ast.SelectionSet, obj *UpdatedEdit) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, updatedEditImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("UpdatedEdit") + case "edit": + + out.Values[i] = ec._UpdatedEdit_edit(ctx, field, obj) + + if out.Values[i] == graphql.Null { + invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + var userImplementors = []string{"User"} func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj *User) graphql.Marshaler { @@ -43141,18 +43964,9 @@ func (ec *executionContext) marshalNNotificationData2githubᚗcomᚋstashappᚋs return ec._NotificationData(ctx, sel, v) } -func (ec *executionContext) marshalNNotifications2githubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐNotifications(ctx context.Context, sel ast.SelectionSet, v Notifications) graphql.Marshaler { - return ec._Notifications(ctx, sel, &v) -} - -func (ec *executionContext) marshalNNotifications2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐNotifications(ctx context.Context, sel ast.SelectionSet, v *Notifications) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._Notifications(ctx, sel, v) +func (ec *executionContext) unmarshalNNotificationQueryInput2githubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐNotificationQueryInput(ctx context.Context, v interface{}) (NotificationQueryInput, error) { + res, err := ec.unmarshalInputNotificationQueryInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) } func (ec *executionContext) unmarshalNOperationEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐOperationEnum(ctx context.Context, v interface{}) (OperationEnum, error) { diff --git a/pkg/models/generated_models.go b/pkg/models/generated_models.go index 7919a835e..469781bf5 100644 --- a/pkg/models/generated_models.go +++ b/pkg/models/generated_models.go @@ -66,17 +66,35 @@ type CancelEditInput struct { ID uuid.UUID `json:"id"` } -type CommentNotification struct { +type CommentCommentedEdit struct { Comment *EditComment `json:"comment"` } -func (CommentNotification) IsNotificationData() {} +func (CommentCommentedEdit) IsNotificationData() {} + +type CommentOwnEdit struct { + Comment *EditComment `json:"comment"` +} + +func (CommentOwnEdit) IsNotificationData() {} + +type CommentVotedEdit struct { + Comment *EditComment `json:"comment"` +} + +func (CommentVotedEdit) IsNotificationData() {} type DateCriterionInput struct { Value string `json:"value"` Modifier CriterionModifier `json:"modifier"` } +type DownvoteOwnEdit struct { + Edit *Edit `json:"edit"` +} + +func (DownvoteOwnEdit) IsNotificationData() {} + type DraftEntityInput struct { Name string `json:"name"` ID *uuid.UUID `json:"id"` @@ -145,11 +163,35 @@ type EyeColorCriterionInput struct { Modifier CriterionModifier `json:"modifier"` } -type FailedVoteNotification struct { - Message string `json:"message"` +type FailedOwnEdit struct { + Edit *Edit `json:"edit"` +} + +func (FailedOwnEdit) IsNotificationData() {} + +type FavoritePerformerEdit struct { + Edit *Edit `json:"edit"` } -func (FailedVoteNotification) IsNotificationData() {} +func (FavoritePerformerEdit) IsNotificationData() {} + +type FavoritePerformerScene struct { + Scene *Scene `json:"scene"` +} + +func (FavoritePerformerScene) IsNotificationData() {} + +type FavoriteStudioEdit struct { + Edit *Edit `json:"edit"` +} + +func (FavoriteStudioEdit) IsNotificationData() {} + +type FavoriteStudioScene struct { + Scene *Scene `json:"scene"` +} + +func (FavoriteStudioScene) IsNotificationData() {} type Fingerprint struct { Hash string `json:"hash"` @@ -246,28 +288,14 @@ type MultiStringCriterionInput struct { Modifier CriterionModifier `json:"modifier"` } -type NegativeVoteNotification struct { - User *User `json:"user"` -} - -func (NegativeVoteNotification) IsNotificationData() {} - type NewUserInput struct { Email string `json:"email"` InviteKey *string `json:"invite_key"` } -type Notification struct { - User *User `json:"user"` - Edit *Edit `json:"edit"` - Data NotificationData `json:"data"` - Created time.Time `json:"created"` - Read bool `json:"read"` -} - -type Notifications struct { - Notifications []*Notification `json:"notifications"` - UnreadCount int `json:"unreadCount"` +type NotificationQueryInput struct { + Page int `json:"page"` + PerPage int `json:"per_page"` } type PerformerAppearance struct { @@ -751,6 +779,12 @@ type TagUpdateInput struct { CategoryID *uuid.UUID `json:"category_id"` } +type UpdatedEdit struct { + Edit *Edit `json:"edit"` +} + +func (UpdatedEdit) IsNotificationData() {} + type UserChangePasswordInput struct { // Password in plain text ExistingPassword *string `json:"existing_password"` diff --git a/pkg/models/model_notification.go b/pkg/models/model_notification.go new file mode 100644 index 000000000..c08198920 --- /dev/null +++ b/pkg/models/model_notification.go @@ -0,0 +1,43 @@ +package models + +import ( + "database/sql" + "time" + + "github.com/gofrs/uuid" +) + +type NotificationEnum string + +const ( + NotificationEnumFavoritePerformerScene NotificationEnum = "FAVORITE_PERFORMER_SCENE" + NotificationEnumFavoritePerformerEdit NotificationEnum = "FAVORITE_PERFORMER_EDIT" + NotificationEnumFavoriteStudioScene NotificationEnum = "FAVORITE_STUDIO_SCENE" + NotificationEnumFavoriteStudioEdit NotificationEnum = "FAVORITE_STUDIO_EDIT" + NotificationEnumCommentOwnEdit NotificationEnum = "COMMENT_OWN_EDIT" + NotificationEnumDownvoteOwnEdit NotificationEnum = "DOWNVOTE_OWN_EDIT" + NotificationEnumFailedOwnEdit NotificationEnum = "FAILED_OWN_EDIT" + NotificationEnumCommentCommentedEdit NotificationEnum = "COMMENT_COMMENTED_EDIT" + NotificationEnumCommentVotedEdit NotificationEnum = "COMMENT_VOTED_EDIT" + NotificationEnumUpdatedEdit NotificationEnum = "UPDATED_EDIT" +) + +type Notification struct { + UserID uuid.UUID `db:"user_id" json:"user_id"` + Type NotificationEnum `db:"type" json:"type"` + TargetID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + ReadAt sql.NullTime `db:"read_at" json:"read_at"` +} + +type Notifications []*Notification + +func (s Notifications) Each(fn func(interface{})) { + for _, v := range s { + fn(v) + } +} + +func (s *Notifications) Add(o interface{}) { + *s = append(*s, o.(*Notification)) +} diff --git a/pkg/models/notification.go b/pkg/models/notification.go new file mode 100644 index 000000000..b8427b4ea --- /dev/null +++ b/pkg/models/notification.go @@ -0,0 +1,17 @@ +package models + +import "github.com/gofrs/uuid" + +type NotificationRepo interface { + GetUnreadCount(userID uuid.UUID) (int, error) + GetNotifications(filter NotificationQueryInput, userID uuid.UUID) ([]*Notification, error) + + TriggerSceneCreationNotifications(sceneID uuid.UUID) error + TriggerPerformerEditNotifications(editID uuid.UUID) error + TriggerStudioEditNotifications(editID uuid.UUID) error + TriggerSceneEditNotifications(editID uuid.UUID) error + TriggerEditCommentNotifications(editID uuid.UUID) error + TriggerDownvoteEditNotifications(editID uuid.UUID) error + TriggerFailedEditNotifications(editID uuid.UUID) error + TriggerUpdatedEditNotifications(editID uuid.UUID) error +} diff --git a/pkg/models/scene.go b/pkg/models/scene.go index 343449888..82d03e311 100644 --- a/pkg/models/scene.go +++ b/pkg/models/scene.go @@ -13,7 +13,7 @@ type SceneRepo interface { UpdateFingerprints(sceneID uuid.UUID, updatedJoins SceneFingerprints) error DestroyFingerprints(sceneID uuid.UUID, toDelete SceneFingerprints) error Find(id uuid.UUID) (*Scene, error) - FindByIds(ids []uuid.UUID) ([]*Scene, error) + FindByIds(ids []uuid.UUID) ([]*Scene, []error) FindByFingerprint(algorithm FingerprintAlgorithm, hash string) ([]*Scene, error) FindByFingerprints(fingerprints []string) ([]*Scene, error) FindByFullFingerprints(fingerprints []*FingerprintQueryInput) ([]*Scene, error) diff --git a/pkg/sqlx/factory.go b/pkg/sqlx/factory.go index 62e0548f1..83de684ed 100644 --- a/pkg/sqlx/factory.go +++ b/pkg/sqlx/factory.go @@ -59,3 +59,7 @@ func (f *repo) Site() models.SiteRepo { func (f *repo) Draft() models.DraftRepo { return newDraftQueryBuilder(f.txnState) } + +func (f *repo) Notification() models.NotificationRepo { + return newNotificationQueryBuilder(f.txnState) +} diff --git a/pkg/sqlx/querybuilder_edit.go b/pkg/sqlx/querybuilder_edit.go index 7f76b22a4..4bb4166e0 100644 --- a/pkg/sqlx/querybuilder_edit.go +++ b/pkg/sqlx/querybuilder_edit.go @@ -10,6 +10,7 @@ import ( "github.com/jmoiron/sqlx" "github.com/lib/pq" "github.com/stashapp/stash-box/pkg/models" + "github.com/stashapp/stash-box/pkg/utils" ) const ( @@ -465,3 +466,44 @@ func (qb *editQueryBuilder) CancelUserEdits(userID uuid.UUID) error { err := qb.dbi.RawQuery(editDBTable, query, args, nil) return err } + +func (qb *editQueryBuilder) FindByIds(ids []uuid.UUID) ([]*models.Edit, []error) { + query := "SELECT edits.* FROM edits WHERE id IN (?)" + query, args, _ := sqlx.In(query, ids) + edits, err := qb.queryEdits(query, args) + if err != nil { + return nil, utils.DuplicateError(err, len(ids)) + } + + m := make(map[uuid.UUID]*models.Edit) + for _, edit := range edits { + m[edit.ID] = edit + } + + result := make([]*models.Edit, len(ids)) + for i, id := range ids { + result[i] = m[id] + } + return result, nil +} + +func (qb *editQueryBuilder) FindCommentsByIds(ids []uuid.UUID) ([]*models.EditComment, []error) { + query := "SELECT EC.* FROM edit_comments EC WHERE id IN (?)" + query, args, _ := sqlx.In(query, ids) + comments := models.EditComments{} + err := qb.dbi.RawQuery(editCommentTable.table, query, args, &comments) + if err != nil { + return nil, utils.DuplicateError(err, len(ids)) + } + + m := make(map[uuid.UUID]*models.EditComment) + for _, comment := range comments { + m[comment.ID] = comment + } + + result := make([]*models.EditComment, len(ids)) + for i, id := range ids { + result[i] = m[id] + } + return result, nil +} diff --git a/pkg/sqlx/querybuilder_notifications.go b/pkg/sqlx/querybuilder_notifications.go new file mode 100644 index 000000000..772685ba3 --- /dev/null +++ b/pkg/sqlx/querybuilder_notifications.go @@ -0,0 +1,195 @@ +package sqlx + +import ( + "github.com/gofrs/uuid" + "github.com/stashapp/stash-box/pkg/models" +) + +const ( + notificationTable = "notifications" +) + +var ( + notificationDBTable = newTable(notificationTable, func() interface{} { + return &models.Notification{} + }) +) + +type notificationsQueryBuilder struct { + dbi *dbi +} + +func newNotificationQueryBuilder(txn *txnState) models.NotificationRepo { + return ¬ificationsQueryBuilder{ + dbi: newDBI(txn), + } +} + +func (qb *notificationsQueryBuilder) toModel(ro interface{}) *models.Notification { + if ro != nil { + return ro.(*models.Notification) + } + + return nil +} + +func (qb *notificationsQueryBuilder) TriggerSceneCreationNotifications(sceneID uuid.UUID) error { + var args []interface{} + args = append(args, sceneID) + query := ` +INSERT INTO notifications + SELECT N.user_id, N.type, $1 + FROM scenes S JOIN studio_favorites SF ON S.studio_id = SF.studio_id + JOIN user_notifications N ON SF.user_id = N.user_id AND N.type = 'FAVORITE_STUDIO_SCENE' + WHERE S.id = $1 + UNION + SELECT N.user_id, N.type, $1 + FROM scene_performers SP JOIN performer_favorites PF ON SP.performer_id = PF.performer_id + JOIN user_notifications N ON PF.user_id = N.user_id AND N.type = 'FAVORITE_PERFORMER_SCENE' + WHERE SP.scene_id = $1 + ` + err := qb.dbi.RawExec(query, args) + return err +} + +func (qb *notificationsQueryBuilder) TriggerPerformerEditNotifications(editID uuid.UUID) error { + var args []interface{} + args = append(args, editID) + query := ` +INSERT INTO notifications + SELECT N.user_id, N.type, $1 + FROM performer_edits PE JOIN performer_favorites PF ON PE.performer_id = PF.performer_id + JOIN user_notifications N ON PF.user_id = N.user_id AND N.type = 'FAVORITE_PERFORMER_EDIT' + WHERE PE.edit_id = $1 + ` + err := qb.dbi.RawExec(query, args) + return err +} + +func (qb *notificationsQueryBuilder) TriggerStudioEditNotifications(editID uuid.UUID) error { + var args []interface{} + args = append(args, editID) + query := ` +INSERT INTO notifications + SELECT N.user_id, N.type, $1 + FROM studio_edits SE + JOIN studio_favorites SF ON SE.studio_id = SF.studio_id + JOIN user_notifications N ON SF.user_id = N.user_id AND N.type = 'FAVORITE_STUDIO_EDIT' + WHERE SE.edit_id = $1 + ` + err := qb.dbi.RawExec(query, args) + return err +} + +func (qb *notificationsQueryBuilder) TriggerSceneEditNotifications(editID uuid.UUID) error { + var args []interface{} + args = append(args, editID) + query := ` +INSERT INTO notifications + SELECT N.user_id, N.type, $1 + FROM edits E JOIN studio_favorites SF ON (E.data->'new_data'->>'studio_id')::uuid = SF.studio_id + JOIN user_notifications N ON SF.user_id = N.user_id AND N.type = 'FAVORITE_STUDIO_EDIT' + WHERE E.id = $1 + UNION + SELECT N.user_id, N.type, $1 + FROM ( + SELECT id, (jsonb_array_elements(edits.data->'new_data'->'added_performers')->>'performer_id')::uuid AS performer_id FROM edits + ) E JOIN performer_favorites PF ON E.performer_id = PF.performer_id + JOIN user_notifications N ON PF.user_id = N.user_id AND N.type = 'FAVORITE_PERFORMER_EDIT' + WHERE E.id = $1 + ` + err := qb.dbi.RawExec(query, args) + return err +} + +func (qb *notificationsQueryBuilder) TriggerEditCommentNotifications(commentID uuid.UUID) error { + var args []interface{} + args = append(args, commentID) + query := ` +INSERT INTO notifications + SELECT N.user_id, N.type, $1 + FROM edit_comments EC + JOIN edits E ON EC.edit_id = E.id + JOIN user_notifications N ON E.user_id = N.user_id AND N.type = 'COMMENT_OWN_EDIT' + WHERE EC.id = $1 + UNION + SELECT N.user_id, N.type, $1 + FROM edit_comments EC + JOIN edits E ON EC.edit_id = E.id JOIN edit_comments EO ON EO.edit_id = E.id + JOIN user_notifications N ON EO.user_id = N.user_id AND N.type = 'COMMENT_COMMENTED_EDIT' + WHERE EO.user_id != E.user_id AND EO.user_id != EC.user_id AND EC.id = $1 + UNION + SELECT N.user_id, N.type, $1 + FROM edit_comments EC + JOIN edits E ON EC.edit_id = E.id JOIN edit_votes EV ON EV.edit_id = E.id + JOIN user_notifications N ON EV.user_id = N.user_id AND N.type = 'COMMENT_VOTED_EDIT' + WHERE EV.vote != 'ABSTAIN' AND EV.user_id != E.user_id AND EV.user_id != EC.user_id AND EC.id = $1 + ` + err := qb.dbi.RawExec(query, args) + return err +} + +func (qb *notificationsQueryBuilder) TriggerDownvoteEditNotifications(editID uuid.UUID) error { + var args []interface{} + args = append(args, editID) + query := ` +INSERT INTO notifications + SELECT N.user_id, N.type, $1 + FROM edits E + JOIN user_notifications N ON E.user_id = N.user_id AND N.type = 'DOWNVOTE_OWN_EDIT' + WHERE E.id = $1 + ` + err := qb.dbi.RawExec(query, args) + return err +} + +func (qb *notificationsQueryBuilder) TriggerFailedEditNotifications(editID uuid.UUID) error { + var args []interface{} + args = append(args, editID) + query := ` +INSERT INTO notifications + SELECT N.user_id, N.type, $1 + FROM edits E + JOIN user_notifications N ON E.user_id = N.user_id AND N.type = 'FAILED_OWN_EDIT' + WHERE E.id = $1 + ` + err := qb.dbi.RawExec(query, args) + return err +} + +func (qb *notificationsQueryBuilder) TriggerUpdatedEditNotifications(editID uuid.UUID) error { + var args []interface{} + args = append(args, editID) + query := ` +INSERT INTO notifications + SELECT N.user_id, N.type, $1 + FROM edits E + JOIN edit_votes EV ON E.id = EV.edit_id + JOIN user_notifications N ON EV.user_id = N.user_id AND N.type = 'UPDATED_EDIT' + WHERE E.id = $1 + ` + err := qb.dbi.RawExec(query, args) + return err +} + +func (qb *notificationsQueryBuilder) GetUnreadCount(userID uuid.UUID) (int, error) { + var args []interface{} + args = append(args, userID) + return runCountQuery(qb.dbi.db(), buildCountQuery("SELECT * FROM notifications WHERE user_id = ? AND read_at IS NULL"), args) +} + +func (qb *notificationsQueryBuilder) GetNotifications(filter models.NotificationQueryInput, userID uuid.UUID) ([]*models.Notification, error) { + query := newQueryBuilder(notificationDBTable) + query.Eq("user_id", userID) + query.Pagination = getPagination(filter.Page, filter.PerPage) + query.Sort = getSort("created_at", models.SortDirectionEnumDesc.String(), notificationDBTable.name, nil) + + var notifications models.Notifications + _, err := qb.dbi.Query(*query, ¬ifications) + + if err != nil { + return nil, err + } + + return notifications, nil +} diff --git a/pkg/sqlx/querybuilder_scene.go b/pkg/sqlx/querybuilder_scene.go index dff89954c..c99890fd2 100644 --- a/pkg/sqlx/querybuilder_scene.go +++ b/pkg/sqlx/querybuilder_scene.go @@ -195,7 +195,7 @@ func (qb *sceneQueryBuilder) FindByFullFingerprints(fingerprints []*models.Finge return qb.queryScenes(query, args) } -func (qb *sceneQueryBuilder) FindByIds(ids []uuid.UUID) ([]*models.Scene, error) { +func (qb *sceneQueryBuilder) FindByIds(ids []uuid.UUID) ([]*models.Scene, []error) { query := ` SELECT scenes.* FROM scenes WHERE id IN (?) @@ -203,7 +203,7 @@ func (qb *sceneQueryBuilder) FindByIds(ids []uuid.UUID) ([]*models.Scene, error) query, args, _ := sqlx.In(query, ids) scenes, err := qb.queryScenes(query, args) if err != nil { - return nil, err + return nil, utils.DuplicateError(err, len(ids)) } m := make(map[uuid.UUID]*models.Scene) From d95ed1e9c823c6c52970ffaebd93ea5b74f2ba20 Mon Sep 17 00:00:00 2001 From: InfiniteStash <117855276+InfiniteStash@users.noreply.github.com> Date: Sun, 7 Jan 2024 11:22:04 +0000 Subject: [PATCH 5/8] WIP --- frontend/src/App.scss | 9 + frontend/src/Main.tsx | 17 +- frontend/src/constants/route.ts | 1 + .../graphql/queries/QueryNotifications.gql | 66 + .../queries/UnreadNotificationCount.gql | 3 + frontend/src/graphql/queries/index.ts | 11 + frontend/src/graphql/types.ts | 7448 ++++++++++++++++- frontend/src/pages/index.tsx | 3 + .../src/pages/notifications/Notifications.tsx | 33 + frontend/src/pages/notifications/index.ts | 1 + graphql/schema/schema.graphql | 2 +- graphql/schema/types/notifications.graphql | 7 +- pkg/api/resolver.go | 3 + pkg/api/resolver_query_find_notifications.go | 29 + pkg/api/resolver_query_notifications.go | 11 +- pkg/models/generated_exec.go | 334 +- pkg/models/generated_models.go | 10 +- pkg/models/model_notification.go | 4 + pkg/models/notification.go | 5 +- pkg/sqlx/querybuilder_notifications.go | 10 +- 20 files changed, 7900 insertions(+), 107 deletions(-) create mode 100644 frontend/src/graphql/queries/QueryNotifications.gql create mode 100644 frontend/src/graphql/queries/UnreadNotificationCount.gql create mode 100644 frontend/src/pages/notifications/Notifications.tsx create mode 100644 frontend/src/pages/notifications/index.ts create mode 100644 pkg/api/resolver_query_find_notifications.go diff --git a/frontend/src/App.scss b/frontend/src/App.scss index b9b02b9a4..554095c6d 100644 --- a/frontend/src/App.scss +++ b/frontend/src/App.scss @@ -94,3 +94,12 @@ div.react-select__menu { margin: auto; max-width: 1200px; } + +.NotificationBadge { + color: white; + + &:active, + &:hover { + color: var(--bs-gray-500); + } +} diff --git a/frontend/src/Main.tsx b/frontend/src/Main.tsx index 6b49ab922..6cb18c0f0 100644 --- a/frontend/src/Main.tsx +++ b/frontend/src/Main.tsx @@ -1,11 +1,15 @@ import { FC, useEffect } from "react"; -import { Navbar, Nav } from "react-bootstrap"; -import { NavLink, useLocation, useNavigate } from "react-router-dom"; +import { Navbar, Nav, Button, Badge } from "react-bootstrap"; +import { NavLink, useLocation, useNavigate, Link } from "react-router-dom"; +import { faBell } from "@fortawesome/free-solid-svg-icons"; +import { faBell as faBellOutlined} from "@fortawesome/free-regular-svg-icons"; import SearchField, { SearchType } from "src/components/searchField"; import { getPlatformURL, getCredentialsSetting } from "src/utils/createClient"; import { isAdmin, canEdit, userHref, setCachedUser } from "src/utils"; import { useAuth } from "src/hooks"; +import { Icon } from "src/components/fragments"; +import { useUnreadNotificationsCount } from "src/graphql"; import { ROUTE_SCENES, ROUTE_PERFORMERS, @@ -22,6 +26,7 @@ import { ROUTE_FORGOT_PASSWORD, ROUTE_SITES, ROUTE_DRAFTS, + ROUTE_NOTIFICATIONS, } from "src/constants/route"; import AuthContext from "./AuthContext"; @@ -33,6 +38,8 @@ const Main: FC = ({ children }) => { const location = useLocation(); const navigate = useNavigate(); const { loading, user } = useAuth(); + const { data: unreadNotifications } = useUnreadNotificationsCount(); + const notificationCount = unreadNotifications?.getUnreadNotificationCount || null; useEffect(() => { if (loading || user) return; @@ -73,6 +80,12 @@ const Main: FC = ({ children }) => { contextValue.authenticated && contextValue.user && ( <> + + + Logged in as @@ -280,3 +283,11 @@ export const useStudioPerformers = ( useQuery(StudioPerformersDocument, { variables, }); + +export const useNotifications = (variables: NotificationsQueryVariables) => + useQuery(NotificationsDocument, { + variables, + }); + +export const useUnreadNotificationsCount = () => + useQuery(UnreadNotificationCountDocument); diff --git a/frontend/src/graphql/types.ts b/frontend/src/graphql/types.ts index 322e82529..ba6b899f5 100644 --- a/frontend/src/graphql/types.ts +++ b/frontend/src/graphql/types.ts @@ -771,11 +771,6 @@ export type NotificationData = | FavoriteStudioScene | UpdatedEdit; -export type NotificationQueryInput = { - page?: Scalars["Int"]; - per_page?: Scalars["Int"]; -}; - export enum OperationEnum { CREATE = "CREATE", DESTROY = "DESTROY", @@ -1125,7 +1120,7 @@ export type Query = { me?: Maybe; queryEdits: QueryEditsResultType; queryExistingScene: QueryExistingSceneResult; - queryNotifications: Array; + queryNotifications: QueryNotificationsResult; queryPerformers: QueryPerformersResultType; queryScenes: QueryScenesResultType; querySites: QuerySitesResultType; @@ -1219,7 +1214,7 @@ export type QueryQueryExistingSceneArgs = { /** The query root for this schema */ export type QueryQueryNotificationsArgs = { - input: NotificationQueryInput; + input: QueryNotificationsInput; }; /** The query root for this schema */ @@ -1283,6 +1278,17 @@ export type QueryExistingSceneResult = { scenes: Array; }; +export type QueryNotificationsInput = { + page?: Scalars["Int"]; + per_page?: Scalars["Int"]; +}; + +export type QueryNotificationsResult = { + __typename: "QueryNotificationsResult"; + count: Scalars["Int"]; + notifications: Array; +}; + export type QueryPerformersResultType = { __typename: "QueryPerformersResultType"; count: Scalars["Int"]; @@ -18030,6 +18036,5471 @@ export type QueryExistingSceneQuery = { }; }; +export type NotificationsQueryVariables = Exact<{ + input: QueryNotificationsInput; +}>; + +export type NotificationsQuery = { + __typename: "Query"; + queryNotifications: { + __typename: "QueryNotificationsResult"; + count: number; + notifications: Array<{ + __typename: "Notification"; + created: string; + read: boolean; + data: + | { + __typename: "CommentCommentedEdit"; + comment: { + __typename: "EditComment"; + id: string; + date: string; + comment: string; + user?: { __typename: "User"; id: string; name: string } | null; + }; + } + | { + __typename: "CommentOwnEdit"; + comment: { + __typename: "EditComment"; + id: string; + date: string; + comment: string; + user?: { __typename: "User"; id: string; name: string } | null; + }; + } + | { + __typename: "CommentVotedEdit"; + comment: { + __typename: "EditComment"; + id: string; + date: string; + comment: string; + user?: { __typename: "User"; id: string; name: string } | null; + }; + } + | { + __typename: "DownvoteOwnEdit"; + edit: { + __typename: "Edit"; + id: string; + target_type: TargetTypeEnum; + operation: OperationEnum; + status: VoteStatusEnum; + bot: boolean; + applied: boolean; + created: string; + updated?: string | null; + closed?: string | null; + expires?: string | null; + vote_count: number; + destructive: boolean; + comments: Array<{ + __typename: "EditComment"; + id: string; + date: string; + comment: string; + user?: { __typename: "User"; id: string; name: string } | null; + }>; + votes: Array<{ + __typename: "EditVote"; + date: string; + vote: VoteTypeEnum; + user?: { __typename: "User"; id: string; name: string } | null; + }>; + user?: { __typename: "User"; id: string; name: string } | null; + target?: + | { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + } + | { + __typename: "Scene"; + id: string; + release_date?: string | null; + title?: string | null; + deleted: boolean; + details?: string | null; + director?: string | null; + code?: string | null; + duration?: number | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + studio?: { + __typename: "Studio"; + id: string; + name: string; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + } | null; + performers: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + gender?: GenderEnum | null; + aliases: Array; + }; + }>; + fingerprints: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + submissions: number; + user_submitted: boolean; + created: string; + updated: string; + }>; + tags: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + aliases: Array; + }>; + } + | { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } + | { + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + details?: + | { + __typename: "PerformerEdit"; + name?: string | null; + disambiguation?: string | null; + added_aliases?: Array | null; + removed_aliases?: Array | null; + gender?: GenderEnum | null; + birthdate?: string | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + eye_color?: EyeColorEnum | null; + hair_color?: HairColorEnum | null; + height?: number | null; + cup_size?: string | null; + band_size?: number | null; + waist_size?: number | null; + hip_size?: number | null; + breast_type?: BreastTypeEnum | null; + career_start_year?: number | null; + career_end_year?: number | null; + draft_id?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + added_tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + removed_tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + added_piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + removed_piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + } + | { + __typename: "SceneEdit"; + title?: string | null; + details?: string | null; + date?: string | null; + duration?: number | null; + director?: string | null; + code?: string | null; + draft_id?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + studio?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + removed_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + added_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + removed_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + added_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + removed_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + } + | { + __typename: "StudioEdit"; + name?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + parent?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + } + | { + __typename: "TagEdit"; + name?: string | null; + description?: string | null; + added_aliases?: Array | null; + removed_aliases?: Array | null; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + old_details?: + | { + __typename: "PerformerEdit"; + name?: string | null; + disambiguation?: string | null; + gender?: GenderEnum | null; + birthdate?: string | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + eye_color?: EyeColorEnum | null; + hair_color?: HairColorEnum | null; + height?: number | null; + cup_size?: string | null; + band_size?: number | null; + waist_size?: number | null; + hip_size?: number | null; + breast_type?: BreastTypeEnum | null; + career_start_year?: number | null; + career_end_year?: number | null; + } + | { + __typename: "SceneEdit"; + title?: string | null; + details?: string | null; + date?: string | null; + duration?: number | null; + director?: string | null; + code?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + studio?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + removed_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + added_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + removed_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + added_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + removed_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + } + | { + __typename: "StudioEdit"; + name?: string | null; + parent?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + } + | { + __typename: "TagEdit"; + name?: string | null; + description?: string | null; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + merge_sources: Array< + | { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + } + | { + __typename: "Scene"; + id: string; + release_date?: string | null; + title?: string | null; + deleted: boolean; + details?: string | null; + director?: string | null; + code?: string | null; + duration?: number | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + studio?: { + __typename: "Studio"; + id: string; + name: string; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + } | null; + performers: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + gender?: GenderEnum | null; + aliases: Array; + }; + }>; + fingerprints: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + submissions: number; + user_submitted: boolean; + created: string; + updated: string; + }>; + tags: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + aliases: Array; + }>; + } + | { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } + | { + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + >; + options?: { + __typename: "PerformerEditOptions"; + set_modify_aliases: boolean; + set_merge_aliases: boolean; + } | null; + }; + } + | { + __typename: "FailedOwnEdit"; + edit: { + __typename: "Edit"; + id: string; + target_type: TargetTypeEnum; + operation: OperationEnum; + status: VoteStatusEnum; + bot: boolean; + applied: boolean; + created: string; + updated?: string | null; + closed?: string | null; + expires?: string | null; + vote_count: number; + destructive: boolean; + comments: Array<{ + __typename: "EditComment"; + id: string; + date: string; + comment: string; + user?: { __typename: "User"; id: string; name: string } | null; + }>; + votes: Array<{ + __typename: "EditVote"; + date: string; + vote: VoteTypeEnum; + user?: { __typename: "User"; id: string; name: string } | null; + }>; + user?: { __typename: "User"; id: string; name: string } | null; + target?: + | { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + } + | { + __typename: "Scene"; + id: string; + release_date?: string | null; + title?: string | null; + deleted: boolean; + details?: string | null; + director?: string | null; + code?: string | null; + duration?: number | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + studio?: { + __typename: "Studio"; + id: string; + name: string; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + } | null; + performers: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + gender?: GenderEnum | null; + aliases: Array; + }; + }>; + fingerprints: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + submissions: number; + user_submitted: boolean; + created: string; + updated: string; + }>; + tags: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + aliases: Array; + }>; + } + | { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } + | { + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + details?: + | { + __typename: "PerformerEdit"; + name?: string | null; + disambiguation?: string | null; + added_aliases?: Array | null; + removed_aliases?: Array | null; + gender?: GenderEnum | null; + birthdate?: string | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + eye_color?: EyeColorEnum | null; + hair_color?: HairColorEnum | null; + height?: number | null; + cup_size?: string | null; + band_size?: number | null; + waist_size?: number | null; + hip_size?: number | null; + breast_type?: BreastTypeEnum | null; + career_start_year?: number | null; + career_end_year?: number | null; + draft_id?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + added_tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + removed_tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + added_piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + removed_piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + } + | { + __typename: "SceneEdit"; + title?: string | null; + details?: string | null; + date?: string | null; + duration?: number | null; + director?: string | null; + code?: string | null; + draft_id?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + studio?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + removed_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + added_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + removed_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + added_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + removed_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + } + | { + __typename: "StudioEdit"; + name?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + parent?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + } + | { + __typename: "TagEdit"; + name?: string | null; + description?: string | null; + added_aliases?: Array | null; + removed_aliases?: Array | null; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + old_details?: + | { + __typename: "PerformerEdit"; + name?: string | null; + disambiguation?: string | null; + gender?: GenderEnum | null; + birthdate?: string | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + eye_color?: EyeColorEnum | null; + hair_color?: HairColorEnum | null; + height?: number | null; + cup_size?: string | null; + band_size?: number | null; + waist_size?: number | null; + hip_size?: number | null; + breast_type?: BreastTypeEnum | null; + career_start_year?: number | null; + career_end_year?: number | null; + } + | { + __typename: "SceneEdit"; + title?: string | null; + details?: string | null; + date?: string | null; + duration?: number | null; + director?: string | null; + code?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + studio?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + removed_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + added_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + removed_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + added_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + removed_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + } + | { + __typename: "StudioEdit"; + name?: string | null; + parent?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + } + | { + __typename: "TagEdit"; + name?: string | null; + description?: string | null; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + merge_sources: Array< + | { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + } + | { + __typename: "Scene"; + id: string; + release_date?: string | null; + title?: string | null; + deleted: boolean; + details?: string | null; + director?: string | null; + code?: string | null; + duration?: number | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + studio?: { + __typename: "Studio"; + id: string; + name: string; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + } | null; + performers: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + gender?: GenderEnum | null; + aliases: Array; + }; + }>; + fingerprints: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + submissions: number; + user_submitted: boolean; + created: string; + updated: string; + }>; + tags: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + aliases: Array; + }>; + } + | { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } + | { + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + >; + options?: { + __typename: "PerformerEditOptions"; + set_modify_aliases: boolean; + set_merge_aliases: boolean; + } | null; + }; + } + | { + __typename: "FavoritePerformerEdit"; + edit: { + __typename: "Edit"; + id: string; + target_type: TargetTypeEnum; + operation: OperationEnum; + status: VoteStatusEnum; + bot: boolean; + applied: boolean; + created: string; + updated?: string | null; + closed?: string | null; + expires?: string | null; + vote_count: number; + destructive: boolean; + comments: Array<{ + __typename: "EditComment"; + id: string; + date: string; + comment: string; + user?: { __typename: "User"; id: string; name: string } | null; + }>; + votes: Array<{ + __typename: "EditVote"; + date: string; + vote: VoteTypeEnum; + user?: { __typename: "User"; id: string; name: string } | null; + }>; + user?: { __typename: "User"; id: string; name: string } | null; + target?: + | { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + } + | { + __typename: "Scene"; + id: string; + release_date?: string | null; + title?: string | null; + deleted: boolean; + details?: string | null; + director?: string | null; + code?: string | null; + duration?: number | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + studio?: { + __typename: "Studio"; + id: string; + name: string; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + } | null; + performers: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + gender?: GenderEnum | null; + aliases: Array; + }; + }>; + fingerprints: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + submissions: number; + user_submitted: boolean; + created: string; + updated: string; + }>; + tags: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + aliases: Array; + }>; + } + | { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } + | { + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + details?: + | { + __typename: "PerformerEdit"; + name?: string | null; + disambiguation?: string | null; + added_aliases?: Array | null; + removed_aliases?: Array | null; + gender?: GenderEnum | null; + birthdate?: string | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + eye_color?: EyeColorEnum | null; + hair_color?: HairColorEnum | null; + height?: number | null; + cup_size?: string | null; + band_size?: number | null; + waist_size?: number | null; + hip_size?: number | null; + breast_type?: BreastTypeEnum | null; + career_start_year?: number | null; + career_end_year?: number | null; + draft_id?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + added_tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + removed_tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + added_piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + removed_piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + } + | { + __typename: "SceneEdit"; + title?: string | null; + details?: string | null; + date?: string | null; + duration?: number | null; + director?: string | null; + code?: string | null; + draft_id?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + studio?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + removed_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + added_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + removed_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + added_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + removed_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + } + | { + __typename: "StudioEdit"; + name?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + parent?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + } + | { + __typename: "TagEdit"; + name?: string | null; + description?: string | null; + added_aliases?: Array | null; + removed_aliases?: Array | null; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + old_details?: + | { + __typename: "PerformerEdit"; + name?: string | null; + disambiguation?: string | null; + gender?: GenderEnum | null; + birthdate?: string | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + eye_color?: EyeColorEnum | null; + hair_color?: HairColorEnum | null; + height?: number | null; + cup_size?: string | null; + band_size?: number | null; + waist_size?: number | null; + hip_size?: number | null; + breast_type?: BreastTypeEnum | null; + career_start_year?: number | null; + career_end_year?: number | null; + } + | { + __typename: "SceneEdit"; + title?: string | null; + details?: string | null; + date?: string | null; + duration?: number | null; + director?: string | null; + code?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + studio?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + removed_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + added_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + removed_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + added_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + removed_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + } + | { + __typename: "StudioEdit"; + name?: string | null; + parent?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + } + | { + __typename: "TagEdit"; + name?: string | null; + description?: string | null; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + merge_sources: Array< + | { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + } + | { + __typename: "Scene"; + id: string; + release_date?: string | null; + title?: string | null; + deleted: boolean; + details?: string | null; + director?: string | null; + code?: string | null; + duration?: number | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + studio?: { + __typename: "Studio"; + id: string; + name: string; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + } | null; + performers: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + gender?: GenderEnum | null; + aliases: Array; + }; + }>; + fingerprints: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + submissions: number; + user_submitted: boolean; + created: string; + updated: string; + }>; + tags: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + aliases: Array; + }>; + } + | { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } + | { + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + >; + options?: { + __typename: "PerformerEditOptions"; + set_modify_aliases: boolean; + set_merge_aliases: boolean; + } | null; + }; + } + | { + __typename: "FavoritePerformerScene"; + scene: { + __typename: "Scene"; + id: string; + release_date?: string | null; + title?: string | null; + deleted: boolean; + details?: string | null; + director?: string | null; + code?: string | null; + duration?: number | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + studio?: { + __typename: "Studio"; + id: string; + name: string; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + } | null; + performers: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + gender?: GenderEnum | null; + aliases: Array; + }; + }>; + fingerprints: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + submissions: number; + user_submitted: boolean; + created: string; + updated: string; + }>; + tags: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + aliases: Array; + }>; + }; + } + | { + __typename: "FavoriteStudioEdit"; + edit: { + __typename: "Edit"; + id: string; + target_type: TargetTypeEnum; + operation: OperationEnum; + status: VoteStatusEnum; + bot: boolean; + applied: boolean; + created: string; + updated?: string | null; + closed?: string | null; + expires?: string | null; + vote_count: number; + destructive: boolean; + comments: Array<{ + __typename: "EditComment"; + id: string; + date: string; + comment: string; + user?: { __typename: "User"; id: string; name: string } | null; + }>; + votes: Array<{ + __typename: "EditVote"; + date: string; + vote: VoteTypeEnum; + user?: { __typename: "User"; id: string; name: string } | null; + }>; + user?: { __typename: "User"; id: string; name: string } | null; + target?: + | { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + } + | { + __typename: "Scene"; + id: string; + release_date?: string | null; + title?: string | null; + deleted: boolean; + details?: string | null; + director?: string | null; + code?: string | null; + duration?: number | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + studio?: { + __typename: "Studio"; + id: string; + name: string; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + } | null; + performers: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + gender?: GenderEnum | null; + aliases: Array; + }; + }>; + fingerprints: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + submissions: number; + user_submitted: boolean; + created: string; + updated: string; + }>; + tags: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + aliases: Array; + }>; + } + | { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } + | { + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + details?: + | { + __typename: "PerformerEdit"; + name?: string | null; + disambiguation?: string | null; + added_aliases?: Array | null; + removed_aliases?: Array | null; + gender?: GenderEnum | null; + birthdate?: string | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + eye_color?: EyeColorEnum | null; + hair_color?: HairColorEnum | null; + height?: number | null; + cup_size?: string | null; + band_size?: number | null; + waist_size?: number | null; + hip_size?: number | null; + breast_type?: BreastTypeEnum | null; + career_start_year?: number | null; + career_end_year?: number | null; + draft_id?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + added_tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + removed_tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + added_piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + removed_piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + } + | { + __typename: "SceneEdit"; + title?: string | null; + details?: string | null; + date?: string | null; + duration?: number | null; + director?: string | null; + code?: string | null; + draft_id?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + studio?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + removed_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + added_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + removed_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + added_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + removed_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + } + | { + __typename: "StudioEdit"; + name?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + parent?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + } + | { + __typename: "TagEdit"; + name?: string | null; + description?: string | null; + added_aliases?: Array | null; + removed_aliases?: Array | null; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + old_details?: + | { + __typename: "PerformerEdit"; + name?: string | null; + disambiguation?: string | null; + gender?: GenderEnum | null; + birthdate?: string | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + eye_color?: EyeColorEnum | null; + hair_color?: HairColorEnum | null; + height?: number | null; + cup_size?: string | null; + band_size?: number | null; + waist_size?: number | null; + hip_size?: number | null; + breast_type?: BreastTypeEnum | null; + career_start_year?: number | null; + career_end_year?: number | null; + } + | { + __typename: "SceneEdit"; + title?: string | null; + details?: string | null; + date?: string | null; + duration?: number | null; + director?: string | null; + code?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + studio?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + removed_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + added_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + removed_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + added_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + removed_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + } + | { + __typename: "StudioEdit"; + name?: string | null; + parent?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + } + | { + __typename: "TagEdit"; + name?: string | null; + description?: string | null; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + merge_sources: Array< + | { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + } + | { + __typename: "Scene"; + id: string; + release_date?: string | null; + title?: string | null; + deleted: boolean; + details?: string | null; + director?: string | null; + code?: string | null; + duration?: number | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + studio?: { + __typename: "Studio"; + id: string; + name: string; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + } | null; + performers: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + gender?: GenderEnum | null; + aliases: Array; + }; + }>; + fingerprints: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + submissions: number; + user_submitted: boolean; + created: string; + updated: string; + }>; + tags: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + aliases: Array; + }>; + } + | { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } + | { + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + >; + options?: { + __typename: "PerformerEditOptions"; + set_modify_aliases: boolean; + set_merge_aliases: boolean; + } | null; + }; + } + | { + __typename: "FavoriteStudioScene"; + scene: { + __typename: "Scene"; + id: string; + release_date?: string | null; + title?: string | null; + deleted: boolean; + details?: string | null; + director?: string | null; + code?: string | null; + duration?: number | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + studio?: { + __typename: "Studio"; + id: string; + name: string; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + } | null; + performers: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + gender?: GenderEnum | null; + aliases: Array; + }; + }>; + fingerprints: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + submissions: number; + user_submitted: boolean; + created: string; + updated: string; + }>; + tags: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + aliases: Array; + }>; + }; + } + | { + __typename: "UpdatedEdit"; + edit: { + __typename: "Edit"; + id: string; + target_type: TargetTypeEnum; + operation: OperationEnum; + status: VoteStatusEnum; + bot: boolean; + applied: boolean; + created: string; + updated?: string | null; + closed?: string | null; + expires?: string | null; + vote_count: number; + destructive: boolean; + comments: Array<{ + __typename: "EditComment"; + id: string; + date: string; + comment: string; + user?: { __typename: "User"; id: string; name: string } | null; + }>; + votes: Array<{ + __typename: "EditVote"; + date: string; + vote: VoteTypeEnum; + user?: { __typename: "User"; id: string; name: string } | null; + }>; + user?: { __typename: "User"; id: string; name: string } | null; + target?: + | { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + } + | { + __typename: "Scene"; + id: string; + release_date?: string | null; + title?: string | null; + deleted: boolean; + details?: string | null; + director?: string | null; + code?: string | null; + duration?: number | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + studio?: { + __typename: "Studio"; + id: string; + name: string; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + } | null; + performers: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + gender?: GenderEnum | null; + aliases: Array; + }; + }>; + fingerprints: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + submissions: number; + user_submitted: boolean; + created: string; + updated: string; + }>; + tags: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + aliases: Array; + }>; + } + | { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } + | { + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + details?: + | { + __typename: "PerformerEdit"; + name?: string | null; + disambiguation?: string | null; + added_aliases?: Array | null; + removed_aliases?: Array | null; + gender?: GenderEnum | null; + birthdate?: string | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + eye_color?: EyeColorEnum | null; + hair_color?: HairColorEnum | null; + height?: number | null; + cup_size?: string | null; + band_size?: number | null; + waist_size?: number | null; + hip_size?: number | null; + breast_type?: BreastTypeEnum | null; + career_start_year?: number | null; + career_end_year?: number | null; + draft_id?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + added_tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + removed_tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + added_piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + removed_piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + } + | { + __typename: "SceneEdit"; + title?: string | null; + details?: string | null; + date?: string | null; + duration?: number | null; + director?: string | null; + code?: string | null; + draft_id?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + studio?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + removed_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + added_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + removed_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + added_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + removed_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + } + | { + __typename: "StudioEdit"; + name?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + parent?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + } + | { + __typename: "TagEdit"; + name?: string | null; + description?: string | null; + added_aliases?: Array | null; + removed_aliases?: Array | null; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + old_details?: + | { + __typename: "PerformerEdit"; + name?: string | null; + disambiguation?: string | null; + gender?: GenderEnum | null; + birthdate?: string | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + eye_color?: EyeColorEnum | null; + hair_color?: HairColorEnum | null; + height?: number | null; + cup_size?: string | null; + band_size?: number | null; + waist_size?: number | null; + hip_size?: number | null; + breast_type?: BreastTypeEnum | null; + career_start_year?: number | null; + career_end_year?: number | null; + } + | { + __typename: "SceneEdit"; + title?: string | null; + details?: string | null; + date?: string | null; + duration?: number | null; + director?: string | null; + code?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + studio?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + removed_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + added_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + removed_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + added_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + removed_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + } + | { + __typename: "StudioEdit"; + name?: string | null; + parent?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + } + | { + __typename: "TagEdit"; + name?: string | null; + description?: string | null; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + merge_sources: Array< + | { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + } + | { + __typename: "Scene"; + id: string; + release_date?: string | null; + title?: string | null; + deleted: boolean; + details?: string | null; + director?: string | null; + code?: string | null; + duration?: number | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + studio?: { + __typename: "Studio"; + id: string; + name: string; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + } | null; + performers: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + gender?: GenderEnum | null; + aliases: Array; + }; + }>; + fingerprints: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + submissions: number; + user_submitted: boolean; + created: string; + updated: string; + }>; + tags: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + aliases: Array; + }>; + } + | { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } + | { + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + >; + options?: { + __typename: "PerformerEditOptions"; + set_modify_aliases: boolean; + set_merge_aliases: boolean; + } | null; + }; + }; + }>; + }; +}; + export type SceneQueryVariables = Exact<{ id: Scalars["ID"]; }>; @@ -18562,6 +24033,15 @@ export type TagsQuery = { }; }; +export type UnreadNotificationCountQueryVariables = Exact<{ + [key: string]: never; +}>; + +export type UnreadNotificationCountQuery = { + __typename: "Query"; + getUnreadNotificationCount: number; +}; + export type UserQueryVariables = Exact<{ name: Scalars["String"]; }>; @@ -46639,20 +52119,26 @@ export const QueryExistingSceneDocument = { QueryExistingSceneQuery, QueryExistingSceneQueryVariables >; -export const SceneDocument = { +export const NotificationsDocument = { kind: "Document", definitions: [ { kind: "OperationDefinition", operation: "query", - name: { kind: "Name", value: "Scene" }, + name: { kind: "Name", value: "Notifications" }, variableDefinitions: [ { kind: "VariableDefinition", - variable: { kind: "Variable", name: { kind: "Name", value: "id" } }, + variable: { + kind: "Variable", + name: { kind: "Name", value: "input" }, + }, type: { kind: "NonNullType", - type: { kind: "NamedType", name: { kind: "Name", value: "ID" } }, + type: { + kind: "NamedType", + name: { kind: "Name", value: "QueryNotificationsInput" }, + }, }, }, ], @@ -46661,23 +52147,1931 @@ export const SceneDocument = { selections: [ { kind: "Field", - name: { kind: "Name", value: "findScene" }, + name: { kind: "Name", value: "queryNotifications" }, arguments: [ { kind: "Argument", - name: { kind: "Name", value: "id" }, + name: { kind: "Name", value: "input" }, value: { kind: "Variable", - name: { kind: "Name", value: "id" }, + name: { kind: "Name", value: "input" }, }, }, ], selectionSet: { kind: "SelectionSet", selections: [ + { kind: "Field", name: { kind: "Name", value: "count" } }, { - kind: "FragmentSpread", - name: { kind: "Name", value: "SceneFragment" }, + kind: "Field", + name: { kind: "Name", value: "notifications" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "created" }, + }, + { kind: "Field", name: { kind: "Name", value: "read" } }, + { + kind: "Field", + name: { kind: "Name", value: "data" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "__typename" }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { + kind: "Name", + value: "FavoritePerformerScene", + }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "scene" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "SceneFragment", + }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { + kind: "Name", + value: "FavoritePerformerEdit", + }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "edit" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "EditFragment", + }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { + kind: "Name", + value: "FavoriteStudioScene", + }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "scene" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "SceneFragment", + }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { + kind: "Name", + value: "FavoriteStudioEdit", + }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "edit" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "EditFragment", + }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "CommentOwnEdit" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "comment" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "CommentFragment", + }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { + kind: "Name", + value: "CommentCommentedEdit", + }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "comment" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "CommentFragment", + }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { + kind: "Name", + value: "CommentVotedEdit", + }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "comment" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "CommentFragment", + }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { + kind: "Name", + value: "DownvoteOwnEdit", + }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "edit" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "EditFragment", + }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "FailedOwnEdit" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "edit" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "EditFragment", + }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "UpdatedEdit" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "edit" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "EditFragment", + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "URLFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "URL" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "url" } }, + { + kind: "Field", + name: { kind: "Name", value: "site" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "icon" } }, + ], + }, + }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "ImageFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Image" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "url" } }, + { kind: "Field", name: { kind: "Name", value: "width" } }, + { kind: "Field", name: { kind: "Name", value: "height" } }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "ScenePerformerFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Performer" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "disambiguation" } }, + { kind: "Field", name: { kind: "Name", value: "deleted" } }, + { kind: "Field", name: { kind: "Name", value: "gender" } }, + { kind: "Field", name: { kind: "Name", value: "aliases" } }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "CommentFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "EditComment" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { + kind: "Field", + name: { kind: "Name", value: "user" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + ], + }, + }, + { kind: "Field", name: { kind: "Name", value: "date" } }, + { kind: "Field", name: { kind: "Name", value: "comment" } }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "TagFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Tag" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "description" } }, + { kind: "Field", name: { kind: "Name", value: "deleted" } }, + { + kind: "Field", + name: { kind: "Name", value: "category" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + ], + }, + }, + { kind: "Field", name: { kind: "Name", value: "aliases" } }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "PerformerFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Performer" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "disambiguation" } }, + { kind: "Field", name: { kind: "Name", value: "deleted" } }, + { kind: "Field", name: { kind: "Name", value: "aliases" } }, + { kind: "Field", name: { kind: "Name", value: "gender" } }, + { kind: "Field", name: { kind: "Name", value: "birth_date" } }, + { kind: "Field", name: { kind: "Name", value: "age" } }, + { kind: "Field", name: { kind: "Name", value: "height" } }, + { kind: "Field", name: { kind: "Name", value: "hair_color" } }, + { kind: "Field", name: { kind: "Name", value: "eye_color" } }, + { kind: "Field", name: { kind: "Name", value: "ethnicity" } }, + { kind: "Field", name: { kind: "Name", value: "country" } }, + { kind: "Field", name: { kind: "Name", value: "career_end_year" } }, + { kind: "Field", name: { kind: "Name", value: "career_start_year" } }, + { kind: "Field", name: { kind: "Name", value: "breast_type" } }, + { kind: "Field", name: { kind: "Name", value: "waist_size" } }, + { kind: "Field", name: { kind: "Name", value: "hip_size" } }, + { kind: "Field", name: { kind: "Name", value: "band_size" } }, + { kind: "Field", name: { kind: "Name", value: "cup_size" } }, + { + kind: "Field", + name: { kind: "Name", value: "tattoos" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "location" } }, + { kind: "Field", name: { kind: "Name", value: "description" } }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "piercings" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "location" } }, + { kind: "Field", name: { kind: "Name", value: "description" } }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { kind: "Field", name: { kind: "Name", value: "is_favorite" } }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "StudioFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Studio" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { + kind: "Field", + name: { kind: "Name", value: "child_studios" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "parent" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "url" } }, + { kind: "Field", name: { kind: "Name", value: "height" } }, + { kind: "Field", name: { kind: "Name", value: "width" } }, + ], + }, + }, + { kind: "Field", name: { kind: "Name", value: "deleted" } }, + { kind: "Field", name: { kind: "Name", value: "is_favorite" } }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "SceneFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Scene" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "release_date" } }, + { kind: "Field", name: { kind: "Name", value: "title" } }, + { kind: "Field", name: { kind: "Name", value: "deleted" } }, + { kind: "Field", name: { kind: "Name", value: "details" } }, + { kind: "Field", name: { kind: "Name", value: "director" } }, + { kind: "Field", name: { kind: "Name", value: "code" } }, + { kind: "Field", name: { kind: "Name", value: "duration" } }, + { + kind: "Field", + name: { kind: "Name", value: "urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "studio" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { + kind: "Field", + name: { kind: "Name", value: "parent" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + ], + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "performers" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "as" } }, + { + kind: "Field", + name: { kind: "Name", value: "performer" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ScenePerformerFragment" }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "fingerprints" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "hash" } }, + { kind: "Field", name: { kind: "Name", value: "algorithm" } }, + { kind: "Field", name: { kind: "Name", value: "duration" } }, + { kind: "Field", name: { kind: "Name", value: "submissions" } }, + { + kind: "Field", + name: { kind: "Name", value: "user_submitted" }, + }, + { kind: "Field", name: { kind: "Name", value: "created" } }, + { kind: "Field", name: { kind: "Name", value: "updated" } }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "tags" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "description" } }, + { kind: "Field", name: { kind: "Name", value: "aliases" } }, + ], + }, + }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "FingerprintFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Fingerprint" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "hash" } }, + { kind: "Field", name: { kind: "Name", value: "algorithm" } }, + { kind: "Field", name: { kind: "Name", value: "duration" } }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "EditFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Edit" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "target_type" } }, + { kind: "Field", name: { kind: "Name", value: "operation" } }, + { kind: "Field", name: { kind: "Name", value: "status" } }, + { kind: "Field", name: { kind: "Name", value: "bot" } }, + { kind: "Field", name: { kind: "Name", value: "applied" } }, + { kind: "Field", name: { kind: "Name", value: "created" } }, + { kind: "Field", name: { kind: "Name", value: "updated" } }, + { kind: "Field", name: { kind: "Name", value: "closed" } }, + { kind: "Field", name: { kind: "Name", value: "expires" } }, + { kind: "Field", name: { kind: "Name", value: "vote_count" } }, + { kind: "Field", name: { kind: "Name", value: "destructive" } }, + { + kind: "Field", + name: { kind: "Name", value: "comments" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "CommentFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "votes" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "user" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + ], + }, + }, + { kind: "Field", name: { kind: "Name", value: "date" } }, + { kind: "Field", name: { kind: "Name", value: "vote" } }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "user" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "target" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Tag" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "TagFragment" }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Performer" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "PerformerFragment" }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Studio" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "StudioFragment" }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Scene" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "SceneFragment" }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "details" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "TagEdit" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "name" } }, + { + kind: "Field", + name: { kind: "Name", value: "description" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_aliases" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_aliases" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "category" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "id" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "name" }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "PerformerEdit" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "name" } }, + { + kind: "Field", + name: { kind: "Name", value: "disambiguation" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_aliases" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_aliases" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "gender" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "birthdate" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "ethnicity" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "country" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "eye_color" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "hair_color" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "height" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "cup_size" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "band_size" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "waist_size" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "hip_size" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "breast_type" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "career_start_year" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "career_end_year" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_tattoos" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "location" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "description" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_tattoos" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "location" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "description" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_piercings" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "location" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "description" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_piercings" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "location" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "description" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "draft_id" }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "StudioEdit" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "name" } }, + { + kind: "Field", + name: { kind: "Name", value: "added_urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "parent" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "StudioFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "SceneEdit" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "title" } }, + { + kind: "Field", + name: { kind: "Name", value: "details" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { kind: "Field", name: { kind: "Name", value: "date" } }, + { + kind: "Field", + name: { kind: "Name", value: "studio" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "StudioFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_performers" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "performer" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "PerformerFragment", + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "as" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_performers" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "performer" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "PerformerFragment", + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "as" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_tags" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "TagFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_tags" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "TagFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_fingerprints" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "FingerprintFragment", + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_fingerprints" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "FingerprintFragment", + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "duration" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "director" }, + }, + { kind: "Field", name: { kind: "Name", value: "code" } }, + { + kind: "Field", + name: { kind: "Name", value: "draft_id" }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "old_details" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "TagEdit" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "name" } }, + { + kind: "Field", + name: { kind: "Name", value: "description" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "category" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "id" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "name" }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "PerformerEdit" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "name" } }, + { + kind: "Field", + name: { kind: "Name", value: "disambiguation" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "gender" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "birthdate" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "ethnicity" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "country" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "eye_color" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "hair_color" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "height" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "cup_size" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "band_size" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "waist_size" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "hip_size" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "breast_type" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "career_start_year" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "career_end_year" }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "StudioEdit" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "name" } }, + { + kind: "Field", + name: { kind: "Name", value: "parent" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "StudioFragment" }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "SceneEdit" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "title" } }, + { + kind: "Field", + name: { kind: "Name", value: "details" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { kind: "Field", name: { kind: "Name", value: "date" } }, + { + kind: "Field", + name: { kind: "Name", value: "studio" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "StudioFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_performers" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "performer" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "PerformerFragment", + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "as" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_performers" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "performer" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "PerformerFragment", + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "as" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_tags" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "TagFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_tags" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "TagFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_fingerprints" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "FingerprintFragment", + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_fingerprints" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "FingerprintFragment", + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "duration" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "director" }, + }, + { kind: "Field", name: { kind: "Name", value: "code" } }, + ], + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "merge_sources" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Tag" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "TagFragment" }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Performer" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "PerformerFragment" }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Studio" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "StudioFragment" }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Scene" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "SceneFragment" }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "options" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "set_modify_aliases" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "set_merge_aliases" }, + }, + ], + }, + }, + ], + }, + }, + ], +} as unknown as DocumentNode; +export const SceneDocument = { + kind: "Document", + definitions: [ + { + kind: "OperationDefinition", + operation: "query", + name: { kind: "Name", value: "Scene" }, + variableDefinitions: [ + { + kind: "VariableDefinition", + variable: { kind: "Variable", name: { kind: "Name", value: "id" } }, + type: { + kind: "NonNullType", + type: { kind: "NamedType", name: { kind: "Name", value: "ID" } }, + }, + }, + ], + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "findScene" }, + arguments: [ + { + kind: "Argument", + name: { kind: "Name", value: "id" }, + value: { + kind: "Variable", + name: { kind: "Name", value: "id" }, + }, + }, + ], + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "SceneFragment" }, }, ], }, @@ -49043,6 +56437,28 @@ export const TagsDocument = { }, ], } as unknown as DocumentNode; +export const UnreadNotificationCountDocument = { + kind: "Document", + definitions: [ + { + kind: "OperationDefinition", + operation: "query", + name: { kind: "Name", value: "UnreadNotificationCount" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "getUnreadNotificationCount" }, + }, + ], + }, + }, + ], +} as unknown as DocumentNode< + UnreadNotificationCountQuery, + UnreadNotificationCountQueryVariables +>; export const UserDocument = { kind: "Document", definitions: [ diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx index e96b9374a..ab4c2b042 100644 --- a/frontend/src/pages/index.tsx +++ b/frontend/src/pages/index.tsx @@ -20,6 +20,7 @@ import { ROUTE_VERSION, ROUTE_SITES, ROUTE_DRAFTS, + ROUTE_NOTIFICATIONS, } from "src/constants/route"; import Home from "src/pages/home"; @@ -39,6 +40,7 @@ import Search from "src/pages/search"; import Version from "src/pages/version"; import Sites from "src/pages/sites"; import Drafts from "src/pages/drafts"; +import Notifications from "src/pages/notifications"; const Pages: FC = () => ( @@ -65,6 +67,7 @@ const Pages: FC = () => ( } /> } /> } /> + } /> } diff --git a/frontend/src/pages/notifications/Notifications.tsx b/frontend/src/pages/notifications/Notifications.tsx new file mode 100644 index 000000000..d8fe2f0ef --- /dev/null +++ b/frontend/src/pages/notifications/Notifications.tsx @@ -0,0 +1,33 @@ +import { FC } from "react"; +import { useNotifications } from "src/graphql"; +import { usePagination } from "src/hooks"; +import { ErrorMessage } from "src/components/fragments"; +import { List } from "src/components/list"; + +const PER_PAGE = 20; + +const Notifications: FC = () => { + const { page, setPage } = usePagination(); + const { loading, data } = useNotifications({ input: { page, per_page: PER_PAGE } }); + + if (loading) return null; + + if (!loading && !data) return ; + + return ( + + {data?.queryNotifications?.map(n => ( +
{ n.data.__typename }
+ ))} +
+ ); +}; + +export default Notifications; diff --git a/frontend/src/pages/notifications/index.ts b/frontend/src/pages/notifications/index.ts new file mode 100644 index 000000000..0c0261f9a --- /dev/null +++ b/frontend/src/pages/notifications/index.ts @@ -0,0 +1 @@ +export { default } from "./Notifications"; diff --git a/graphql/schema/schema.graphql b/graphql/schema/schema.graphql index 4112d153f..cd938937c 100644 --- a/graphql/schema/schema.graphql +++ b/graphql/schema/schema.graphql @@ -75,7 +75,7 @@ type Query { ### Instance Config ### getConfig: StashBoxConfig! - queryNotifications(input: NotificationQueryInput!): [Notification!]! @hasRole(role: READ) + queryNotifications(input: QueryNotificationsInput!): QueryNotificationsResult! @hasRole(role: READ) getUnreadNotificationCount: Int! @hasRole(role: READ) } diff --git a/graphql/schema/types/notifications.graphql b/graphql/schema/types/notifications.graphql index dae9af18d..e9d0e3384 100644 --- a/graphql/schema/types/notifications.graphql +++ b/graphql/schema/types/notifications.graphql @@ -56,7 +56,12 @@ type UpdatedEdit { edit: Edit! } -input NotificationQueryInput { +input QueryNotificationsInput { page: Int! = 1 per_page: Int! = 25 } + +type QueryNotificationsResult { + count: Int! + notifications: [Notification!]! +} diff --git a/pkg/api/resolver.go b/pkg/api/resolver.go index 1a79372ed..facbcf92c 100644 --- a/pkg/api/resolver.go +++ b/pkg/api/resolver.go @@ -92,6 +92,9 @@ func (r *Resolver) SceneDraft() models.SceneDraftResolver { func (r *Resolver) QueryExistingSceneResult() models.QueryExistingSceneResultResolver { return &queryExistingSceneResolver{r} } +func (r *Resolver) QueryNotificationsResult() models.QueryNotificationsResultResolver { + return &queryNotificationsResolver{r} +} func (r *Resolver) Notification() models.NotificationResolver { return ¬ificationResolver{r} diff --git a/pkg/api/resolver_query_find_notifications.go b/pkg/api/resolver_query_find_notifications.go new file mode 100644 index 000000000..b483cc7b1 --- /dev/null +++ b/pkg/api/resolver_query_find_notifications.go @@ -0,0 +1,29 @@ +package api + +import ( + "context" + + "github.com/stashapp/stash-box/pkg/models" +) + +func (r *queryResolver) QueryNotifications(ctx context.Context, input models.QueryNotificationsInput) (*models.QueryNotificationsResult, error) { + return &models.QueryNotificationsResult{ + Input: input, + }, nil +} + +type queryNotificationsResolver struct{ *Resolver } + +func (r *queryNotificationsResolver) Count(ctx context.Context, query *models.QueryNotificationsResult) (int, error) { + fac := r.getRepoFactory(ctx) + qb := fac.Notification() + currentUser := getCurrentUser(ctx) + return qb.GetNotificationsCount(currentUser.ID) +} + +func (r *queryNotificationsResolver) Notifications(ctx context.Context, query *models.QueryNotificationsResult) ([]*models.Notification, error) { + fac := r.getRepoFactory(ctx) + qb := fac.Notification() + currentUser := getCurrentUser(ctx) + return qb.GetNotifications(query.Input, currentUser.ID) +} diff --git a/pkg/api/resolver_query_notifications.go b/pkg/api/resolver_query_notifications.go index 2eee3b77b..06fc3c0d1 100644 --- a/pkg/api/resolver_query_notifications.go +++ b/pkg/api/resolver_query_notifications.go @@ -2,20 +2,11 @@ package api import ( "context" - - "github.com/stashapp/stash-box/pkg/models" ) func (r *queryResolver) GetUnreadNotificationCount(ctx context.Context) (int, error) { fac := r.getRepoFactory(ctx) qb := fac.Notification() currentUser := getCurrentUser(ctx) - return qb.GetUnreadCount(currentUser.ID) -} - -func (r *queryResolver) QueryNotifications(ctx context.Context, input models.NotificationQueryInput) ([]*models.Notification, error) { - fac := r.getRepoFactory(ctx) - qb := fac.Notification() - currentUser := getCurrentUser(ctx) - return qb.GetNotifications(input, currentUser.ID) + return qb.GetUnreadNotificationsCount(currentUser.ID) } diff --git a/pkg/models/generated_exec.go b/pkg/models/generated_exec.go index 18e117bf7..5ebd53181 100644 --- a/pkg/models/generated_exec.go +++ b/pkg/models/generated_exec.go @@ -50,6 +50,7 @@ type ResolverRoot interface { Query() QueryResolver QueryEditsResultType() QueryEditsResultTypeResolver QueryExistingSceneResult() QueryExistingSceneResultResolver + QueryNotificationsResult() QueryNotificationsResultResolver QueryPerformersResultType() QueryPerformersResultTypeResolver QueryScenesResultType() QueryScenesResultTypeResolver Scene() SceneResolver @@ -387,7 +388,7 @@ type ComplexityRoot struct { Me func(childComplexity int) int QueryEdits func(childComplexity int, input EditQueryInput) int QueryExistingScene func(childComplexity int, input QueryExistingSceneInput) int - QueryNotifications func(childComplexity int, input NotificationQueryInput) int + QueryNotifications func(childComplexity int, input QueryNotificationsInput) int QueryPerformers func(childComplexity int, input PerformerQueryInput) int QueryScenes func(childComplexity int, input SceneQueryInput) int QuerySites func(childComplexity int) int @@ -411,6 +412,11 @@ type ComplexityRoot struct { Scenes func(childComplexity int) int } + QueryNotificationsResult struct { + Count func(childComplexity int) int + Notifications func(childComplexity int) int + } + QueryPerformersResultType struct { Count func(childComplexity int) int Performers func(childComplexity int) int @@ -818,7 +824,7 @@ type QueryResolver interface { QueryExistingScene(ctx context.Context, input QueryExistingSceneInput) (*QueryExistingSceneResult, error) Version(ctx context.Context) (*Version, error) GetConfig(ctx context.Context) (*StashBoxConfig, error) - QueryNotifications(ctx context.Context, input NotificationQueryInput) ([]*Notification, error) + QueryNotifications(ctx context.Context, input QueryNotificationsInput) (*QueryNotificationsResult, error) GetUnreadNotificationCount(ctx context.Context) (int, error) } type QueryEditsResultTypeResolver interface { @@ -829,6 +835,10 @@ type QueryExistingSceneResultResolver interface { Edits(ctx context.Context, obj *QueryExistingSceneResult) ([]*Edit, error) Scenes(ctx context.Context, obj *QueryExistingSceneResult) ([]*Scene, error) } +type QueryNotificationsResultResolver interface { + Count(ctx context.Context, obj *QueryNotificationsResult) (int, error) + Notifications(ctx context.Context, obj *QueryNotificationsResult) ([]*Notification, error) +} type QueryPerformersResultTypeResolver interface { Count(ctx context.Context, obj *PerformerQuery) (int, error) Performers(ctx context.Context, obj *PerformerQuery) ([]*Performer, error) @@ -2887,7 +2897,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return 0, false } - return e.complexity.Query.QueryNotifications(childComplexity, args["input"].(NotificationQueryInput)), true + return e.complexity.Query.QueryNotifications(childComplexity, args["input"].(QueryNotificationsInput)), true case "Query.queryPerformers": if e.complexity.Query.QueryPerformers == nil { @@ -3034,6 +3044,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.QueryExistingSceneResult.Scenes(childComplexity), true + case "QueryNotificationsResult.count": + if e.complexity.QueryNotificationsResult.Count == nil { + break + } + + return e.complexity.QueryNotificationsResult.Count(childComplexity), true + + case "QueryNotificationsResult.notifications": + if e.complexity.QueryNotificationsResult.Notifications == nil { + break + } + + return e.complexity.QueryNotificationsResult.Notifications(childComplexity), true + case "QueryPerformersResultType.count": if e.complexity.QueryPerformersResultType.Count == nil { break @@ -4150,7 +4174,6 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputMultiIDCriterionInput, ec.unmarshalInputMultiStringCriterionInput, ec.unmarshalInputNewUserInput, - ec.unmarshalInputNotificationQueryInput, ec.unmarshalInputPerformerAppearanceInput, ec.unmarshalInputPerformerCreateInput, ec.unmarshalInputPerformerDestroyInput, @@ -4162,6 +4185,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputPerformerScenesInput, ec.unmarshalInputPerformerUpdateInput, ec.unmarshalInputQueryExistingSceneInput, + ec.unmarshalInputQueryNotificationsInput, ec.unmarshalInputResetPasswordInput, ec.unmarshalInputRevokeInviteInput, ec.unmarshalInputRoleCriterionInput, @@ -4605,10 +4629,15 @@ type UpdatedEdit { edit: Edit! } -input NotificationQueryInput { +input QueryNotificationsInput { page: Int! = 1 per_page: Int! = 25 } + +type QueryNotificationsResult { + count: Int! + notifications: [Notification!]! +} `, BuiltIn: false}, {Name: "../../graphql/schema/types/performer.graphql", Input: `enum GenderEnum { MALE @@ -5765,7 +5794,7 @@ type Query { ### Instance Config ### getConfig: StashBoxConfig! - queryNotifications(input: NotificationQueryInput!): [Notification!]! @hasRole(role: READ) + queryNotifications(input: QueryNotificationsInput!): QueryNotificationsResult! @hasRole(role: READ) getUnreadNotificationCount: Int! @hasRole(role: READ) } @@ -6970,10 +6999,10 @@ func (ec *executionContext) field_Query_queryExistingScene_args(ctx context.Cont func (ec *executionContext) field_Query_queryNotifications_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 NotificationQueryInput + var arg0 QueryNotificationsInput if tmp, ok := rawArgs["input"]; ok { ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("input")) - arg0, err = ec.unmarshalNNotificationQueryInput2githubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐNotificationQueryInput(ctx, tmp) + arg0, err = ec.unmarshalNQueryNotificationsInput2githubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐQueryNotificationsInput(ctx, tmp) if err != nil { return nil, err } @@ -22044,7 +22073,7 @@ func (ec *executionContext) _Query_queryNotifications(ctx context.Context, field resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { directive0 := func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().QueryNotifications(rctx, fc.Args["input"].(NotificationQueryInput)) + return ec.resolvers.Query().QueryNotifications(rctx, fc.Args["input"].(QueryNotificationsInput)) } directive1 := func(ctx context.Context) (interface{}, error) { role, err := ec.unmarshalNRoleEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐRoleEnum(ctx, "READ") @@ -22064,10 +22093,10 @@ func (ec *executionContext) _Query_queryNotifications(ctx context.Context, field if tmp == nil { return nil, nil } - if data, ok := tmp.([]*Notification); ok { + if data, ok := tmp.(*QueryNotificationsResult); ok { return data, nil } - return nil, fmt.Errorf(`unexpected type %T from directive, should be []*github.com/stashapp/stash-box/pkg/models.Notification`, tmp) + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/stashapp/stash-box/pkg/models.QueryNotificationsResult`, tmp) }) if err != nil { ec.Error(ctx, err) @@ -22078,9 +22107,9 @@ func (ec *executionContext) _Query_queryNotifications(ctx context.Context, field } return graphql.Null } - res := resTmp.([]*Notification) + res := resTmp.(*QueryNotificationsResult) fc.Result = res - return ec.marshalNNotification2ᚕᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐNotificationᚄ(ctx, field.Selections, res) + return ec.marshalNQueryNotificationsResult2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐQueryNotificationsResult(ctx, field.Selections, res) } func (ec *executionContext) fieldContext_Query_queryNotifications(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { @@ -22091,14 +22120,12 @@ func (ec *executionContext) fieldContext_Query_queryNotifications(ctx context.Co IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "created": - return ec.fieldContext_Notification_created(ctx, field) - case "read": - return ec.fieldContext_Notification_read(ctx, field) - case "data": - return ec.fieldContext_Notification_data(ctx, field) + case "count": + return ec.fieldContext_QueryNotificationsResult_count(ctx, field) + case "notifications": + return ec.fieldContext_QueryNotificationsResult_notifications(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type Notification", field.Name) + return nil, fmt.Errorf("no field named %q was found under type QueryNotificationsResult", field.Name) }, } defer func() { @@ -22607,6 +22634,102 @@ func (ec *executionContext) fieldContext_QueryExistingSceneResult_scenes(ctx con return fc, nil } +func (ec *executionContext) _QueryNotificationsResult_count(ctx context.Context, field graphql.CollectedField, obj *QueryNotificationsResult) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_QueryNotificationsResult_count(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.QueryNotificationsResult().Count(rctx, obj) + }) + 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.(int) + fc.Result = res + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_QueryNotificationsResult_count(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "QueryNotificationsResult", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Int does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _QueryNotificationsResult_notifications(ctx context.Context, field graphql.CollectedField, obj *QueryNotificationsResult) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_QueryNotificationsResult_notifications(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.QueryNotificationsResult().Notifications(rctx, obj) + }) + 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.([]*Notification) + fc.Result = res + return ec.marshalNNotification2ᚕᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐNotificationᚄ(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_QueryNotificationsResult_notifications(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "QueryNotificationsResult", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "created": + return ec.fieldContext_Notification_created(ctx, field) + case "read": + return ec.fieldContext_Notification_read(ctx, field) + case "data": + return ec.fieldContext_Notification_data(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Notification", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _QueryPerformersResultType_count(ctx context.Context, field graphql.CollectedField, obj *PerformerQuery) (ret graphql.Marshaler) { fc, err := ec.fieldContext_QueryPerformersResultType_count(ctx, field) if err != nil { @@ -33148,49 +33271,6 @@ func (ec *executionContext) unmarshalInputNewUserInput(ctx context.Context, obj return it, nil } -func (ec *executionContext) unmarshalInputNotificationQueryInput(ctx context.Context, obj interface{}) (NotificationQueryInput, error) { - var it NotificationQueryInput - asMap := map[string]interface{}{} - for k, v := range obj.(map[string]interface{}) { - asMap[k] = v - } - - if _, present := asMap["page"]; !present { - asMap["page"] = 1 - } - if _, present := asMap["per_page"]; !present { - asMap["per_page"] = 25 - } - - fieldsInOrder := [...]string{"page", "per_page"} - for _, k := range fieldsInOrder { - v, ok := asMap[k] - if !ok { - continue - } - switch k { - case "page": - var err error - - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) - it.Page, err = ec.unmarshalNInt2int(ctx, v) - if err != nil { - return it, err - } - case "per_page": - var err error - - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("per_page")) - it.PerPage, err = ec.unmarshalNInt2int(ctx, v) - if err != nil { - return it, err - } - } - } - - return it, nil -} - func (ec *executionContext) unmarshalInputPerformerAppearanceInput(ctx context.Context, obj interface{}) (PerformerAppearanceInput, error) { var it PerformerAppearanceInput asMap := map[string]interface{}{} @@ -34455,6 +34535,49 @@ func (ec *executionContext) unmarshalInputQueryExistingSceneInput(ctx context.Co return it, nil } +func (ec *executionContext) unmarshalInputQueryNotificationsInput(ctx context.Context, obj interface{}) (QueryNotificationsInput, error) { + var it QueryNotificationsInput + asMap := map[string]interface{}{} + for k, v := range obj.(map[string]interface{}) { + asMap[k] = v + } + + if _, present := asMap["page"]; !present { + asMap["page"] = 1 + } + if _, present := asMap["per_page"]; !present { + asMap["per_page"] = 25 + } + + fieldsInOrder := [...]string{"page", "per_page"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "page": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) + it.Page, err = ec.unmarshalNInt2int(ctx, v) + if err != nil { + return it, err + } + case "per_page": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("per_page")) + it.PerPage, err = ec.unmarshalNInt2int(ctx, v) + if err != nil { + return it, err + } + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputResetPasswordInput(ctx context.Context, obj interface{}) (ResetPasswordInput, error) { var it ResetPasswordInput asMap := map[string]interface{}{} @@ -40366,6 +40489,67 @@ func (ec *executionContext) _QueryExistingSceneResult(ctx context.Context, sel a return out } +var queryNotificationsResultImplementors = []string{"QueryNotificationsResult"} + +func (ec *executionContext) _QueryNotificationsResult(ctx context.Context, sel ast.SelectionSet, obj *QueryNotificationsResult) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, queryNotificationsResultImplementors) + out := graphql.NewFieldSet(fields) + var invalids uint32 + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("QueryNotificationsResult") + case "count": + field := field + + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._QueryNotificationsResult_count(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + } + + out.Concurrently(i, func() graphql.Marshaler { + return innerFunc(ctx) + + }) + case "notifications": + field := field + + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._QueryNotificationsResult_notifications(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + } + + out.Concurrently(i, func() graphql.Marshaler { + return innerFunc(ctx) + + }) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalids > 0 { + return graphql.Null + } + return out +} + var queryPerformersResultTypeImplementors = []string{"QueryPerformersResultType"} func (ec *executionContext) _QueryPerformersResultType(ctx context.Context, sel ast.SelectionSet, obj *PerformerQuery) graphql.Marshaler { @@ -43964,11 +44148,6 @@ func (ec *executionContext) marshalNNotificationData2githubᚗcomᚋstashappᚋs return ec._NotificationData(ctx, sel, v) } -func (ec *executionContext) unmarshalNNotificationQueryInput2githubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐNotificationQueryInput(ctx context.Context, v interface{}) (NotificationQueryInput, error) { - res, err := ec.unmarshalInputNotificationQueryInput(ctx, v) - return res, graphql.ErrorOnPath(ctx, err) -} - func (ec *executionContext) unmarshalNOperationEnum2githubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐOperationEnum(ctx context.Context, v interface{}) (OperationEnum, error) { var res OperationEnum err := res.UnmarshalGQL(v) @@ -44219,6 +44398,25 @@ func (ec *executionContext) marshalNQueryExistingSceneResult2ᚖgithubᚗcomᚋs return ec._QueryExistingSceneResult(ctx, sel, v) } +func (ec *executionContext) unmarshalNQueryNotificationsInput2githubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐQueryNotificationsInput(ctx context.Context, v interface{}) (QueryNotificationsInput, error) { + res, err := ec.unmarshalInputQueryNotificationsInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNQueryNotificationsResult2githubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐQueryNotificationsResult(ctx context.Context, sel ast.SelectionSet, v QueryNotificationsResult) graphql.Marshaler { + return ec._QueryNotificationsResult(ctx, sel, &v) +} + +func (ec *executionContext) marshalNQueryNotificationsResult2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐQueryNotificationsResult(ctx context.Context, sel ast.SelectionSet, v *QueryNotificationsResult) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._QueryNotificationsResult(ctx, sel, v) +} + func (ec *executionContext) marshalNQueryPerformersResultType2githubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐPerformerQuery(ctx context.Context, sel ast.SelectionSet, v PerformerQuery) graphql.Marshaler { return ec._QueryPerformersResultType(ctx, sel, &v) } diff --git a/pkg/models/generated_models.go b/pkg/models/generated_models.go index 469781bf5..bf812db18 100644 --- a/pkg/models/generated_models.go +++ b/pkg/models/generated_models.go @@ -293,11 +293,6 @@ type NewUserInput struct { InviteKey *string `json:"invite_key"` } -type NotificationQueryInput struct { - Page int `json:"page"` - PerPage int `json:"per_page"` -} - type PerformerAppearance struct { Performer *Performer `json:"performer"` // Performing as alias @@ -487,6 +482,11 @@ type QueryExistingSceneInput struct { Fingerprints []*FingerprintInput `json:"fingerprints"` } +type QueryNotificationsInput struct { + Page int `json:"page"` + PerPage int `json:"per_page"` +} + type QuerySitesResultType struct { Count int `json:"count"` Sites []*Site `json:"sites"` diff --git a/pkg/models/model_notification.go b/pkg/models/model_notification.go index c08198920..c9e0f82a5 100644 --- a/pkg/models/model_notification.go +++ b/pkg/models/model_notification.go @@ -41,3 +41,7 @@ func (s Notifications) Each(fn func(interface{})) { func (s *Notifications) Add(o interface{}) { *s = append(*s, o.(*Notification)) } + +type QueryNotificationsResult struct { + Input QueryNotificationsInput +} diff --git a/pkg/models/notification.go b/pkg/models/notification.go index b8427b4ea..536ddea31 100644 --- a/pkg/models/notification.go +++ b/pkg/models/notification.go @@ -3,8 +3,9 @@ package models import "github.com/gofrs/uuid" type NotificationRepo interface { - GetUnreadCount(userID uuid.UUID) (int, error) - GetNotifications(filter NotificationQueryInput, userID uuid.UUID) ([]*Notification, error) + GetNotificationsCount(userID uuid.UUID) (int, error) + GetUnreadNotificationsCount(userID uuid.UUID) (int, error) + GetNotifications(filter QueryNotificationsInput, userID uuid.UUID) ([]*Notification, error) TriggerSceneCreationNotifications(sceneID uuid.UUID) error TriggerPerformerEditNotifications(editID uuid.UUID) error diff --git a/pkg/sqlx/querybuilder_notifications.go b/pkg/sqlx/querybuilder_notifications.go index 772685ba3..52f8f3037 100644 --- a/pkg/sqlx/querybuilder_notifications.go +++ b/pkg/sqlx/querybuilder_notifications.go @@ -172,13 +172,19 @@ INSERT INTO notifications return err } -func (qb *notificationsQueryBuilder) GetUnreadCount(userID uuid.UUID) (int, error) { +func (qb *notificationsQueryBuilder) GetUnreadNotificationsCount(userID uuid.UUID) (int, error) { var args []interface{} args = append(args, userID) return runCountQuery(qb.dbi.db(), buildCountQuery("SELECT * FROM notifications WHERE user_id = ? AND read_at IS NULL"), args) } -func (qb *notificationsQueryBuilder) GetNotifications(filter models.NotificationQueryInput, userID uuid.UUID) ([]*models.Notification, error) { +func (qb *notificationsQueryBuilder) GetNotificationsCount(userID uuid.UUID) (int, error) { + var args []interface{} + args = append(args, userID) + return runCountQuery(qb.dbi.db(), buildCountQuery("SELECT * FROM notifications WHERE user_id = ?"), args) +} + +func (qb *notificationsQueryBuilder) GetNotifications(filter models.QueryNotificationsInput, userID uuid.UUID) ([]*models.Notification, error) { query := newQueryBuilder(notificationDBTable) query.Eq("user_id", userID) query.Pagination = getPagination(filter.Page, filter.PerPage) From b5a527dfef7919e99ccd6d4afa46232949ac491c Mon Sep 17 00:00:00 2001 From: InfiniteStash <117855276+InfiniteStash@users.noreply.github.com> Date: Wed, 10 Jan 2024 19:58:13 +0000 Subject: [PATCH 6/8] WIP --- .../graphql/queries/QueryNotifications.gql | 13 +- frontend/src/graphql/types.ts | 8444 ++++++++++++++--- .../src/pages/notifications/Notification.tsx | 49 + .../src/pages/notifications/Notifications.tsx | 7 +- graphql/schema/types/edit.graphql | 1 + pkg/api/resolver_model_edit_comment.go | 7 + pkg/models/generated_exec.go | 124 + 7 files changed, 7322 insertions(+), 1323 deletions(-) create mode 100644 frontend/src/pages/notifications/Notification.tsx diff --git a/frontend/src/graphql/queries/QueryNotifications.gql b/frontend/src/graphql/queries/QueryNotifications.gql index 4496bdaa7..ee0215f22 100644 --- a/frontend/src/graphql/queries/QueryNotifications.gql +++ b/frontend/src/graphql/queries/QueryNotifications.gql @@ -2,6 +2,13 @@ #import "../fragments/EditFragment.gql" #import "../fragments/CommentFragment.gql" +fragment NotificationCommentFragment on EditComment { + ...CommentFragment + edit { + ...EditFragment + } +} + query Notifications($input: QueryNotificationsInput!) { queryNotifications(input: $input) { count @@ -32,17 +39,17 @@ query Notifications($input: QueryNotificationsInput!) { } ...on CommentOwnEdit { comment { - ...CommentFragment + ...NotificationCommentFragment } } ...on CommentCommentedEdit{ comment { - ...CommentFragment + ...NotificationCommentFragment } } ...on CommentVotedEdit { comment { - ...CommentFragment + ...NotificationCommentFragment } } ...on DownvoteOwnEdit { diff --git a/frontend/src/graphql/types.ts b/frontend/src/graphql/types.ts index ba6b899f5..f52041e79 100644 --- a/frontend/src/graphql/types.ts +++ b/frontend/src/graphql/types.ts @@ -183,6 +183,7 @@ export type EditComment = { __typename: "EditComment"; comment: Scalars["String"]; date: Scalars["Time"]; + edit: Edit; id: Scalars["ID"]; user?: Maybe; }; @@ -18036,157 +18037,4365 @@ export type QueryExistingSceneQuery = { }; }; -export type NotificationsQueryVariables = Exact<{ - input: QueryNotificationsInput; -}>; - -export type NotificationsQuery = { - __typename: "Query"; - queryNotifications: { - __typename: "QueryNotificationsResult"; - count: number; - notifications: Array<{ - __typename: "Notification"; - created: string; - read: boolean; - data: - | { - __typename: "CommentCommentedEdit"; - comment: { - __typename: "EditComment"; +export type NotificationCommentFragment = { + __typename: "EditComment"; + id: string; + date: string; + comment: string; + edit: { + __typename: "Edit"; + id: string; + target_type: TargetTypeEnum; + operation: OperationEnum; + status: VoteStatusEnum; + bot: boolean; + applied: boolean; + created: string; + updated?: string | null; + closed?: string | null; + expires?: string | null; + vote_count: number; + destructive: boolean; + comments: Array<{ + __typename: "EditComment"; + id: string; + date: string; + comment: string; + user?: { __typename: "User"; id: string; name: string } | null; + }>; + votes: Array<{ + __typename: "EditVote"; + date: string; + vote: VoteTypeEnum; + user?: { __typename: "User"; id: string; name: string } | null; + }>; + user?: { __typename: "User"; id: string; name: string } | null; + target?: + | { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; id: string; - date: string; - comment: string; - user?: { __typename: "User"; id: string; name: string } | null; + name: string; + icon: string; }; - } - | { - __typename: "CommentOwnEdit"; - comment: { - __typename: "EditComment"; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + } + | { + __typename: "Scene"; + id: string; + release_date?: string | null; + title?: string | null; + deleted: boolean; + details?: string | null; + director?: string | null; + code?: string | null; + duration?: number | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; id: string; - date: string; - comment: string; - user?: { __typename: "User"; id: string; name: string } | null; + name: string; + icon: string; }; - } - | { - __typename: "CommentVotedEdit"; - comment: { - __typename: "EditComment"; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + studio?: { + __typename: "Studio"; + id: string; + name: string; + parent?: { __typename: "Studio"; id: string; name: string } | null; + } | null; + performers: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; id: string; - date: string; - comment: string; - user?: { __typename: "User"; id: string; name: string } | null; + name: string; + disambiguation?: string | null; + deleted: boolean; + gender?: GenderEnum | null; + aliases: Array; }; - } - | { - __typename: "DownvoteOwnEdit"; - edit: { - __typename: "Edit"; + }>; + fingerprints: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + submissions: number; + user_submitted: boolean; + created: string; + updated: string; + }>; + tags: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + aliases: Array; + }>; + } + | { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { __typename: "Studio"; id: string; name: string } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; id: string; - target_type: TargetTypeEnum; - operation: OperationEnum; - status: VoteStatusEnum; - bot: boolean; - applied: boolean; - created: string; - updated?: string | null; - closed?: string | null; - expires?: string | null; - vote_count: number; - destructive: boolean; - comments: Array<{ - __typename: "EditComment"; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } + | { + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + details?: + | { + __typename: "PerformerEdit"; + name?: string | null; + disambiguation?: string | null; + added_aliases?: Array | null; + removed_aliases?: Array | null; + gender?: GenderEnum | null; + birthdate?: string | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + eye_color?: EyeColorEnum | null; + hair_color?: HairColorEnum | null; + height?: number | null; + cup_size?: string | null; + band_size?: number | null; + waist_size?: number | null; + hip_size?: number | null; + breast_type?: BreastTypeEnum | null; + career_start_year?: number | null; + career_end_year?: number | null; + draft_id?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + added_tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + removed_tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + added_piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + removed_piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + } + | { + __typename: "SceneEdit"; + title?: string | null; + details?: string | null; + date?: string | null; + duration?: number | null; + director?: string | null; + code?: string | null; + draft_id?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + studio?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { __typename: "Studio"; id: string; name: string } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; id: string; - date: string; - comment: string; - user?: { __typename: "User"; id: string; name: string } | null; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; }>; - votes: Array<{ - __typename: "EditVote"; - date: string; - vote: VoteTypeEnum; - user?: { __typename: "User"; id: string; name: string } | null; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; }>; - user?: { __typename: "User"; id: string; name: string } | null; - target?: - | { - __typename: "Performer"; - id: string; - name: string; - disambiguation?: string | null; - deleted: boolean; - aliases: Array; - gender?: GenderEnum | null; - birth_date?: string | null; - age?: number | null; - height?: number | null; - hair_color?: HairColorEnum | null; - eye_color?: EyeColorEnum | null; - ethnicity?: EthnicityEnum | null; - country?: string | null; - career_end_year?: number | null; - career_start_year?: number | null; - breast_type?: BreastTypeEnum | null; - waist_size?: number | null; - hip_size?: number | null; - band_size?: number | null; - cup_size?: string | null; - is_favorite: boolean; - tattoos?: Array<{ - __typename: "BodyModification"; - location: string; - description?: string | null; - }> | null; - piercings?: Array<{ - __typename: "BodyModification"; - location: string; - description?: string | null; - }> | null; - urls: Array<{ - __typename: "URL"; - url: string; - site: { - __typename: "Site"; - id: string; - name: string; - icon: string; - }; - }>; - images: Array<{ - __typename: "Image"; - id: string; - url: string; - width: number; - height: number; - }>; - } - | { - __typename: "Scene"; - id: string; - release_date?: string | null; - title?: string | null; - deleted: boolean; - details?: string | null; - director?: string | null; - code?: string | null; - duration?: number | null; - urls: Array<{ - __typename: "URL"; - url: string; - site: { - __typename: "Site"; - id: string; - name: string; - icon: string; - }; - }>; - images: Array<{ - __typename: "Image"; - id: string; - url: string; - width: number; + }; + }> | null; + removed_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + added_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + removed_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + added_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + removed_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + } + | { + __typename: "StudioEdit"; + name?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + parent?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { __typename: "Studio"; id: string; name: string } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + } + | { + __typename: "TagEdit"; + name?: string | null; + description?: string | null; + added_aliases?: Array | null; + removed_aliases?: Array | null; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + old_details?: + | { + __typename: "PerformerEdit"; + name?: string | null; + disambiguation?: string | null; + gender?: GenderEnum | null; + birthdate?: string | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + eye_color?: EyeColorEnum | null; + hair_color?: HairColorEnum | null; + height?: number | null; + cup_size?: string | null; + band_size?: number | null; + waist_size?: number | null; + hip_size?: number | null; + breast_type?: BreastTypeEnum | null; + career_start_year?: number | null; + career_end_year?: number | null; + } + | { + __typename: "SceneEdit"; + title?: string | null; + details?: string | null; + date?: string | null; + duration?: number | null; + director?: string | null; + code?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + studio?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { __typename: "Studio"; id: string; name: string } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + removed_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + added_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + removed_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + added_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + removed_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + } + | { + __typename: "StudioEdit"; + name?: string | null; + parent?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { __typename: "Studio"; id: string; name: string } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + } + | { + __typename: "TagEdit"; + name?: string | null; + description?: string | null; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + merge_sources: Array< + | { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + } + | { + __typename: "Scene"; + id: string; + release_date?: string | null; + title?: string | null; + deleted: boolean; + details?: string | null; + director?: string | null; + code?: string | null; + duration?: number | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + studio?: { + __typename: "Studio"; + id: string; + name: string; + parent?: { __typename: "Studio"; id: string; name: string } | null; + } | null; + performers: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + gender?: GenderEnum | null; + aliases: Array; + }; + }>; + fingerprints: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + submissions: number; + user_submitted: boolean; + created: string; + updated: string; + }>; + tags: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + aliases: Array; + }>; + } + | { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { __typename: "Studio"; id: string; name: string } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } + | { + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + >; + options?: { + __typename: "PerformerEditOptions"; + set_modify_aliases: boolean; + set_merge_aliases: boolean; + } | null; + }; + user?: { __typename: "User"; id: string; name: string } | null; +}; + +export type NotificationsQueryVariables = Exact<{ + input: QueryNotificationsInput; +}>; + +export type NotificationsQuery = { + __typename: "Query"; + queryNotifications: { + __typename: "QueryNotificationsResult"; + count: number; + notifications: Array<{ + __typename: "Notification"; + created: string; + read: boolean; + data: + | { + __typename: "CommentCommentedEdit"; + comment: { + __typename: "EditComment"; + id: string; + date: string; + comment: string; + edit: { + __typename: "Edit"; + id: string; + target_type: TargetTypeEnum; + operation: OperationEnum; + status: VoteStatusEnum; + bot: boolean; + applied: boolean; + created: string; + updated?: string | null; + closed?: string | null; + expires?: string | null; + vote_count: number; + destructive: boolean; + comments: Array<{ + __typename: "EditComment"; + id: string; + date: string; + comment: string; + user?: { + __typename: "User"; + id: string; + name: string; + } | null; + }>; + votes: Array<{ + __typename: "EditVote"; + date: string; + vote: VoteTypeEnum; + user?: { + __typename: "User"; + id: string; + name: string; + } | null; + }>; + user?: { __typename: "User"; id: string; name: string } | null; + target?: + | { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + } + | { + __typename: "Scene"; + id: string; + release_date?: string | null; + title?: string | null; + deleted: boolean; + details?: string | null; + director?: string | null; + code?: string | null; + duration?: number | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + studio?: { + __typename: "Studio"; + id: string; + name: string; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + } | null; + performers: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + gender?: GenderEnum | null; + aliases: Array; + }; + }>; + fingerprints: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + submissions: number; + user_submitted: boolean; + created: string; + updated: string; + }>; + tags: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + aliases: Array; + }>; + } + | { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } + | { + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + details?: + | { + __typename: "PerformerEdit"; + name?: string | null; + disambiguation?: string | null; + added_aliases?: Array | null; + removed_aliases?: Array | null; + gender?: GenderEnum | null; + birthdate?: string | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + eye_color?: EyeColorEnum | null; + hair_color?: HairColorEnum | null; + height?: number | null; + cup_size?: string | null; + band_size?: number | null; + waist_size?: number | null; + hip_size?: number | null; + breast_type?: BreastTypeEnum | null; + career_start_year?: number | null; + career_end_year?: number | null; + draft_id?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + added_tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + removed_tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + added_piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + removed_piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + } + | { + __typename: "SceneEdit"; + title?: string | null; + details?: string | null; + date?: string | null; + duration?: number | null; + director?: string | null; + code?: string | null; + draft_id?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + studio?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + removed_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + added_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + removed_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + added_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + removed_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + } + | { + __typename: "StudioEdit"; + name?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + parent?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + } + | { + __typename: "TagEdit"; + name?: string | null; + description?: string | null; + added_aliases?: Array | null; + removed_aliases?: Array | null; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + old_details?: + | { + __typename: "PerformerEdit"; + name?: string | null; + disambiguation?: string | null; + gender?: GenderEnum | null; + birthdate?: string | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + eye_color?: EyeColorEnum | null; + hair_color?: HairColorEnum | null; + height?: number | null; + cup_size?: string | null; + band_size?: number | null; + waist_size?: number | null; + hip_size?: number | null; + breast_type?: BreastTypeEnum | null; + career_start_year?: number | null; + career_end_year?: number | null; + } + | { + __typename: "SceneEdit"; + title?: string | null; + details?: string | null; + date?: string | null; + duration?: number | null; + director?: string | null; + code?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + studio?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + removed_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + added_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + removed_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + added_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + removed_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + } + | { + __typename: "StudioEdit"; + name?: string | null; + parent?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + } + | { + __typename: "TagEdit"; + name?: string | null; + description?: string | null; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + merge_sources: Array< + | { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + } + | { + __typename: "Scene"; + id: string; + release_date?: string | null; + title?: string | null; + deleted: boolean; + details?: string | null; + director?: string | null; + code?: string | null; + duration?: number | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + studio?: { + __typename: "Studio"; + id: string; + name: string; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + } | null; + performers: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + gender?: GenderEnum | null; + aliases: Array; + }; + }>; + fingerprints: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + submissions: number; + user_submitted: boolean; + created: string; + updated: string; + }>; + tags: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + aliases: Array; + }>; + } + | { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } + | { + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + >; + options?: { + __typename: "PerformerEditOptions"; + set_modify_aliases: boolean; + set_merge_aliases: boolean; + } | null; + }; + user?: { __typename: "User"; id: string; name: string } | null; + }; + } + | { + __typename: "CommentOwnEdit"; + comment: { + __typename: "EditComment"; + id: string; + date: string; + comment: string; + edit: { + __typename: "Edit"; + id: string; + target_type: TargetTypeEnum; + operation: OperationEnum; + status: VoteStatusEnum; + bot: boolean; + applied: boolean; + created: string; + updated?: string | null; + closed?: string | null; + expires?: string | null; + vote_count: number; + destructive: boolean; + comments: Array<{ + __typename: "EditComment"; + id: string; + date: string; + comment: string; + user?: { + __typename: "User"; + id: string; + name: string; + } | null; + }>; + votes: Array<{ + __typename: "EditVote"; + date: string; + vote: VoteTypeEnum; + user?: { + __typename: "User"; + id: string; + name: string; + } | null; + }>; + user?: { __typename: "User"; id: string; name: string } | null; + target?: + | { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + } + | { + __typename: "Scene"; + id: string; + release_date?: string | null; + title?: string | null; + deleted: boolean; + details?: string | null; + director?: string | null; + code?: string | null; + duration?: number | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + studio?: { + __typename: "Studio"; + id: string; + name: string; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + } | null; + performers: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + gender?: GenderEnum | null; + aliases: Array; + }; + }>; + fingerprints: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + submissions: number; + user_submitted: boolean; + created: string; + updated: string; + }>; + tags: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + aliases: Array; + }>; + } + | { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } + | { + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + details?: + | { + __typename: "PerformerEdit"; + name?: string | null; + disambiguation?: string | null; + added_aliases?: Array | null; + removed_aliases?: Array | null; + gender?: GenderEnum | null; + birthdate?: string | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + eye_color?: EyeColorEnum | null; + hair_color?: HairColorEnum | null; + height?: number | null; + cup_size?: string | null; + band_size?: number | null; + waist_size?: number | null; + hip_size?: number | null; + breast_type?: BreastTypeEnum | null; + career_start_year?: number | null; + career_end_year?: number | null; + draft_id?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + added_tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + removed_tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + added_piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + removed_piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + } + | { + __typename: "SceneEdit"; + title?: string | null; + details?: string | null; + date?: string | null; + duration?: number | null; + director?: string | null; + code?: string | null; + draft_id?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + studio?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + removed_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + added_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + removed_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + added_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + removed_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + } + | { + __typename: "StudioEdit"; + name?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + parent?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + } + | { + __typename: "TagEdit"; + name?: string | null; + description?: string | null; + added_aliases?: Array | null; + removed_aliases?: Array | null; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + old_details?: + | { + __typename: "PerformerEdit"; + name?: string | null; + disambiguation?: string | null; + gender?: GenderEnum | null; + birthdate?: string | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + eye_color?: EyeColorEnum | null; + hair_color?: HairColorEnum | null; + height?: number | null; + cup_size?: string | null; + band_size?: number | null; + waist_size?: number | null; + hip_size?: number | null; + breast_type?: BreastTypeEnum | null; + career_start_year?: number | null; + career_end_year?: number | null; + } + | { + __typename: "SceneEdit"; + title?: string | null; + details?: string | null; + date?: string | null; + duration?: number | null; + director?: string | null; + code?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + studio?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + removed_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + added_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + removed_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + added_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + removed_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + } + | { + __typename: "StudioEdit"; + name?: string | null; + parent?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + } + | { + __typename: "TagEdit"; + name?: string | null; + description?: string | null; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + merge_sources: Array< + | { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + } + | { + __typename: "Scene"; + id: string; + release_date?: string | null; + title?: string | null; + deleted: boolean; + details?: string | null; + director?: string | null; + code?: string | null; + duration?: number | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + studio?: { + __typename: "Studio"; + id: string; + name: string; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + } | null; + performers: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + gender?: GenderEnum | null; + aliases: Array; + }; + }>; + fingerprints: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + submissions: number; + user_submitted: boolean; + created: string; + updated: string; + }>; + tags: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + aliases: Array; + }>; + } + | { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } + | { + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + >; + options?: { + __typename: "PerformerEditOptions"; + set_modify_aliases: boolean; + set_merge_aliases: boolean; + } | null; + }; + user?: { __typename: "User"; id: string; name: string } | null; + }; + } + | { + __typename: "CommentVotedEdit"; + comment: { + __typename: "EditComment"; + id: string; + date: string; + comment: string; + edit: { + __typename: "Edit"; + id: string; + target_type: TargetTypeEnum; + operation: OperationEnum; + status: VoteStatusEnum; + bot: boolean; + applied: boolean; + created: string; + updated?: string | null; + closed?: string | null; + expires?: string | null; + vote_count: number; + destructive: boolean; + comments: Array<{ + __typename: "EditComment"; + id: string; + date: string; + comment: string; + user?: { + __typename: "User"; + id: string; + name: string; + } | null; + }>; + votes: Array<{ + __typename: "EditVote"; + date: string; + vote: VoteTypeEnum; + user?: { + __typename: "User"; + id: string; + name: string; + } | null; + }>; + user?: { __typename: "User"; id: string; name: string } | null; + target?: + | { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + } + | { + __typename: "Scene"; + id: string; + release_date?: string | null; + title?: string | null; + deleted: boolean; + details?: string | null; + director?: string | null; + code?: string | null; + duration?: number | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + studio?: { + __typename: "Studio"; + id: string; + name: string; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + } | null; + performers: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + gender?: GenderEnum | null; + aliases: Array; + }; + }>; + fingerprints: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + submissions: number; + user_submitted: boolean; + created: string; + updated: string; + }>; + tags: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + aliases: Array; + }>; + } + | { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } + | { + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + details?: + | { + __typename: "PerformerEdit"; + name?: string | null; + disambiguation?: string | null; + added_aliases?: Array | null; + removed_aliases?: Array | null; + gender?: GenderEnum | null; + birthdate?: string | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + eye_color?: EyeColorEnum | null; + hair_color?: HairColorEnum | null; + height?: number | null; + cup_size?: string | null; + band_size?: number | null; + waist_size?: number | null; + hip_size?: number | null; + breast_type?: BreastTypeEnum | null; + career_start_year?: number | null; + career_end_year?: number | null; + draft_id?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + added_tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + removed_tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + added_piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + removed_piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + } + | { + __typename: "SceneEdit"; + title?: string | null; + details?: string | null; + date?: string | null; + duration?: number | null; + director?: string | null; + code?: string | null; + draft_id?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + studio?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + removed_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + added_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + removed_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + added_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + removed_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + } + | { + __typename: "StudioEdit"; + name?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + parent?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + } + | { + __typename: "TagEdit"; + name?: string | null; + description?: string | null; + added_aliases?: Array | null; + removed_aliases?: Array | null; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + old_details?: + | { + __typename: "PerformerEdit"; + name?: string | null; + disambiguation?: string | null; + gender?: GenderEnum | null; + birthdate?: string | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + eye_color?: EyeColorEnum | null; + hair_color?: HairColorEnum | null; + height?: number | null; + cup_size?: string | null; + band_size?: number | null; + waist_size?: number | null; + hip_size?: number | null; + breast_type?: BreastTypeEnum | null; + career_start_year?: number | null; + career_end_year?: number | null; + } + | { + __typename: "SceneEdit"; + title?: string | null; + details?: string | null; + date?: string | null; + duration?: number | null; + director?: string | null; + code?: string | null; + added_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + removed_urls?: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }> | null; + studio?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + added_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + removed_performers?: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + }; + }> | null; + added_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + removed_tags?: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + }> | null; + added_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + removed_images?: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + } | null> | null; + added_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + removed_fingerprints?: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + }> | null; + } + | { + __typename: "StudioEdit"; + name?: string | null; + parent?: { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } | null; + } + | { + __typename: "TagEdit"; + name?: string | null; + description?: string | null; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + | null; + merge_sources: Array< + | { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + } + | { + __typename: "Scene"; + id: string; + release_date?: string | null; + title?: string | null; + deleted: boolean; + details?: string | null; + director?: string | null; + code?: string | null; + duration?: number | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + studio?: { + __typename: "Studio"; + id: string; + name: string; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + } | null; + performers: Array<{ + __typename: "PerformerAppearance"; + as?: string | null; + performer: { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + gender?: GenderEnum | null; + aliases: Array; + }; + }>; + fingerprints: Array<{ + __typename: "Fingerprint"; + hash: string; + algorithm: FingerprintAlgorithm; + duration: number; + submissions: number; + user_submitted: boolean; + created: string; + updated: string; + }>; + tags: Array<{ + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + aliases: Array; + }>; + } + | { + __typename: "Studio"; + id: string; + name: string; + deleted: boolean; + is_favorite: boolean; + child_studios: Array<{ + __typename: "Studio"; + id: string; + name: string; + }>; + parent?: { + __typename: "Studio"; + id: string; + name: string; + } | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + height: number; + width: number; + }>; + } + | { + __typename: "Tag"; + id: string; + name: string; + description?: string | null; + deleted: boolean; + aliases: Array; + category?: { + __typename: "TagCategory"; + id: string; + name: string; + } | null; + } + >; + options?: { + __typename: "PerformerEditOptions"; + set_modify_aliases: boolean; + set_merge_aliases: boolean; + } | null; + }; + user?: { __typename: "User"; id: string; name: string } | null; + }; + } + | { + __typename: "DownvoteOwnEdit"; + edit: { + __typename: "Edit"; + id: string; + target_type: TargetTypeEnum; + operation: OperationEnum; + status: VoteStatusEnum; + bot: boolean; + applied: boolean; + created: string; + updated?: string | null; + closed?: string | null; + expires?: string | null; + vote_count: number; + destructive: boolean; + comments: Array<{ + __typename: "EditComment"; + id: string; + date: string; + comment: string; + user?: { __typename: "User"; id: string; name: string } | null; + }>; + votes: Array<{ + __typename: "EditVote"; + date: string; + vote: VoteTypeEnum; + user?: { __typename: "User"; id: string; name: string } | null; + }>; + user?: { __typename: "User"; id: string; name: string } | null; + target?: + | { + __typename: "Performer"; + id: string; + name: string; + disambiguation?: string | null; + deleted: boolean; + aliases: Array; + gender?: GenderEnum | null; + birth_date?: string | null; + age?: number | null; + height?: number | null; + hair_color?: HairColorEnum | null; + eye_color?: EyeColorEnum | null; + ethnicity?: EthnicityEnum | null; + country?: string | null; + career_end_year?: number | null; + career_start_year?: number | null; + breast_type?: BreastTypeEnum | null; + waist_size?: number | null; + hip_size?: number | null; + band_size?: number | null; + cup_size?: string | null; + is_favorite: boolean; + tattoos?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + piercings?: Array<{ + __typename: "BodyModification"; + location: string; + description?: string | null; + }> | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; + height: number; + }>; + } + | { + __typename: "Scene"; + id: string; + release_date?: string | null; + title?: string | null; + deleted: boolean; + details?: string | null; + director?: string | null; + code?: string | null; + duration?: number | null; + urls: Array<{ + __typename: "URL"; + url: string; + site: { + __typename: "Site"; + id: string; + name: string; + icon: string; + }; + }>; + images: Array<{ + __typename: "Image"; + id: string; + url: string; + width: number; height: number; }>; studio?: { @@ -24116,6 +28325,326 @@ export type VersionQuery = { }; }; +export const UrlFragmentDoc = { + kind: "Document", + definitions: [ + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "URLFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "URL" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "url" } }, + { + kind: "Field", + name: { kind: "Name", value: "site" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "icon" } }, + ], + }, + }, + ], + }, + }, + ], +} as unknown as DocumentNode; +export const ImageFragmentDoc = { + kind: "Document", + definitions: [ + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "ImageFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Image" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "url" } }, + { kind: "Field", name: { kind: "Name", value: "width" } }, + { kind: "Field", name: { kind: "Name", value: "height" } }, + ], + }, + }, + ], +} as unknown as DocumentNode; +export const ScenePerformerFragmentDoc = { + kind: "Document", + definitions: [ + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "ScenePerformerFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Performer" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "disambiguation" } }, + { kind: "Field", name: { kind: "Name", value: "deleted" } }, + { kind: "Field", name: { kind: "Name", value: "gender" } }, + { kind: "Field", name: { kind: "Name", value: "aliases" } }, + ], + }, + }, + ], +} as unknown as DocumentNode; +export const QuerySceneFragmentDoc = { + kind: "Document", + definitions: [ + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "QuerySceneFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Scene" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "release_date" } }, + { kind: "Field", name: { kind: "Name", value: "title" } }, + { kind: "Field", name: { kind: "Name", value: "duration" } }, + { + kind: "Field", + name: { kind: "Name", value: "urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "studio" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "performers" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "as" } }, + { + kind: "Field", + name: { kind: "Name", value: "performer" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ScenePerformerFragment" }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "URLFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "URL" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "url" } }, + { + kind: "Field", + name: { kind: "Name", value: "site" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "icon" } }, + ], + }, + }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "ImageFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Image" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "url" } }, + { kind: "Field", name: { kind: "Name", value: "width" } }, + { kind: "Field", name: { kind: "Name", value: "height" } }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "ScenePerformerFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Performer" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "disambiguation" } }, + { kind: "Field", name: { kind: "Name", value: "deleted" } }, + { kind: "Field", name: { kind: "Name", value: "gender" } }, + { kind: "Field", name: { kind: "Name", value: "aliases" } }, + ], + }, + }, + ], +} as unknown as DocumentNode; +export const SearchPerformerFragmentDoc = { + kind: "Document", + definitions: [ + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "SearchPerformerFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Performer" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "disambiguation" } }, + { kind: "Field", name: { kind: "Name", value: "deleted" } }, + { kind: "Field", name: { kind: "Name", value: "gender" } }, + { kind: "Field", name: { kind: "Name", value: "aliases" } }, + { kind: "Field", name: { kind: "Name", value: "country" } }, + { kind: "Field", name: { kind: "Name", value: "career_start_year" } }, + { kind: "Field", name: { kind: "Name", value: "career_end_year" } }, + { kind: "Field", name: { kind: "Name", value: "scene_count" } }, + { kind: "Field", name: { kind: "Name", value: "birth_date" } }, + { + kind: "Field", + name: { kind: "Name", value: "urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { kind: "Field", name: { kind: "Name", value: "is_favorite" } }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "URLFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "URL" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "url" } }, + { + kind: "Field", + name: { kind: "Name", value: "site" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "icon" } }, + ], + }, + }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "ImageFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Image" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "url" } }, + { kind: "Field", name: { kind: "Name", value: "width" } }, + { kind: "Field", name: { kind: "Name", value: "height" } }, + ], + }, + }, + ], +} as unknown as DocumentNode; export const CommentFragmentDoc = { kind: "Document", definitions: [ @@ -24146,11 +28675,1702 @@ export const CommentFragmentDoc = { ], }, }, - ], -} as unknown as DocumentNode; -export const TagFragmentDoc = { - kind: "Document", - definitions: [ + ], +} as unknown as DocumentNode; +export const TagFragmentDoc = { + kind: "Document", + definitions: [ + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "TagFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Tag" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "description" } }, + { kind: "Field", name: { kind: "Name", value: "deleted" } }, + { + kind: "Field", + name: { kind: "Name", value: "category" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + ], + }, + }, + { kind: "Field", name: { kind: "Name", value: "aliases" } }, + ], + }, + }, + ], +} as unknown as DocumentNode; +export const PerformerFragmentDoc = { + kind: "Document", + definitions: [ + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "PerformerFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Performer" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "disambiguation" } }, + { kind: "Field", name: { kind: "Name", value: "deleted" } }, + { kind: "Field", name: { kind: "Name", value: "aliases" } }, + { kind: "Field", name: { kind: "Name", value: "gender" } }, + { kind: "Field", name: { kind: "Name", value: "birth_date" } }, + { kind: "Field", name: { kind: "Name", value: "age" } }, + { kind: "Field", name: { kind: "Name", value: "height" } }, + { kind: "Field", name: { kind: "Name", value: "hair_color" } }, + { kind: "Field", name: { kind: "Name", value: "eye_color" } }, + { kind: "Field", name: { kind: "Name", value: "ethnicity" } }, + { kind: "Field", name: { kind: "Name", value: "country" } }, + { kind: "Field", name: { kind: "Name", value: "career_end_year" } }, + { kind: "Field", name: { kind: "Name", value: "career_start_year" } }, + { kind: "Field", name: { kind: "Name", value: "breast_type" } }, + { kind: "Field", name: { kind: "Name", value: "waist_size" } }, + { kind: "Field", name: { kind: "Name", value: "hip_size" } }, + { kind: "Field", name: { kind: "Name", value: "band_size" } }, + { kind: "Field", name: { kind: "Name", value: "cup_size" } }, + { + kind: "Field", + name: { kind: "Name", value: "tattoos" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "location" } }, + { kind: "Field", name: { kind: "Name", value: "description" } }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "piercings" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "location" } }, + { kind: "Field", name: { kind: "Name", value: "description" } }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { kind: "Field", name: { kind: "Name", value: "is_favorite" } }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "URLFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "URL" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "url" } }, + { + kind: "Field", + name: { kind: "Name", value: "site" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "icon" } }, + ], + }, + }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "ImageFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Image" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "url" } }, + { kind: "Field", name: { kind: "Name", value: "width" } }, + { kind: "Field", name: { kind: "Name", value: "height" } }, + ], + }, + }, + ], +} as unknown as DocumentNode; +export const StudioFragmentDoc = { + kind: "Document", + definitions: [ + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "StudioFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Studio" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { + kind: "Field", + name: { kind: "Name", value: "child_studios" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "parent" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "url" } }, + { kind: "Field", name: { kind: "Name", value: "height" } }, + { kind: "Field", name: { kind: "Name", value: "width" } }, + ], + }, + }, + { kind: "Field", name: { kind: "Name", value: "deleted" } }, + { kind: "Field", name: { kind: "Name", value: "is_favorite" } }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "URLFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "URL" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "url" } }, + { + kind: "Field", + name: { kind: "Name", value: "site" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "icon" } }, + ], + }, + }, + ], + }, + }, + ], +} as unknown as DocumentNode; +export const SceneFragmentDoc = { + kind: "Document", + definitions: [ + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "SceneFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Scene" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "release_date" } }, + { kind: "Field", name: { kind: "Name", value: "title" } }, + { kind: "Field", name: { kind: "Name", value: "deleted" } }, + { kind: "Field", name: { kind: "Name", value: "details" } }, + { kind: "Field", name: { kind: "Name", value: "director" } }, + { kind: "Field", name: { kind: "Name", value: "code" } }, + { kind: "Field", name: { kind: "Name", value: "duration" } }, + { + kind: "Field", + name: { kind: "Name", value: "urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "studio" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { + kind: "Field", + name: { kind: "Name", value: "parent" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + ], + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "performers" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "as" } }, + { + kind: "Field", + name: { kind: "Name", value: "performer" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ScenePerformerFragment" }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "fingerprints" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "hash" } }, + { kind: "Field", name: { kind: "Name", value: "algorithm" } }, + { kind: "Field", name: { kind: "Name", value: "duration" } }, + { kind: "Field", name: { kind: "Name", value: "submissions" } }, + { + kind: "Field", + name: { kind: "Name", value: "user_submitted" }, + }, + { kind: "Field", name: { kind: "Name", value: "created" } }, + { kind: "Field", name: { kind: "Name", value: "updated" } }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "tags" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "description" } }, + { kind: "Field", name: { kind: "Name", value: "aliases" } }, + ], + }, + }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "URLFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "URL" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "url" } }, + { + kind: "Field", + name: { kind: "Name", value: "site" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "icon" } }, + ], + }, + }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "ImageFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Image" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "url" } }, + { kind: "Field", name: { kind: "Name", value: "width" } }, + { kind: "Field", name: { kind: "Name", value: "height" } }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "ScenePerformerFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Performer" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "disambiguation" } }, + { kind: "Field", name: { kind: "Name", value: "deleted" } }, + { kind: "Field", name: { kind: "Name", value: "gender" } }, + { kind: "Field", name: { kind: "Name", value: "aliases" } }, + ], + }, + }, + ], +} as unknown as DocumentNode; +export const FingerprintFragmentDoc = { + kind: "Document", + definitions: [ + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "FingerprintFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Fingerprint" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "hash" } }, + { kind: "Field", name: { kind: "Name", value: "algorithm" } }, + { kind: "Field", name: { kind: "Name", value: "duration" } }, + ], + }, + }, + ], +} as unknown as DocumentNode; +export const EditFragmentDoc = { + kind: "Document", + definitions: [ + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "EditFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Edit" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "target_type" } }, + { kind: "Field", name: { kind: "Name", value: "operation" } }, + { kind: "Field", name: { kind: "Name", value: "status" } }, + { kind: "Field", name: { kind: "Name", value: "bot" } }, + { kind: "Field", name: { kind: "Name", value: "applied" } }, + { kind: "Field", name: { kind: "Name", value: "created" } }, + { kind: "Field", name: { kind: "Name", value: "updated" } }, + { kind: "Field", name: { kind: "Name", value: "closed" } }, + { kind: "Field", name: { kind: "Name", value: "expires" } }, + { kind: "Field", name: { kind: "Name", value: "vote_count" } }, + { kind: "Field", name: { kind: "Name", value: "destructive" } }, + { + kind: "Field", + name: { kind: "Name", value: "comments" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "CommentFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "votes" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "user" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + ], + }, + }, + { kind: "Field", name: { kind: "Name", value: "date" } }, + { kind: "Field", name: { kind: "Name", value: "vote" } }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "user" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "target" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Tag" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "TagFragment" }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Performer" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "PerformerFragment" }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Studio" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "StudioFragment" }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Scene" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "SceneFragment" }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "details" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "TagEdit" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "name" } }, + { + kind: "Field", + name: { kind: "Name", value: "description" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_aliases" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_aliases" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "category" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "id" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "name" }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "PerformerEdit" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "name" } }, + { + kind: "Field", + name: { kind: "Name", value: "disambiguation" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_aliases" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_aliases" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "gender" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "birthdate" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "ethnicity" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "country" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "eye_color" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "hair_color" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "height" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "cup_size" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "band_size" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "waist_size" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "hip_size" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "breast_type" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "career_start_year" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "career_end_year" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_tattoos" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "location" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "description" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_tattoos" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "location" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "description" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_piercings" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "location" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "description" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_piercings" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "location" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "description" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "draft_id" }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "StudioEdit" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "name" } }, + { + kind: "Field", + name: { kind: "Name", value: "added_urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "parent" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "StudioFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "SceneEdit" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "title" } }, + { + kind: "Field", + name: { kind: "Name", value: "details" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { kind: "Field", name: { kind: "Name", value: "date" } }, + { + kind: "Field", + name: { kind: "Name", value: "studio" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "StudioFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_performers" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "performer" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "PerformerFragment", + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "as" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_performers" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "performer" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "PerformerFragment", + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "as" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_tags" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "TagFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_tags" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "TagFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_fingerprints" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "FingerprintFragment", + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_fingerprints" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "FingerprintFragment", + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "duration" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "director" }, + }, + { kind: "Field", name: { kind: "Name", value: "code" } }, + { + kind: "Field", + name: { kind: "Name", value: "draft_id" }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "old_details" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "TagEdit" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "name" } }, + { + kind: "Field", + name: { kind: "Name", value: "description" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "category" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "id" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "name" }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "PerformerEdit" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "name" } }, + { + kind: "Field", + name: { kind: "Name", value: "disambiguation" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "gender" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "birthdate" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "ethnicity" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "country" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "eye_color" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "hair_color" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "height" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "cup_size" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "band_size" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "waist_size" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "hip_size" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "breast_type" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "career_start_year" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "career_end_year" }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "StudioEdit" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "name" } }, + { + kind: "Field", + name: { kind: "Name", value: "parent" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "StudioFragment" }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "SceneEdit" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "title" } }, + { + kind: "Field", + name: { kind: "Name", value: "details" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { kind: "Field", name: { kind: "Name", value: "date" } }, + { + kind: "Field", + name: { kind: "Name", value: "studio" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "StudioFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_performers" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "performer" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "PerformerFragment", + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "as" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_performers" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "performer" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "PerformerFragment", + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "as" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_tags" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "TagFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_tags" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "TagFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_fingerprints" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "FingerprintFragment", + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_fingerprints" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "FingerprintFragment", + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "duration" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "director" }, + }, + { kind: "Field", name: { kind: "Name", value: "code" } }, + ], + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "merge_sources" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Tag" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "TagFragment" }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Performer" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "PerformerFragment" }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Studio" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "StudioFragment" }, + }, + ], + }, + }, + { + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Scene" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "SceneFragment" }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "options" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "set_modify_aliases" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "set_merge_aliases" }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "URLFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "URL" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "url" } }, + { + kind: "Field", + name: { kind: "Name", value: "site" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "icon" } }, + ], + }, + }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "ImageFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Image" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "url" } }, + { kind: "Field", name: { kind: "Name", value: "width" } }, + { kind: "Field", name: { kind: "Name", value: "height" } }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "ScenePerformerFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Performer" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "disambiguation" } }, + { kind: "Field", name: { kind: "Name", value: "deleted" } }, + { kind: "Field", name: { kind: "Name", value: "gender" } }, + { kind: "Field", name: { kind: "Name", value: "aliases" } }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "CommentFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "EditComment" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { + kind: "Field", + name: { kind: "Name", value: "user" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + ], + }, + }, + { kind: "Field", name: { kind: "Name", value: "date" } }, + { kind: "Field", name: { kind: "Name", value: "comment" } }, + ], + }, + }, { kind: "FragmentDefinition", name: { kind: "Name", value: "TagFragment" }, @@ -24180,11 +30400,384 @@ export const TagFragmentDoc = { ], }, }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "PerformerFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Performer" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "disambiguation" } }, + { kind: "Field", name: { kind: "Name", value: "deleted" } }, + { kind: "Field", name: { kind: "Name", value: "aliases" } }, + { kind: "Field", name: { kind: "Name", value: "gender" } }, + { kind: "Field", name: { kind: "Name", value: "birth_date" } }, + { kind: "Field", name: { kind: "Name", value: "age" } }, + { kind: "Field", name: { kind: "Name", value: "height" } }, + { kind: "Field", name: { kind: "Name", value: "hair_color" } }, + { kind: "Field", name: { kind: "Name", value: "eye_color" } }, + { kind: "Field", name: { kind: "Name", value: "ethnicity" } }, + { kind: "Field", name: { kind: "Name", value: "country" } }, + { kind: "Field", name: { kind: "Name", value: "career_end_year" } }, + { kind: "Field", name: { kind: "Name", value: "career_start_year" } }, + { kind: "Field", name: { kind: "Name", value: "breast_type" } }, + { kind: "Field", name: { kind: "Name", value: "waist_size" } }, + { kind: "Field", name: { kind: "Name", value: "hip_size" } }, + { kind: "Field", name: { kind: "Name", value: "band_size" } }, + { kind: "Field", name: { kind: "Name", value: "cup_size" } }, + { + kind: "Field", + name: { kind: "Name", value: "tattoos" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "location" } }, + { kind: "Field", name: { kind: "Name", value: "description" } }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "piercings" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "location" } }, + { kind: "Field", name: { kind: "Name", value: "description" } }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { kind: "Field", name: { kind: "Name", value: "is_favorite" } }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "StudioFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Studio" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { + kind: "Field", + name: { kind: "Name", value: "child_studios" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "parent" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "url" } }, + { kind: "Field", name: { kind: "Name", value: "height" } }, + { kind: "Field", name: { kind: "Name", value: "width" } }, + ], + }, + }, + { kind: "Field", name: { kind: "Name", value: "deleted" } }, + { kind: "Field", name: { kind: "Name", value: "is_favorite" } }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "SceneFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Scene" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "release_date" } }, + { kind: "Field", name: { kind: "Name", value: "title" } }, + { kind: "Field", name: { kind: "Name", value: "deleted" } }, + { kind: "Field", name: { kind: "Name", value: "details" } }, + { kind: "Field", name: { kind: "Name", value: "director" } }, + { kind: "Field", name: { kind: "Name", value: "code" } }, + { kind: "Field", name: { kind: "Name", value: "duration" } }, + { + kind: "Field", + name: { kind: "Name", value: "urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "studio" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { + kind: "Field", + name: { kind: "Name", value: "parent" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + ], + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "performers" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "as" } }, + { + kind: "Field", + name: { kind: "Name", value: "performer" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ScenePerformerFragment" }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "fingerprints" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "hash" } }, + { kind: "Field", name: { kind: "Name", value: "algorithm" } }, + { kind: "Field", name: { kind: "Name", value: "duration" } }, + { kind: "Field", name: { kind: "Name", value: "submissions" } }, + { + kind: "Field", + name: { kind: "Name", value: "user_submitted" }, + }, + { kind: "Field", name: { kind: "Name", value: "created" } }, + { kind: "Field", name: { kind: "Name", value: "updated" } }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "tags" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "description" } }, + { kind: "Field", name: { kind: "Name", value: "aliases" } }, + ], + }, + }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "FingerprintFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Fingerprint" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "hash" } }, + { kind: "Field", name: { kind: "Name", value: "algorithm" } }, + { kind: "Field", name: { kind: "Name", value: "duration" } }, + ], + }, + }, ], -} as unknown as DocumentNode; -export const UrlFragmentDoc = { +} as unknown as DocumentNode; +export const NotificationCommentFragmentDoc = { kind: "Document", definitions: [ + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "NotificationCommentFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "EditComment" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "CommentFragment" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "edit" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "EditFragment" }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "CommentFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "EditComment" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { + kind: "Field", + name: { kind: "Name", value: "user" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + ], + }, + }, + { kind: "Field", name: { kind: "Name", value: "date" } }, + { kind: "Field", name: { kind: "Name", value: "comment" } }, + ], + }, + }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "TagFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Tag" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + { kind: "Field", name: { kind: "Name", value: "description" } }, + { kind: "Field", name: { kind: "Name", value: "deleted" } }, + { + kind: "Field", + name: { kind: "Name", value: "category" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "id" } }, + { kind: "Field", name: { kind: "Name", value: "name" } }, + ], + }, + }, + { kind: "Field", name: { kind: "Name", value: "aliases" } }, + ], + }, + }, { kind: "FragmentDefinition", name: { kind: "Name", value: "URLFragment" }, @@ -24211,11 +30804,6 @@ export const UrlFragmentDoc = { ], }, }, - ], -} as unknown as DocumentNode; -export const ImageFragmentDoc = { - kind: "Document", - definitions: [ { kind: "FragmentDefinition", name: { kind: "Name", value: "ImageFragment" }, @@ -24233,11 +30821,6 @@ export const ImageFragmentDoc = { ], }, }, - ], -} as unknown as DocumentNode; -export const PerformerFragmentDoc = { - kind: "Document", - definitions: [ { kind: "FragmentDefinition", name: { kind: "Name", value: "PerformerFragment" }, @@ -24320,54 +30903,6 @@ export const PerformerFragmentDoc = { ], }, }, - { - kind: "FragmentDefinition", - name: { kind: "Name", value: "URLFragment" }, - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "URL" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "url" } }, - { - kind: "Field", - name: { kind: "Name", value: "site" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "icon" } }, - ], - }, - }, - ], - }, - }, - { - kind: "FragmentDefinition", - name: { kind: "Name", value: "ImageFragment" }, - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "Image" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "url" } }, - { kind: "Field", name: { kind: "Name", value: "width" } }, - { kind: "Field", name: { kind: "Name", value: "height" } }, - ], - }, - }, - ], -} as unknown as DocumentNode; -export const StudioFragmentDoc = { - kind: "Document", - definitions: [ { kind: "FragmentDefinition", name: { kind: "Name", value: "StudioFragment" }, @@ -24433,37 +30968,6 @@ export const StudioFragmentDoc = { ], }, }, - { - kind: "FragmentDefinition", - name: { kind: "Name", value: "URLFragment" }, - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "URL" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "url" } }, - { - kind: "Field", - name: { kind: "Name", value: "site" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "icon" } }, - ], - }, - }, - ], - }, - }, - ], -} as unknown as DocumentNode; -export const ScenePerformerFragmentDoc = { - kind: "Document", - definitions: [ { kind: "FragmentDefinition", name: { kind: "Name", value: "ScenePerformerFragment" }, @@ -24483,11 +30987,6 @@ export const ScenePerformerFragmentDoc = { ], }, }, - ], -} as unknown as DocumentNode; -export const SceneFragmentDoc = { - kind: "Document", - definitions: [ { kind: "FragmentDefinition", name: { kind: "Name", value: "SceneFragment" }, @@ -24612,73 +31111,6 @@ export const SceneFragmentDoc = { ], }, }, - { - kind: "FragmentDefinition", - name: { kind: "Name", value: "URLFragment" }, - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "URL" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "url" } }, - { - kind: "Field", - name: { kind: "Name", value: "site" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "icon" } }, - ], - }, - }, - ], - }, - }, - { - kind: "FragmentDefinition", - name: { kind: "Name", value: "ImageFragment" }, - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "Image" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "url" } }, - { kind: "Field", name: { kind: "Name", value: "width" } }, - { kind: "Field", name: { kind: "Name", value: "height" } }, - ], - }, - }, - { - kind: "FragmentDefinition", - name: { kind: "Name", value: "ScenePerformerFragment" }, - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "Performer" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "disambiguation" } }, - { kind: "Field", name: { kind: "Name", value: "deleted" } }, - { kind: "Field", name: { kind: "Name", value: "gender" } }, - { kind: "Field", name: { kind: "Name", value: "aliases" } }, - ], - }, - }, - ], -} as unknown as DocumentNode; -export const FingerprintFragmentDoc = { - kind: "Document", - definitions: [ { kind: "FragmentDefinition", name: { kind: "Name", value: "FingerprintFragment" }, @@ -24695,11 +31127,6 @@ export const FingerprintFragmentDoc = { ], }, }, - ], -} as unknown as DocumentNode; -export const EditFragmentDoc = { - kind: "Document", - definitions: [ { kind: "FragmentDefinition", name: { kind: "Name", value: "EditFragment" }, @@ -25141,384 +31568,30 @@ export const EditFragmentDoc = { name: { kind: "Name", value: "StudioFragment" }, }, ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "added_images" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "FragmentSpread", - name: { kind: "Name", value: "ImageFragment" }, - }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "removed_images" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "FragmentSpread", - name: { kind: "Name", value: "ImageFragment" }, - }, - ], - }, - }, - ], - }, - }, - { - kind: "InlineFragment", - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "SceneEdit" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "title" } }, - { - kind: "Field", - name: { kind: "Name", value: "details" }, - }, - { - kind: "Field", - name: { kind: "Name", value: "added_urls" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "FragmentSpread", - name: { kind: "Name", value: "URLFragment" }, - }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "removed_urls" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "FragmentSpread", - name: { kind: "Name", value: "URLFragment" }, - }, - ], - }, - }, - { kind: "Field", name: { kind: "Name", value: "date" } }, - { - kind: "Field", - name: { kind: "Name", value: "studio" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "FragmentSpread", - name: { kind: "Name", value: "StudioFragment" }, - }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "added_performers" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "Field", - name: { kind: "Name", value: "performer" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "FragmentSpread", - name: { - kind: "Name", - value: "PerformerFragment", - }, - }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "as" }, - }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "removed_performers" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "Field", - name: { kind: "Name", value: "performer" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "FragmentSpread", - name: { - kind: "Name", - value: "PerformerFragment", - }, - }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "as" }, - }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "added_tags" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "FragmentSpread", - name: { kind: "Name", value: "TagFragment" }, - }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "removed_tags" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "FragmentSpread", - name: { kind: "Name", value: "TagFragment" }, - }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "added_images" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "FragmentSpread", - name: { kind: "Name", value: "ImageFragment" }, - }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "removed_images" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "FragmentSpread", - name: { kind: "Name", value: "ImageFragment" }, - }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "added_fingerprints" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "FragmentSpread", - name: { - kind: "Name", - value: "FingerprintFragment", - }, - }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "removed_fingerprints" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "FragmentSpread", - name: { - kind: "Name", - value: "FingerprintFragment", - }, - }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "duration" }, - }, - { - kind: "Field", - name: { kind: "Name", value: "director" }, - }, - { kind: "Field", name: { kind: "Name", value: "code" } }, - { - kind: "Field", - name: { kind: "Name", value: "draft_id" }, - }, - ], - }, - }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "old_details" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "InlineFragment", - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "TagEdit" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "name" } }, - { - kind: "Field", - name: { kind: "Name", value: "description" }, - }, - { - kind: "Field", - name: { kind: "Name", value: "category" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "Field", - name: { kind: "Name", value: "id" }, - }, - { - kind: "Field", - name: { kind: "Name", value: "name" }, - }, - ], - }, - }, - ], - }, - }, - { - kind: "InlineFragment", - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "PerformerEdit" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "name" } }, - { - kind: "Field", - name: { kind: "Name", value: "disambiguation" }, - }, - { - kind: "Field", - name: { kind: "Name", value: "gender" }, - }, - { - kind: "Field", - name: { kind: "Name", value: "birthdate" }, - }, - { - kind: "Field", - name: { kind: "Name", value: "ethnicity" }, - }, - { - kind: "Field", - name: { kind: "Name", value: "country" }, - }, - { - kind: "Field", - name: { kind: "Name", value: "eye_color" }, - }, - { - kind: "Field", - name: { kind: "Name", value: "hair_color" }, - }, - { - kind: "Field", - name: { kind: "Name", value: "height" }, - }, - { - kind: "Field", - name: { kind: "Name", value: "cup_size" }, - }, - { - kind: "Field", - name: { kind: "Name", value: "band_size" }, - }, - { - kind: "Field", - name: { kind: "Name", value: "waist_size" }, - }, - { - kind: "Field", - name: { kind: "Name", value: "hip_size" }, - }, - { - kind: "Field", - name: { kind: "Name", value: "breast_type" }, - }, - { - kind: "Field", - name: { kind: "Name", value: "career_start_year" }, + }, }, { kind: "Field", - name: { kind: "Name", value: "career_end_year" }, + name: { kind: "Name", value: "added_images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, }, - ], - }, - }, - { - kind: "InlineFragment", - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "StudioEdit" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "name" } }, { kind: "Field", - name: { kind: "Name", value: "parent" }, + name: { kind: "Name", value: "removed_images" }, selectionSet: { kind: "SelectionSet", selections: [ { kind: "FragmentSpread", - name: { kind: "Name", value: "StudioFragment" }, + name: { kind: "Name", value: "ImageFragment" }, }, ], }, @@ -25731,6 +31804,10 @@ export const EditFragmentDoc = { name: { kind: "Name", value: "director" }, }, { kind: "Field", name: { kind: "Name", value: "code" } }, + { + kind: "Field", + name: { kind: "Name", value: "draft_id" }, + }, ], }, }, @@ -25739,7 +31816,7 @@ export const EditFragmentDoc = { }, { kind: "Field", - name: { kind: "Name", value: "merge_sources" }, + name: { kind: "Name", value: "old_details" }, selectionSet: { kind: "SelectionSet", selections: [ @@ -25747,14 +31824,32 @@ export const EditFragmentDoc = { kind: "InlineFragment", typeCondition: { kind: "NamedType", - name: { kind: "Name", value: "Tag" }, + name: { kind: "Name", value: "TagEdit" }, }, selectionSet: { kind: "SelectionSet", selections: [ + { kind: "Field", name: { kind: "Name", value: "name" } }, { - kind: "FragmentSpread", - name: { kind: "Name", value: "TagFragment" }, + kind: "Field", + name: { kind: "Name", value: "description" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "category" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "id" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "name" }, + }, + ], + }, }, ], }, @@ -25763,14 +31858,71 @@ export const EditFragmentDoc = { kind: "InlineFragment", typeCondition: { kind: "NamedType", - name: { kind: "Name", value: "Performer" }, + name: { kind: "Name", value: "PerformerEdit" }, }, selectionSet: { kind: "SelectionSet", selections: [ + { kind: "Field", name: { kind: "Name", value: "name" } }, { - kind: "FragmentSpread", - name: { kind: "Name", value: "PerformerFragment" }, + kind: "Field", + name: { kind: "Name", value: "disambiguation" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "gender" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "birthdate" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "ethnicity" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "country" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "eye_color" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "hair_color" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "height" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "cup_size" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "band_size" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "waist_size" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "hip_size" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "breast_type" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "career_start_year" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "career_end_year" }, }, ], }, @@ -25779,14 +31931,24 @@ export const EditFragmentDoc = { kind: "InlineFragment", typeCondition: { kind: "NamedType", - name: { kind: "Name", value: "Studio" }, + name: { kind: "Name", value: "StudioEdit" }, }, selectionSet: { kind: "SelectionSet", selections: [ + { kind: "Field", name: { kind: "Name", value: "name" } }, { - kind: "FragmentSpread", - name: { kind: "Name", value: "StudioFragment" }, + kind: "Field", + name: { kind: "Name", value: "parent" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "StudioFragment" }, + }, + ], + }, }, ], }, @@ -25795,518 +31957,279 @@ export const EditFragmentDoc = { kind: "InlineFragment", typeCondition: { kind: "NamedType", - name: { kind: "Name", value: "Scene" }, + name: { kind: "Name", value: "SceneEdit" }, }, selectionSet: { kind: "SelectionSet", selections: [ + { kind: "Field", name: { kind: "Name", value: "title" } }, { - kind: "FragmentSpread", - name: { kind: "Name", value: "SceneFragment" }, + kind: "Field", + name: { kind: "Name", value: "details" }, }, - ], - }, - }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "options" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "Field", - name: { kind: "Name", value: "set_modify_aliases" }, - }, - { - kind: "Field", - name: { kind: "Name", value: "set_merge_aliases" }, - }, - ], - }, - }, - ], - }, - }, - { - kind: "FragmentDefinition", - name: { kind: "Name", value: "URLFragment" }, - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "URL" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "url" } }, - { - kind: "Field", - name: { kind: "Name", value: "site" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "icon" } }, - ], - }, - }, - ], - }, - }, - { - kind: "FragmentDefinition", - name: { kind: "Name", value: "ImageFragment" }, - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "Image" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "url" } }, - { kind: "Field", name: { kind: "Name", value: "width" } }, - { kind: "Field", name: { kind: "Name", value: "height" } }, - ], - }, - }, - { - kind: "FragmentDefinition", - name: { kind: "Name", value: "ScenePerformerFragment" }, - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "Performer" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "disambiguation" } }, - { kind: "Field", name: { kind: "Name", value: "deleted" } }, - { kind: "Field", name: { kind: "Name", value: "gender" } }, - { kind: "Field", name: { kind: "Name", value: "aliases" } }, - ], - }, - }, - { - kind: "FragmentDefinition", - name: { kind: "Name", value: "CommentFragment" }, - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "EditComment" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { - kind: "Field", - name: { kind: "Name", value: "user" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - ], - }, - }, - { kind: "Field", name: { kind: "Name", value: "date" } }, - { kind: "Field", name: { kind: "Name", value: "comment" } }, - ], - }, - }, - { - kind: "FragmentDefinition", - name: { kind: "Name", value: "TagFragment" }, - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "Tag" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "description" } }, - { kind: "Field", name: { kind: "Name", value: "deleted" } }, - { - kind: "Field", - name: { kind: "Name", value: "category" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - ], - }, - }, - { kind: "Field", name: { kind: "Name", value: "aliases" } }, - ], - }, - }, - { - kind: "FragmentDefinition", - name: { kind: "Name", value: "PerformerFragment" }, - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "Performer" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "disambiguation" } }, - { kind: "Field", name: { kind: "Name", value: "deleted" } }, - { kind: "Field", name: { kind: "Name", value: "aliases" } }, - { kind: "Field", name: { kind: "Name", value: "gender" } }, - { kind: "Field", name: { kind: "Name", value: "birth_date" } }, - { kind: "Field", name: { kind: "Name", value: "age" } }, - { kind: "Field", name: { kind: "Name", value: "height" } }, - { kind: "Field", name: { kind: "Name", value: "hair_color" } }, - { kind: "Field", name: { kind: "Name", value: "eye_color" } }, - { kind: "Field", name: { kind: "Name", value: "ethnicity" } }, - { kind: "Field", name: { kind: "Name", value: "country" } }, - { kind: "Field", name: { kind: "Name", value: "career_end_year" } }, - { kind: "Field", name: { kind: "Name", value: "career_start_year" } }, - { kind: "Field", name: { kind: "Name", value: "breast_type" } }, - { kind: "Field", name: { kind: "Name", value: "waist_size" } }, - { kind: "Field", name: { kind: "Name", value: "hip_size" } }, - { kind: "Field", name: { kind: "Name", value: "band_size" } }, - { kind: "Field", name: { kind: "Name", value: "cup_size" } }, - { - kind: "Field", - name: { kind: "Name", value: "tattoos" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "location" } }, - { kind: "Field", name: { kind: "Name", value: "description" } }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "piercings" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "location" } }, - { kind: "Field", name: { kind: "Name", value: "description" } }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "urls" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "FragmentSpread", - name: { kind: "Name", value: "URLFragment" }, - }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "images" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "FragmentSpread", - name: { kind: "Name", value: "ImageFragment" }, - }, - ], - }, - }, - { kind: "Field", name: { kind: "Name", value: "is_favorite" } }, - ], - }, - }, - { - kind: "FragmentDefinition", - name: { kind: "Name", value: "StudioFragment" }, - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "Studio" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { - kind: "Field", - name: { kind: "Name", value: "child_studios" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "parent" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "urls" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "FragmentSpread", - name: { kind: "Name", value: "URLFragment" }, - }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "images" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "url" } }, - { kind: "Field", name: { kind: "Name", value: "height" } }, - { kind: "Field", name: { kind: "Name", value: "width" } }, - ], - }, - }, - { kind: "Field", name: { kind: "Name", value: "deleted" } }, - { kind: "Field", name: { kind: "Name", value: "is_favorite" } }, - ], - }, - }, - { - kind: "FragmentDefinition", - name: { kind: "Name", value: "SceneFragment" }, - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "Scene" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "release_date" } }, - { kind: "Field", name: { kind: "Name", value: "title" } }, - { kind: "Field", name: { kind: "Name", value: "deleted" } }, - { kind: "Field", name: { kind: "Name", value: "details" } }, - { kind: "Field", name: { kind: "Name", value: "director" } }, - { kind: "Field", name: { kind: "Name", value: "code" } }, - { kind: "Field", name: { kind: "Name", value: "duration" } }, - { - kind: "Field", - name: { kind: "Name", value: "urls" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "FragmentSpread", - name: { kind: "Name", value: "URLFragment" }, - }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "images" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "FragmentSpread", - name: { kind: "Name", value: "ImageFragment" }, + { + kind: "Field", + name: { kind: "Name", value: "added_urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_urls" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "URLFragment" }, + }, + ], + }, + }, + { kind: "Field", name: { kind: "Name", value: "date" } }, + { + kind: "Field", + name: { kind: "Name", value: "studio" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "StudioFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_performers" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "performer" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "PerformerFragment", + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "as" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_performers" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "performer" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "PerformerFragment", + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "as" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_tags" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "TagFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_tags" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "TagFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_images" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "ImageFragment" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "added_fingerprints" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "FingerprintFragment", + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "removed_fingerprints" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { + kind: "Name", + value: "FingerprintFragment", + }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "duration" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "director" }, + }, + { kind: "Field", name: { kind: "Name", value: "code" } }, + ], + }, }, ], }, }, { kind: "Field", - name: { kind: "Name", value: "studio" }, + name: { kind: "Name", value: "merge_sources" }, selectionSet: { kind: "SelectionSet", selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, { - kind: "Field", - name: { kind: "Name", value: "parent" }, + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Tag" }, + }, selectionSet: { kind: "SelectionSet", selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, + { + kind: "FragmentSpread", + name: { kind: "Name", value: "TagFragment" }, + }, ], }, }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "performers" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "as" } }, { - kind: "Field", - name: { kind: "Name", value: "performer" }, + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Performer" }, + }, selectionSet: { kind: "SelectionSet", selections: [ { kind: "FragmentSpread", - name: { kind: "Name", value: "ScenePerformerFragment" }, + name: { kind: "Name", value: "PerformerFragment" }, }, ], }, }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "fingerprints" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "hash" } }, - { kind: "Field", name: { kind: "Name", value: "algorithm" } }, - { kind: "Field", name: { kind: "Name", value: "duration" } }, - { kind: "Field", name: { kind: "Name", value: "submissions" } }, - { - kind: "Field", - name: { kind: "Name", value: "user_submitted" }, - }, - { kind: "Field", name: { kind: "Name", value: "created" } }, - { kind: "Field", name: { kind: "Name", value: "updated" } }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "tags" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "description" } }, - { kind: "Field", name: { kind: "Name", value: "aliases" } }, - ], - }, - }, - ], - }, - }, - { - kind: "FragmentDefinition", - name: { kind: "Name", value: "FingerprintFragment" }, - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "Fingerprint" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "hash" } }, - { kind: "Field", name: { kind: "Name", value: "algorithm" } }, - { kind: "Field", name: { kind: "Name", value: "duration" } }, - ], - }, - }, - ], -} as unknown as DocumentNode; -export const QuerySceneFragmentDoc = { - kind: "Document", - definitions: [ - { - kind: "FragmentDefinition", - name: { kind: "Name", value: "QuerySceneFragment" }, - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "Scene" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "release_date" } }, - { kind: "Field", name: { kind: "Name", value: "title" } }, - { kind: "Field", name: { kind: "Name", value: "duration" } }, - { - kind: "Field", - name: { kind: "Name", value: "urls" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { - kind: "FragmentSpread", - name: { kind: "Name", value: "URLFragment" }, - }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "images" }, - selectionSet: { - kind: "SelectionSet", - selections: [ { - kind: "FragmentSpread", - name: { kind: "Name", value: "ImageFragment" }, + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Studio" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "StudioFragment" }, + }, + ], + }, }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "studio" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "performers" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "as" } }, { - kind: "Field", - name: { kind: "Name", value: "performer" }, + kind: "InlineFragment", + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "Scene" }, + }, selectionSet: { kind: "SelectionSet", selections: [ { kind: "FragmentSpread", - name: { kind: "Name", value: "ScenePerformerFragment" }, + name: { kind: "Name", value: "SceneFragment" }, }, ], }, @@ -26314,172 +32237,28 @@ export const QuerySceneFragmentDoc = { ], }, }, - ], - }, - }, - { - kind: "FragmentDefinition", - name: { kind: "Name", value: "URLFragment" }, - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "URL" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "url" } }, - { - kind: "Field", - name: { kind: "Name", value: "site" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "icon" } }, - ], - }, - }, - ], - }, - }, - { - kind: "FragmentDefinition", - name: { kind: "Name", value: "ImageFragment" }, - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "Image" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "url" } }, - { kind: "Field", name: { kind: "Name", value: "width" } }, - { kind: "Field", name: { kind: "Name", value: "height" } }, - ], - }, - }, - { - kind: "FragmentDefinition", - name: { kind: "Name", value: "ScenePerformerFragment" }, - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "Performer" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "disambiguation" } }, - { kind: "Field", name: { kind: "Name", value: "deleted" } }, - { kind: "Field", name: { kind: "Name", value: "gender" } }, - { kind: "Field", name: { kind: "Name", value: "aliases" } }, - ], - }, - }, - ], -} as unknown as DocumentNode; -export const SearchPerformerFragmentDoc = { - kind: "Document", - definitions: [ - { - kind: "FragmentDefinition", - name: { kind: "Name", value: "SearchPerformerFragment" }, - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "Performer" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "disambiguation" } }, - { kind: "Field", name: { kind: "Name", value: "deleted" } }, - { kind: "Field", name: { kind: "Name", value: "gender" } }, - { kind: "Field", name: { kind: "Name", value: "aliases" } }, - { kind: "Field", name: { kind: "Name", value: "country" } }, - { kind: "Field", name: { kind: "Name", value: "career_start_year" } }, - { kind: "Field", name: { kind: "Name", value: "career_end_year" } }, - { kind: "Field", name: { kind: "Name", value: "scene_count" } }, - { kind: "Field", name: { kind: "Name", value: "birth_date" } }, { kind: "Field", - name: { kind: "Name", value: "urls" }, + name: { kind: "Name", value: "options" }, selectionSet: { kind: "SelectionSet", selections: [ { - kind: "FragmentSpread", - name: { kind: "Name", value: "URLFragment" }, + kind: "Field", + name: { kind: "Name", value: "set_modify_aliases" }, }, - ], - }, - }, - { - kind: "Field", - name: { kind: "Name", value: "images" }, - selectionSet: { - kind: "SelectionSet", - selections: [ { - kind: "FragmentSpread", - name: { kind: "Name", value: "ImageFragment" }, + kind: "Field", + name: { kind: "Name", value: "set_merge_aliases" }, }, ], }, }, - { kind: "Field", name: { kind: "Name", value: "is_favorite" } }, - ], - }, - }, - { - kind: "FragmentDefinition", - name: { kind: "Name", value: "URLFragment" }, - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "URL" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "url" } }, - { - kind: "Field", - name: { kind: "Name", value: "site" }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "name" } }, - { kind: "Field", name: { kind: "Name", value: "icon" } }, - ], - }, - }, - ], - }, - }, - { - kind: "FragmentDefinition", - name: { kind: "Name", value: "ImageFragment" }, - typeCondition: { - kind: "NamedType", - name: { kind: "Name", value: "Image" }, - }, - selectionSet: { - kind: "SelectionSet", - selections: [ - { kind: "Field", name: { kind: "Name", value: "id" } }, - { kind: "Field", name: { kind: "Name", value: "url" } }, - { kind: "Field", name: { kind: "Name", value: "width" } }, - { kind: "Field", name: { kind: "Name", value: "height" } }, ], }, }, ], -} as unknown as DocumentNode; +} as unknown as DocumentNode; export const ActivateNewUserDocument = { kind: "Document", definitions: [ @@ -52326,7 +58105,8 @@ export const NotificationsDocument = { kind: "FragmentSpread", name: { kind: "Name", - value: "CommentFragment", + value: + "NotificationCommentFragment", }, }, ], @@ -52357,7 +58137,8 @@ export const NotificationsDocument = { kind: "FragmentSpread", name: { kind: "Name", - value: "CommentFragment", + value: + "NotificationCommentFragment", }, }, ], @@ -52388,7 +58169,8 @@ export const NotificationsDocument = { kind: "FragmentSpread", name: { kind: "Name", - value: "CommentFragment", + value: + "NotificationCommentFragment", }, }, ], @@ -54031,6 +59813,36 @@ export const NotificationsDocument = { ], }, }, + { + kind: "FragmentDefinition", + name: { kind: "Name", value: "NotificationCommentFragment" }, + typeCondition: { + kind: "NamedType", + name: { kind: "Name", value: "EditComment" }, + }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "CommentFragment" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "edit" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "FragmentSpread", + name: { kind: "Name", value: "EditFragment" }, + }, + ], + }, + }, + ], + }, + }, ], } as unknown as DocumentNode; export const SceneDocument = { diff --git a/frontend/src/pages/notifications/Notification.tsx b/frontend/src/pages/notifications/Notification.tsx new file mode 100644 index 000000000..1e55a2143 --- /dev/null +++ b/frontend/src/pages/notifications/Notification.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { NotificationsQuery } from 'src/graphql'; +import SceneCard from 'src/components/sceneCard'; +import EditCard from 'src/components/editCard'; + +type NotificationType = NotificationsQuery["queryNotifications"]["notifications"][number]; + +interface Props { + notification: NotificationType; +} + +const headers = { + 'FavoritePerformerScene': 'New scene involving a favorite performer', + 'FavoriteStudioScene': 'New scene from a favorite studio', + 'FavoritePerformerEdit': 'New edit involving a favorite performer', + 'FavoriteStudioEdit': 'New edit involving a favorite studio', + 'DownvoteOwnEdit': 'Your edit was downvoted', + 'FailedOwnEdit': 'Your edit failed', + 'UpdatedEdit': 'And edit you voted on was updated', + 'CommentOwnEdit': 'A user commented on your edit', + 'CommentCommentedEdit': 'A user commented on an edit you\'ve commented on', +} + +const renderNotificationBody = (notification: NotificationType) => { + switch (notification.data.__typename) { + case 'FavoritePerformerScene': + case 'FavoriteStudioScene': + return ( + <> +

{ headers[notification.data.__typename] }

+ + + ); + case 'FavoritePerformerEdit': + case 'FavoriteStudioEdit': + case 'DownvoteOwnEdit': + case 'FailedOwnEdit': + case 'UpdatedEdit': + return ; + case 'CommentOwnEdit': + case 'CommentCommentedEdit': + case 'CommentVotedEdit': + return 'comment'; + } +} + +export const Notification: React.FC = ({ notification }) => { + return
{ renderNotificationBody(notification) }
+} diff --git a/frontend/src/pages/notifications/Notifications.tsx b/frontend/src/pages/notifications/Notifications.tsx index d8fe2f0ef..f72c6318a 100644 --- a/frontend/src/pages/notifications/Notifications.tsx +++ b/frontend/src/pages/notifications/Notifications.tsx @@ -3,6 +3,7 @@ import { useNotifications } from "src/graphql"; import { usePagination } from "src/hooks"; import { ErrorMessage } from "src/components/fragments"; import { List } from "src/components/list"; +import { Notification } from "./Notification"; const PER_PAGE = 20; @@ -19,13 +20,11 @@ const Notifications: FC = () => { page={page} setPage={setPage} perPage={PER_PAGE} - listCount={data?.queryNotifications?.length ?? 0} + listCount={data?.queryNotifications.count} loading={loading} entityName="notifications" > - {data?.queryNotifications?.map(n => ( -
{ n.data.__typename }
- ))} + {data?.queryNotifications?.notifications?.map(n => )} ); }; diff --git a/graphql/schema/types/edit.graphql b/graphql/schema/types/edit.graphql index 9928e9a12..4e7ca7a27 100644 --- a/graphql/schema/types/edit.graphql +++ b/graphql/schema/types/edit.graphql @@ -36,6 +36,7 @@ type EditComment { user: User date: Time! comment: String! + edit: Edit! } union EditDetails = PerformerEdit | SceneEdit | StudioEdit | TagEdit diff --git a/pkg/api/resolver_model_edit_comment.go b/pkg/api/resolver_model_edit_comment.go index 9269ddb88..75580b195 100644 --- a/pkg/api/resolver_model_edit_comment.go +++ b/pkg/api/resolver_model_edit_comment.go @@ -37,3 +37,10 @@ func (r *editCommentResolver) User(ctx context.Context, obj *models.EditComment) return user, nil } + +func (r *editCommentResolver) Edit(ctx context.Context, obj *models.EditComment) (*models.Edit, error) { + fac := r.getRepoFactory(ctx) + qb := fac.Edit() + + return qb.Find(obj.EditID) +} diff --git a/pkg/models/generated_exec.go b/pkg/models/generated_exec.go index 5ebd53181..34a822c1f 100644 --- a/pkg/models/generated_exec.go +++ b/pkg/models/generated_exec.go @@ -141,6 +141,7 @@ type ComplexityRoot struct { EditComment struct { Comment func(childComplexity int) int Date func(childComplexity int) int + Edit func(childComplexity int) int ID func(childComplexity int) int User func(childComplexity int) int } @@ -670,6 +671,7 @@ type EditCommentResolver interface { User(ctx context.Context, obj *EditComment) (*User, error) Date(ctx context.Context, obj *EditComment) (*time.Time, error) Comment(ctx context.Context, obj *EditComment) (string, error) + Edit(ctx context.Context, obj *EditComment) (*Edit, error) } type EditVoteResolver interface { User(ctx context.Context, obj *EditVote) (*User, error) @@ -1232,6 +1234,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.EditComment.Date(childComplexity), true + case "EditComment.edit": + if e.complexity.EditComment.Edit == nil { + break + } + + return e.complexity.EditComment.Edit(childComplexity), true + case "EditComment.id": if e.complexity.EditComment.ID == nil { break @@ -4353,6 +4362,7 @@ type EditComment { user: User date: Time! comment: String! + edit: Edit! } union EditDetails = PerformerEdit | SceneEdit | StudioEdit | TagEdit @@ -7358,6 +7368,8 @@ func (ec *executionContext) fieldContext_CommentCommentedEdit_comment(ctx contex return ec.fieldContext_EditComment_date(ctx, field) case "comment": return ec.fieldContext_EditComment_comment(ctx, field) + case "edit": + return ec.fieldContext_EditComment_edit(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type EditComment", field.Name) }, @@ -7412,6 +7424,8 @@ func (ec *executionContext) fieldContext_CommentOwnEdit_comment(ctx context.Cont return ec.fieldContext_EditComment_date(ctx, field) case "comment": return ec.fieldContext_EditComment_comment(ctx, field) + case "edit": + return ec.fieldContext_EditComment_edit(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type EditComment", field.Name) }, @@ -7466,6 +7480,8 @@ func (ec *executionContext) fieldContext_CommentVotedEdit_comment(ctx context.Co return ec.fieldContext_EditComment_date(ctx, field) case "comment": return ec.fieldContext_EditComment_comment(ctx, field) + case "edit": + return ec.fieldContext_EditComment_edit(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type EditComment", field.Name) }, @@ -8495,6 +8511,8 @@ func (ec *executionContext) fieldContext_Edit_comments(ctx context.Context, fiel return ec.fieldContext_EditComment_date(ctx, field) case "comment": return ec.fieldContext_EditComment_comment(ctx, field) + case "edit": + return ec.fieldContext_EditComment_edit(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type EditComment", field.Name) }, @@ -9094,6 +9112,92 @@ func (ec *executionContext) fieldContext_EditComment_comment(ctx context.Context return fc, nil } +func (ec *executionContext) _EditComment_edit(ctx context.Context, field graphql.CollectedField, obj *EditComment) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_EditComment_edit(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.EditComment().Edit(rctx, obj) + }) + 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.(*Edit) + fc.Result = res + return ec.marshalNEdit2ᚖgithubᚗcomᚋstashappᚋstashᚑboxᚋpkgᚋmodelsᚐEdit(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_EditComment_edit(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "EditComment", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Edit_id(ctx, field) + case "user": + return ec.fieldContext_Edit_user(ctx, field) + case "target": + return ec.fieldContext_Edit_target(ctx, field) + case "target_type": + return ec.fieldContext_Edit_target_type(ctx, field) + case "merge_sources": + return ec.fieldContext_Edit_merge_sources(ctx, field) + case "operation": + return ec.fieldContext_Edit_operation(ctx, field) + case "bot": + return ec.fieldContext_Edit_bot(ctx, field) + case "details": + return ec.fieldContext_Edit_details(ctx, field) + case "old_details": + return ec.fieldContext_Edit_old_details(ctx, field) + case "options": + return ec.fieldContext_Edit_options(ctx, field) + case "comments": + return ec.fieldContext_Edit_comments(ctx, field) + case "votes": + return ec.fieldContext_Edit_votes(ctx, field) + case "vote_count": + return ec.fieldContext_Edit_vote_count(ctx, field) + case "destructive": + return ec.fieldContext_Edit_destructive(ctx, field) + case "status": + return ec.fieldContext_Edit_status(ctx, field) + case "applied": + return ec.fieldContext_Edit_applied(ctx, field) + case "created": + return ec.fieldContext_Edit_created(ctx, field) + case "updated": + return ec.fieldContext_Edit_updated(ctx, field) + case "closed": + return ec.fieldContext_Edit_closed(ctx, field) + case "expires": + return ec.fieldContext_Edit_expires(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Edit", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _EditVote_user(ctx context.Context, field graphql.CollectedField, obj *EditVote) (ret graphql.Marshaler) { fc, err := ec.fieldContext_EditVote_user(ctx, field) if err != nil { @@ -37710,6 +37814,26 @@ func (ec *executionContext) _EditComment(ctx context.Context, sel ast.SelectionS return res } + out.Concurrently(i, func() graphql.Marshaler { + return innerFunc(ctx) + + }) + case "edit": + field := field + + innerFunc := func(ctx context.Context) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._EditComment_edit(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + return res + } + out.Concurrently(i, func() graphql.Marshaler { return innerFunc(ctx) From 03c63f0f08d2ca22468b73efd0241995b6021912 Mon Sep 17 00:00:00 2001 From: InfiniteStash <117855276+InfiniteStash@users.noreply.github.com> Date: Fri, 1 Mar 2024 19:51:18 +0000 Subject: [PATCH 7/8] Delete old migration number --- .../postgres/31_notifications.up.sql | 31 ------------------- 1 file changed, 31 deletions(-) delete mode 100644 pkg/database/migrations/postgres/31_notifications.up.sql diff --git a/pkg/database/migrations/postgres/31_notifications.up.sql b/pkg/database/migrations/postgres/31_notifications.up.sql deleted file mode 100644 index e149a0634..000000000 --- a/pkg/database/migrations/postgres/31_notifications.up.sql +++ /dev/null @@ -1,31 +0,0 @@ -CREATE TYPE notification_type AS ENUM ( - 'FAVORITE_PERFORMER_SCENE', - 'FAVORITE_PERFORMER_EDIT', - 'FAVORITE_STUDIO_SCENE', - 'FAVORITE_STUDIO_EDIT', - 'COMMENT_OWN_EDIT', - 'DOWNVOTE_OWN_EDIT', - 'FAILED_OWN_EDIT', - 'COMMENT_COMMENTED_EDIT', - 'COMMENT_VOTED_EDIT', - 'UPDATED_EDIT' -); - -CREATE TABLE notifications ( - user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, - type notification_type NOT NULL, - id UUID NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT NOW(), - read_at TIMESTAMP -); -CREATE INDEX notifications_user_read_idx ON notifications (user_id, read_at); - -CREATE TABLE user_notifications ( - user_id UUID REFERENCES users(id) ON DELETE CASCADE NOT NULL, - type notification_type NOT NULL -); -CREATE INDEX user_notifications_user_id_idx ON user_notifications (user_id); -CREATE INDEX user_notifications_type_idx ON user_notifications (type); - -INSERT INTO user_notifications -SELECT id, type FROM unnest(enum_range(NULL::notification_type)) AS type CROSS JOIN users; From 59e46db9bdb3d53b1220d853d26507d74486bbea Mon Sep 17 00:00:00 2001 From: InfiniteStash <117855276+InfiniteStash@users.noreply.github.com> Date: Sun, 3 Mar 2024 16:49:52 +0000 Subject: [PATCH 8/8] Lint --- pkg/api/resolver_model_notification.go | 25 ++++++++++++---------- pkg/dataloader/loaders.go | 12 +++++------ pkg/manager/notifications/notifications.go | 8 ++++--- pkg/sqlx/querybuilder_notifications.go | 8 ------- 4 files changed, 25 insertions(+), 28 deletions(-) diff --git a/pkg/api/resolver_model_notification.go b/pkg/api/resolver_model_notification.go index c9694d665..0fef143bd 100644 --- a/pkg/api/resolver_model_notification.go +++ b/pkg/api/resolver_model_notification.go @@ -25,23 +25,24 @@ func (r *notificationResolver) Data(ctx context.Context, obj *models.Notificatio case models.NotificationEnumCommentOwnEdit: fallthrough case models.NotificationEnumCommentVotedEdit: - comment, err := dataloader.For(ctx).EditCommentById.Load(obj.TargetID) + comment, err := dataloader.For(ctx).EditCommentByID.Load(obj.TargetID) if err != nil { return nil, err } - if obj.Type == models.NotificationEnumCommentCommentedEdit { + switch obj.Type { + case models.NotificationEnumCommentCommentedEdit: return &models.CommentCommentedEdit{Comment: comment}, nil - } else if obj.Type == models.NotificationEnumCommentOwnEdit { + case models.NotificationEnumCommentOwnEdit: return &models.CommentOwnEdit{Comment: comment}, nil - } else { + default: return &models.CommentVotedEdit{Comment: comment}, nil } case models.NotificationEnumFavoritePerformerScene: fallthrough case models.NotificationEnumFavoriteStudioScene: - scene, err := dataloader.For(ctx).SceneById.Load(obj.TargetID) + scene, err := dataloader.For(ctx).SceneByID.Load(obj.TargetID) if err != nil { return nil, err } @@ -61,20 +62,22 @@ func (r *notificationResolver) Data(ctx context.Context, obj *models.Notificatio case models.NotificationEnumFailedOwnEdit: fallthrough case models.NotificationEnumUpdatedEdit: - edit, err := dataloader.For(ctx).EditById.Load(obj.TargetID) + edit, err := dataloader.For(ctx).EditByID.Load(obj.TargetID) if err != nil { return nil, err } - if obj.Type == models.NotificationEnumFavoritePerformerEdit { + // nolint:exhaustive + switch obj.Type { + case models.NotificationEnumFavoritePerformerEdit: return &models.FavoritePerformerEdit{Edit: edit}, nil - } else if obj.Type == models.NotificationEnumFavoriteStudioEdit { + case models.NotificationEnumFavoriteStudioEdit: return &models.FavoriteStudioEdit{Edit: edit}, nil - } else if obj.Type == models.NotificationEnumDownvoteOwnEdit { + case models.NotificationEnumDownvoteOwnEdit: return &models.DownvoteOwnEdit{Edit: edit}, nil - } else if obj.Type == models.NotificationEnumFailedOwnEdit { + case models.NotificationEnumFailedOwnEdit: return &models.FailedOwnEdit{Edit: edit}, nil - } else if obj.Type == models.NotificationEnumUpdatedEdit { + case models.NotificationEnumUpdatedEdit: return &models.UpdatedEdit{Edit: edit}, nil } } diff --git a/pkg/dataloader/loaders.go b/pkg/dataloader/loaders.go index 091ae8c69..8bbca6946 100644 --- a/pkg/dataloader/loaders.go +++ b/pkg/dataloader/loaders.go @@ -28,7 +28,7 @@ type Loaders struct { PerformerTattoosByID BodyModificationsLoader PerformerUrlsByID URLLoader PerformerIsFavoriteByID BoolsLoader - SceneById SceneLoader + SceneByID SceneLoader SceneImageIDsByID UUIDsLoader SceneAppearancesByID SceneAppearancesLoader SceneUrlsByID URLLoader @@ -40,8 +40,8 @@ type Loaders struct { StudioByID StudioLoader TagByID TagLoader TagCategoryByID TagCategoryLoader - EditById EditLoader - EditCommentById EditCommentLoader + EditByID EditLoader + EditCommentByID EditCommentLoader } func Middleware(fac models.Repo) func(next http.Handler) http.Handler { @@ -225,7 +225,7 @@ func GetLoaders(ctx context.Context, fac models.Repo) *Loaders { return qb.FindByIds(ids) }, }, - EditById: EditLoader{ + EditByID: EditLoader{ maxBatch: 1000, wait: 1 * time.Millisecond, fetch: func(ids []uuid.UUID) ([]*models.Edit, []error) { @@ -233,7 +233,7 @@ func GetLoaders(ctx context.Context, fac models.Repo) *Loaders { return qb.FindByIds(ids) }, }, - EditCommentById: EditCommentLoader{ + EditCommentByID: EditCommentLoader{ maxBatch: 1000, wait: 1 * time.Millisecond, fetch: func(ids []uuid.UUID) ([]*models.EditComment, []error) { @@ -241,7 +241,7 @@ func GetLoaders(ctx context.Context, fac models.Repo) *Loaders { return qb.FindCommentsByIds(ids) }, }, - SceneById: SceneLoader{ + SceneByID: SceneLoader{ maxBatch: 1000, wait: 1 * time.Millisecond, fetch: func(ids []uuid.UUID) ([]*models.Scene, []error) { diff --git a/pkg/manager/notifications/notifications.go b/pkg/manager/notifications/notifications.go index 03240e3f9..8dd401f58 100644 --- a/pkg/manager/notifications/notifications.go +++ b/pkg/manager/notifications/notifications.go @@ -1,3 +1,4 @@ +//nolint:errcheck package notifications import ( @@ -31,11 +32,12 @@ func OnCancelEdit(fac models.Repo, edit *models.Edit) { func OnCreateEdit(fac models.Repo, edit *models.Edit) { fmt.Println("onCreateEdit") - if edit.TargetType == models.TargetTypeEnumPerformer.String() { + switch edit.TargetType { + case models.TargetTypeEnumPerformer.String(): fac.Notification().TriggerPerformerEditNotifications(edit.ID) - } else if edit.TargetType == models.TargetTypeEnumScene.String() { + case models.TargetTypeEnumScene.String(): fac.Notification().TriggerSceneEditNotifications(edit.ID) - } else if edit.TargetType == models.TargetTypeEnumStudio.String() { + case models.TargetTypeEnumStudio.String(): fac.Notification().TriggerStudioEditNotifications(edit.ID) } } diff --git a/pkg/sqlx/querybuilder_notifications.go b/pkg/sqlx/querybuilder_notifications.go index 52f8f3037..38b246d09 100644 --- a/pkg/sqlx/querybuilder_notifications.go +++ b/pkg/sqlx/querybuilder_notifications.go @@ -25,14 +25,6 @@ func newNotificationQueryBuilder(txn *txnState) models.NotificationRepo { } } -func (qb *notificationsQueryBuilder) toModel(ro interface{}) *models.Notification { - if ro != nil { - return ro.(*models.Notification) - } - - return nil -} - func (qb *notificationsQueryBuilder) TriggerSceneCreationNotifications(sceneID uuid.UUID) error { var args []interface{} args = append(args, sceneID)