Compare commits

..

1 Commits

Author SHA1 Message Date
Dean Wenstrand
fd6ee4f182 Declare taxonomy filter variables on the makes/runs leaderboards
The backend leaderboard resolvers gained game_type, table size and pocket
size filters, but GetMakesLeaderboard and GetRunsLeaderboard never declared
them — GraphQL silently drops undeclared variables, so anything the client
passed was ignored and both boards stayed global.

Declare and forward the five filter args on both queries, matching the
names the drill run leaderboard already uses.
2026-07-20 22:35:48 -07:00
10 changed files with 129 additions and 1426 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -78,7 +78,6 @@ fragment VideoCardFields on VideoGQL {
replies {
id
message
replyToCommentId
user {
...UserSocialsFields
}

View File

@@ -1,25 +0,0 @@
# Legacy shot-sim operations still used by mobile master's
# LiveStreamPicker / RecordedShotPicker; remove when those are deleted.
query GetRecordedStreams($limit: Int! = 25) {
getFeedVideos(
limit: $limit
filters: { isStreamCompleted: true, excludeVideosWithNoShots: true }
includePrivate: MINE
feedInput: { allUsers: true }
) {
videos {
id
name
createdAt
screenshotUri
framesPerSecond
totalShots
owner {
id
username
profileImageUri
}
}
}
}

View File

@@ -1,19 +1,10 @@
# Lightweight two-step picker data for Shot Lab's video-first source: recent
# videos from the requested audience (mine, followed users, or public), then
# that video's shots. The stream fields hint at "actively updating" without
# gating.
query GetShotLabVideos(
$feedInput: VideoFeedInputGQL!
$includePrivate: IncludePrivateEnum!
$limit: Int! = 25
$after: String = null
) {
# Lightweight two-step picker data for loading a recorded shot into Shot Lab.
query GetRecordedStreams($limit: Int! = 25) {
getFeedVideos(
limit: $limit
after: $after
includeCallersVideos: false
includePrivate: $includePrivate
feedInput: $feedInput
filters: { isStreamCompleted: true, excludeVideosWithNoShots: true }
includePrivate: MINE
feedInput: { allUsers: true }
) {
videos {
id
@@ -27,15 +18,6 @@ query GetShotLabVideos(
username
profileImageUri
}
stream {
id
isCompleted
lastSegmentUploadedAt
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
@@ -54,11 +36,26 @@ query GetRecordedStreamShots($videoId: Int!, $limit: Int! = 200) {
serializedShotPaths {
b64EncodedBuffer
}
cueObjectFeatures {
cueObjectDistance
cueObjectAngle
cueBallSpeed
shotDirection
spinType
}
pocketingIntentionFeatures {
make
targetPocketDistance
targetPocketAngle
targetPocketAngleDirection
marginOfErrorInDegrees
intendedPocketType
difficulty
}
pocketingIntentionInfo {
ballId
pocketId
pathMetadataIndex
}
}
}

View File

@@ -14,34 +14,12 @@ query GetSessionCoach($videoId: Int!) {
unknownOutcomeCount
analyzedShotCount
makePercentage
longestMakeStreak
recentBaseline {
sessionCount
attemptCount
madeCount
makePercentage
}
positiveCandidates {
...SessionCoachCandidateFields
}
primaryCandidate {
...SessionCoachCandidateFields
}
candidates {
...SessionCoachCandidateFields
}
agentGeneration {
...SessionCoachGenerationFields
}
}
}
mutation RequestSessionCoachGeneration($videoId: Int!) {
requestSessionCoachGeneration(videoId: $videoId) {
state
agentGeneration {
...SessionCoachGenerationFields
}
}
}
@@ -69,52 +47,5 @@ fragment SessionCoachCandidateFields on SessionCoachCandidateGQL {
patternShotCount
score
rankReason
baselineSessionCount
baselineAttemptCount
baselineMadeCount
baselineMakePercentage
}
}
fragment SessionCoachGenerationFields on SessionCoachGenerationGQL {
id
state
createdAt
completedAt
decision {
selectedPositiveCandidateId
positiveTitle
positiveBody
selectedCandidateId
supportingCandidateIds
supportingShotIds
sessionSummary
title
body
suggestedAction
drillId
drillReason
practiceAttemptCount
practiceTarget
}
diagnostics {
analysisVersion
promptVersion
providerKey
modelName
inputHash
attemptCount
maxAttempts
enqueueCount
latencyMs
inputTokens
cachedInputTokens
outputTokens
failureCode
updatedAt
startedAt
lastEnqueuedAt
abstentionReason
synthesisRationale
}
}

View File

@@ -1,8 +0,0 @@
mutation CreateShotShareLink($shotIds: [Int!]!, $paddingSeconds: Float = null) {
createShotShareLink(
input: { shotIds: $shotIds, paddingSeconds: $paddingSeconds }
) {
slug
url
}
}

View File

@@ -1,72 +1,3 @@
fragment TableStateLayoutFields on TableStateGQL {
identifierToPosition
identifierToColor {
hex
}
}
fragment DetectionBoxFields on BoundingBoxGQL {
left
top
width
height
}
fragment DetectionPocketPointFields on PocketPointsGQL {
topLeft {
x
y
}
topSide {
x
y
}
topRight {
x
y
}
bottomLeft {
x
y
}
bottomSide {
x
y
}
bottomRight {
x
y
}
}
# Everything needed to redraw a detection over the image it came from:
# the detector's boxes plus the homography that turned them into plane
# positions. Boxes, pocket boxes, and source points share the crop's
# coordinate space; the crop locates that space in the full frame.
#
# Only the photo query selects this. The live and shot queries read a
# frame out of a video the app never holds, so there is no image to
# overlay, and getLiveTableState is polled every five seconds.
fragment TableStateDetectionFields on TableStateGQL {
identifierToBox {
...DetectionBoxFields
}
homography {
crop {
...DetectionBoxFields
}
pockets {
...DetectionBoxFields
}
sourcePoints {
...DetectionPocketPointFields
}
destPoints {
...DetectionPocketPointFields
}
}
}
query GetTableState(
$b64Image: String!
$tableSize: Float
@@ -77,8 +8,10 @@ query GetTableState(
tableSize: $tableSize
useHomography: $useHomography
) {
...TableStateLayoutFields
...TableStateDetectionFields
identifierToPosition
identifierToColor {
hex
}
}
}
@@ -87,7 +20,10 @@ query GetLiveTableState($videoId: Int!) {
videoId
frameIndex
tableState {
...TableStateLayoutFields
identifierToPosition
identifierToColor {
hex
}
}
}
}
@@ -98,7 +34,10 @@ query GetShotTableState($shotId: Int!) {
videoId
frameIndex
tableState {
...TableStateLayoutFields
identifierToPosition
identifierToColor {
hex
}
}
}
}

View File

@@ -131,7 +131,6 @@ query GetVideoSocialDetailsById($videoId: Int!) {
replies {
id
message
replyToCommentId
user {
...UserSocialsFields
}

View File

@@ -48,52 +48,6 @@ mutation GetUploadLink($videoId: Int!, $segmentIndex: Int!) {
}
}
stream {
id
uploadCompletionCursor
}
}
}
mutation GetUploadLinks($videoId: Int!, $segmentIndexes: [Int!]!) {
getUploadLinks(videoId: $videoId, segmentIndexes: $segmentIndexes) {
value {
... on UploadLinkBatch {
links {
segmentIndex
uploadUrl
headers {
key
value
}
}
alreadyUploaded
}
... on GetUploadLinkErrors {
error {
... on MustHaveSetForUploadLinkErr {
resolution
framesPerSecond
}
... on ProcessingFailedErr {
processing {
status
errors {
message
}
}
}
... on StorageLimitExceededErr {
reason
tierName
retainedStorageUsedBytes
retainedStorageLimitBytes
remainingStorageBytes
}
}
}
}
stream {
id
uploadCompletionCursor
}
}
@@ -173,7 +127,6 @@ fragment UploadStreamWithDetails on VideoGQL {
name
startTime
stream {
id
isCompleted
lastIntendedSegmentBound
uploadCompletionCursor

View File

@@ -221,7 +221,6 @@ type CommentGQL {
user: UserGQL!
message: String!
replies: [CommentGQL!]!
replyToCommentId: Int
}
type CountLeaderboardGQL {
@@ -249,11 +248,6 @@ input CreatePoolHallCameraInput {
claimTermsText: String = null
}
input CreateShotShareLinkInput {
shotIds: [Int!]!
paddingSeconds: Float = null
}
input CreatePoolHallInput {
name: String!
address: String = null
@@ -531,11 +525,6 @@ type GetUploadLinkReturn {
stream: UploadStreamGQL
}
type GetUploadLinksReturn {
value: UploadLinkBatchGetUploadLinkErrors!
stream: UploadStreamGQL
}
type HLSPlaylistGQL {
videoId: Int!
m3u8Text: String!
@@ -711,7 +700,6 @@ type Mutation {
cancelCameraClaimSession(claimSessionId: ID!): CameraClaimSession!
endCameraLease(leaseId: ID!): CameraLease!
extendCameraLease(leaseId: ID!, additionalMinutes: Int! = 60): CameraLease!
createShotShareLink(input: CreateShotShareLinkInput!): ShareLinkGQL!
finalizePlayerAssignments(
input: FinalizePlayerAssignmentsInput!
): [PlayerClusterGQL!]!
@@ -728,7 +716,6 @@ type Mutation {
shotId: Int!
fieldsToEdit: EditableShotFieldInputGQL!
): EditShotReturn!
requestSessionCoachGeneration(videoId: Int!): SessionCoachGQL!
getProfileImageUploadLink(
fileExt: String = ".png"
): GetProfileUploadLinkReturn!
@@ -766,7 +753,6 @@ type Mutation {
expectedDurationSeconds: Float = null
): CreateUploadStreamReturn!
getUploadLink(videoId: Int!, segmentIndex: Int!): GetUploadLinkReturn!
getUploadLinks(videoId: Int!, segmentIndexes: [Int!]!): GetUploadLinksReturn!
getHlsInitUploadLink(videoId: Int!): GetUploadLinkReturn!
setSegmentDuration(
videoId: Int!
@@ -1301,25 +1287,11 @@ type SegmentAlreadyUploadedErr {
segmentId: Int!
}
type SegmentUploadLink {
segmentIndex: Int!
uploadUrl: String!
headers: [Header]!
}
type SerializedShotPathsGQL {
b64EncodedBuffer: String
}
type SessionCoachBaselineGQL {
sessionCount: Int!
attemptCount: Int!
madeCount: Int!
makePercentage: Float!
}
enum SessionCoachCandidateFamilyEnum {
OVERALL
AIMING
CUT_ANGLE
SPIN
@@ -1343,31 +1315,11 @@ type SessionCoachCandidateGQL {
}
enum SessionCoachCandidateKindEnum {
BASELINE_COMPARISON
SESSION_STREAK
STRENGTH_PATTERN
DIRECTIONAL_MISS
OUTCOME_PATTERN
USAGE_PATTERN
}
type SessionCoachDecisionGQL {
selectedPositiveCandidateId: ID
positiveTitle: String
positiveBody: String
selectedCandidateId: ID
supportingCandidateIds: [ID!]!
supportingShotIds: [Int!]!
sessionSummary: String
title: String
body: String
suggestedAction: String
drillId: String
drillReason: String
practiceAttemptCount: Int
practiceTarget: String
}
type SessionCoachEvidenceGQL {
bucketKey: String!
bucketLabel: String!
@@ -1381,10 +1333,6 @@ type SessionCoachEvidenceGQL {
patternShotCount: Int!
score: Float!
rankReason: String!
baselineSessionCount: Int!
baselineAttemptCount: Int!
baselineMadeCount: Int!
baselineMakePercentage: Float
}
type SessionCoachGQL {
@@ -1402,49 +1350,8 @@ type SessionCoachGQL {
unknownOutcomeCount: Int!
analyzedShotCount: Int!
makePercentage: Float
longestMakeStreak: Int!
recentBaseline: SessionCoachBaselineGQL
positiveCandidates: [SessionCoachCandidateGQL!]!
primaryCandidate: SessionCoachCandidateGQL
candidates: [SessionCoachCandidateGQL!]!
agentGeneration: SessionCoachGenerationGQL
}
type SessionCoachGenerationDiagnosticsGQL {
analysisVersion: String!
promptVersion: String!
providerKey: String!
modelName: String!
inputHash: String!
attemptCount: Int!
maxAttempts: Int!
enqueueCount: Int!
latencyMs: Int
inputTokens: Int
cachedInputTokens: Int
outputTokens: Int
failureCode: String
updatedAt: DateTime!
startedAt: DateTime
lastEnqueuedAt: DateTime
abstentionReason: String
synthesisRationale: String
}
type SessionCoachGenerationGQL {
id: ID!
state: SessionCoachGenerationStateEnum!
createdAt: DateTime!
completedAt: DateTime
decision: SessionCoachDecisionGQL
diagnostics: SessionCoachGenerationDiagnosticsGQL
}
enum SessionCoachGenerationStateEnum {
QUEUED
RUNNING
READY
FAILED
}
enum SessionCoachIneligibilityReasonEnum {
@@ -1708,7 +1615,6 @@ type TableStateGQL {
identifierToPosition: [[Float!]!]!
homography: HomographyInfoGQL
identifierToColor: [RGBColorGQL]!
identifierToBox: [BoundingBoxGQL!]!
}
type TagClassGQL {
@@ -1794,13 +1700,6 @@ type UploadLink {
headers: [Header]!
}
type UploadLinkBatch {
links: [SegmentUploadLink!]!
alreadyUploaded: [Int!]!
}
union UploadLinkBatchGetUploadLinkErrors = UploadLinkBatch | GetUploadLinkErrors
union UploadLinkGetProfileUploadLinkErrors =
UploadLink
| GetProfileUploadLinkErrors
@@ -1920,11 +1819,6 @@ type UserSubscriptionStatusGQL {
stripeSubscriptionId: String
}
type ShareLinkGQL {
slug: String!
url: String!
}
type VideoExportJobGQL {
id: Int!
videoId: Int!
@@ -1935,7 +1829,7 @@ type VideoExportJobGQL {
shotIds: [Int!]
runId: Int
downloadUrl: String
fileSizeBytes: BigInt
fileSizeBytes: Int
expiresAt: DateTime
createdAt: DateTime
}
@@ -1986,7 +1880,7 @@ type VideoGQL {
startTime: DateTime
endTime: DateTime
elapsedTime: Float
framesPerSecond: Float
framesPerSecond: Float!
tableSize: Float!
pocketSize: Float
private: Boolean!