Compare commits

..

22 Commits

Author SHA1 Message Date
8111042936 Add pool hall camera claim schema
All checks were successful
Tests / Tests (pull_request) Successful in 9s
2026-06-16 15:41:16 -07:00
d6fd68c1f6 add quotaEnforcementEnabled to deployed config
All checks were successful
Tests / Tests (pull_request) Successful in 10s
2026-05-28 14:40:31 -07:00
deb724b430 Add quota bucket status fields
All checks were successful
Tests / Tests (pull_request) Successful in 9s
2026-05-25 16:26:57 -07:00
Dean Wenstrand
9250e4c639 Add username + profileImageUri to PlayerClusterGQL
All checks were successful
Tests / Tests (pull_request) Successful in 9s
The labeling UI was falling back to "user N" for any assigned cluster
whose user wasn't the video owner — e.g. an admin re-labels a video
they don't own. With the resolver now resolving usernames for every
confirmed cluster, the FE can render real names regardless of who's
viewing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 17:25:34 -07:00
Dean Wenstrand
5cf2dbaf01 Add averageDifficulty to PlayerSummaryFields
All checks were successful
Tests / Tests (pull_request) Successful in 9s
Regenerated via `just gql` after BE added average_difficulty to
PlayerSummaryGQL.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 15:34:25 -07:00
239a143554 Merge pull request 'Regenerate schema + add longestRun to PlayerSummaryFields' (#244) from dean/player-summaries-longest-run-types into master
Reviewed-on: #244
2026-05-11 20:29:39 +00:00
Dean Wenstrand
296522afb8 Regenerate schema + add longestRun to PlayerSummaryFields
All checks were successful
Tests / Tests (pull_request) Successful in 10s
Generated by `just gql` after BE added longest_run to PlayerSummaryGQL.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 13:21:47 -07:00
f42579076e Merge pull request 'Add score to PlayerSummaryFields + PlayerClusterFields fragments' (#243) from dean/video-match-score-types into master
Reviewed-on: #243
2026-05-11 17:43:43 +00:00
Dean Wenstrand
0c9eb4945a Add score to PlayerSummaryFields + PlayerClusterFields fragments
All checks were successful
Tests / Tests (pull_request) Successful in 9s
Picks up the BE additions in railbird PR dean/video-match-score:
new `score` field on PlayerClusterGQL and PlayerSummaryGQL, and
a new `score` input field on ClusterAssignmentInput.

Generated by `just gql`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 10:22:58 -07:00
1182c15004 Merge pull request 'Add playerSummaries to schema + Video fragments' (#242) from dean/player-summaries-types into master
Reviewed-on: #242
2026-05-09 19:34:40 +00:00
Dean Wenstrand
755336b16a Add playerSummaries to schema + Video fragments
All checks were successful
Tests / Tests (pull_request) Successful in 10s
Backs the multi-player vs UI: feed cards and the detail page both
read `video.playerSummaries`, a per-cluster rollup with username,
profile image, representative full-frame URL, makes/total/percentage.

  - PlayerSummaryFields fragment in shooter.gql
  - VideoCardFields (feed) and GetVideoDetails (detail) include
    playerSummaries via the new fragment
  - VideoCardFields tag selection extended to include tagClasses,
    needed for the FE's player_count detection

Generated by `just gql` from the BE additions in railbird PR
dean/video-player-summaries.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 12:02:57 -07:00
c1efe9f5f2 Merge pull request 'Add VideoPlayerClusters query + FinalizePlayerAssignments mutation' (#241) from dean/player-labeling-ops into master
Reviewed-on: #241
2026-05-09 04:46:10 +00:00
Dean Wenstrand
a3460842ac Add VideoPlayerClusters query + FinalizePlayerAssignments mutation
All checks were successful
Tests / Tests (pull_request) Successful in 10s
Operation file for the per-video player-labeling UI that consumes the
schema types added in #240.

  - VideoPlayerClusters($videoId): read clusters + crops
  - FinalizePlayerAssignments($input): apply user assignments and
    shot moves in one transaction

Generated by `just gql`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 21:44:42 -07:00
84d3a0252d Merge pull request 'Add player cluster + labeling mutation types' (#240) from dean/labeling-api-types into master
Reviewed-on: #240
2026-05-09 00:38:29 +00:00
130314546c Merge pull request 'Add trial days to GetAvailableSubscriptions' (#239) from loewy/add-trial-period-days into master
Reviewed-on: #239
2026-05-04 21:46:45 +00:00
b88f172355 add trial days to return
All checks were successful
Tests / Tests (pull_request) Successful in 10s
2026-05-04 14:07:16 -07:00
639fc88b0b Merge pull request 'Add createCustomerPortalSession' (#238) from loewy/create-customer-portal-session into master
Reviewed-on: #238
2026-05-01 22:45:40 +00:00
20f50368c9 remove return url
All checks were successful
Tests / Tests (pull_request) Successful in 10s
2026-05-01 13:15:18 -07:00
8367c2d0cd add createCustomerPortalSession
All checks were successful
Tests / Tests (pull_request) Successful in 10s
2026-04-30 11:44:34 -07:00
b0c62f6e80 Merge pull request 'Trim getFeed query fragment' (#237) from loewy/trim-get-feed-query-fragment into master
Reviewed-on: #237
2026-04-29 20:26:40 +00:00
bc1ff66467 trim uncessary requested fields
All checks were successful
Tests / Tests (pull_request) Successful in 38s
2026-04-28 12:53:25 -07:00
ae37a3d9d9 Merge pull request 'Default getLongestRunsLeaderboard limit to 50' (#236) from dean/leaderboard-default-top-50 into master
Reviewed-on: #236
2026-04-24 00:01:23 +00:00
7 changed files with 821 additions and 62 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -6,6 +6,7 @@ query getDeployedConfig {
firebase
minimumAllowedAppVersion
subscriptionGatingEnabled
quotaEnforcementEnabled
bannerMessages {
color
dismissible

View File

@@ -31,41 +31,32 @@ fragment VideoCardFields on VideoGQL {
}
name
screenshotUri
totalShotsMade
totalShots
makePercentage
averageTimeBetweenShots
averageDifficulty
createdAt
updatedAt
startTime
endTime
private
elapsedTime
screenshotUri
stream {
id
lastIntendedSegmentBound
isCompleted
streamSegmentType
}
tableSize
pocketSize
tags {
name
tagClasses {
name
}
name
}
playerSummaries {
...PlayerSummaryFields
}
currentProcessing {
id
errors {
message
}
status
statuses {
status
}
}
reactions {
videoId

View File

@@ -20,8 +20,15 @@ mutation CreateSubscription($priceId: String!) {
}
}
mutation CreateCustomerPortalSession {
createCustomerPortalSession {
portalUrl
}
}
query GetAvailableSubscriptionOptions {
getAvailableSubscriptionOptions {
trialPeriodDays
products {
id
name

View File

@@ -0,0 +1,52 @@
fragment PlayerSummaryFields on PlayerSummaryGQL {
clusterId
userId
username
profileImageUri
representativeFullFrameUrl
totalShots
totalShotsMade
makePercentage
score
longestRun
averageDifficulty
averageTimeBetweenShots
}
fragment PlayerClusterShotFields on PlayerClusterShotGQL {
shotId
bboxX1
bboxY1
bboxX2
bboxY2
confidence
isConfirmed
cropUrl
fullFrameUrl
}
fragment PlayerClusterFields on PlayerClusterGQL {
videoId
clusterId
nShots
userId
username
profileImageUri
confirmed
score
shots {
...PlayerClusterShotFields
}
}
query VideoPlayerClusters($videoId: Int!) {
videoPlayerClusters(videoId: $videoId) {
...PlayerClusterFields
}
}
mutation FinalizePlayerAssignments($input: FinalizePlayerAssignmentsInput!) {
finalizePlayerAssignments(input: $input) {
...PlayerClusterFields
}
}

View File

@@ -83,6 +83,9 @@ query GetVideoDetails($videoId: Int!) {
}
name
}
playerSummaries {
...PlayerSummaryFields
}
}
}

View File

@@ -42,6 +42,12 @@ type Query {
filters: NotificationFilters = null
): NotificationConnection!
unreadNotificationCount: Int!
poolHalls: [PoolHall!]!
claimablePoolHalls: [PoolHall!]!
poolHallCameras(poolHallId: ID!): [PoolHallCamera!]!
claimableCameras(poolHallId: ID!): [PoolHallCamera!]!
cameraClaimSession(id: ID!): CameraClaimSession
activeCameraLease: CameraLease
getRuns(
filterInput: RunFilterInput!
runIds: [Int!] = null
@@ -406,6 +412,7 @@ type VideoGQL {
currentProcessing: VideoProcessingGQL
reactions: [ReactionGQL!]!
comments: [CommentGQL!]!
playerSummaries: [PlayerSummaryGQL!]!
}
type ShotGQL {
@@ -665,6 +672,21 @@ type CommentGQL {
replies: [CommentGQL!]!
}
type PlayerSummaryGQL {
clusterId: Int!
userId: Int
username: String
profileImageUri: String
representativeFullFrameUrl: String
totalShots: Int!
totalShotsMade: Int!
makePercentage: Float!
score: Int
longestRun: Int!
averageDifficulty: Float
averageTimeBetweenShots: Float
}
type DeployedConfigGQL {
allowNewUsers: Boolean!
firebase: Boolean!
@@ -672,6 +694,7 @@ type DeployedConfigGQL {
environment: String!
minimumAllowedAppVersion: String!
subscriptionGatingEnabled: Boolean!
quotaEnforcementEnabled: Boolean!
bannerMessages: [BannerGQL!]!
defaultAndroidRecordingFormat: StreamSegmentTypeEnum!
bucketUrl: String!
@@ -823,6 +846,63 @@ input NotificationFilters {
notificationTypes: [NotificationTypeEnum!] = null
}
type PoolHall {
id: ID!
name: String!
address: String
latitude: Float
longitude: Float
timezone: String
status: String!
createdAt: DateTime!
updatedAt: DateTime!
}
type PoolHallCamera {
id: ID!
poolHallId: ID!
name: String!
tableLabel: String
streamPath: String!
status: String!
lastPublishedAt: DateTime
lastUnpublishedAt: DateTime
createdAt: DateTime!
updatedAt: DateTime!
poolHall: PoolHall!
}
type CameraClaimSession {
id: ID!
cameraId: ID!
userId: ID!
challengeCode: String!
status: String!
expiresAt: DateTime!
detectedAt: DateTime
failedAt: DateTime
failureReason: String
createdAt: DateTime!
updatedAt: DateTime!
camera: PoolHallCamera!
}
type CameraLease {
id: ID!
cameraId: ID!
claimSessionId: ID
userId: ID!
videoId: ID
status: String!
startedAt: DateTime!
endedAt: DateTime
expiresAt: DateTime
endReason: String
createdAt: DateTime!
updatedAt: DateTime!
camera: PoolHallCamera!
}
type GetRunsResult {
runs: [RunGQL!]!
count: Int
@@ -866,7 +946,10 @@ type PlayerClusterGQL {
clusterId: Int!
nShots: Int!
userId: Int
username: String
profileImageUri: String
confirmed: Boolean!
score: Int
shots: [PlayerClusterShotGQL!]!
}
@@ -1021,6 +1104,19 @@ type QuotaStatusGQL {
durationUsedSeconds: Float!
durationLimitSeconds: Int
maxVideoDurationSeconds: Int
importQuotaBuckets: [QuotaBucketStatusGQL!]!
recordingQuotaBuckets: [QuotaBucketStatusGQL!]!
durationRemainingSeconds: Float
canUpload: Boolean!
}
type QuotaBucketStatusGQL {
quotaKey: String!
appliesToUploadKind: String!
periodStart: DateTime!
periodEnd: DateTime!
durationUsedSeconds: Float!
durationLimitSeconds: Int
durationRemainingSeconds: Float
canUpload: Boolean!
}
@@ -1116,6 +1212,15 @@ type Mutation {
markAllNotificationsAsRead: Boolean!
markNotificationsAsRead(notificationIds: [Int!]!): Boolean!
deleteNotification(notificationId: Int!): Boolean!
createPoolHall(input: CreatePoolHallInput!): PoolHall!
updatePoolHall(input: UpdatePoolHallInput!): PoolHall!
createPoolHallCamera(
input: CreatePoolHallCameraInput!
): PoolHallCameraStreamCredentials!
updatePoolHallCamera(input: UpdatePoolHallCameraInput!): PoolHallCamera!
rotatePoolHallCameraStreamKey(cameraId: ID!): PoolHallCameraStreamCredentials!
createCameraClaimSession(cameraId: ID!): CameraClaimSession!
cancelCameraClaimSession(claimSessionId: ID!): CameraClaimSession!
finalizePlayerAssignments(
input: FinalizePlayerAssignmentsInput!
): [PlayerClusterGQL!]!
@@ -1190,6 +1295,44 @@ enum ReportReasonEnum {
OTHER
}
input CreatePoolHallInput {
name: String!
address: String = null
latitude: Float = null
longitude: Float = null
timezone: String = null
}
input UpdatePoolHallInput {
id: ID!
name: String = null
address: String = null
latitude: Float = null
longitude: Float = null
timezone: String = null
status: String = null
}
type PoolHallCameraStreamCredentials {
camera: PoolHallCamera!
streamKey: String!
rtmpPath: String!
}
input CreatePoolHallCameraInput {
poolHallId: ID!
name: String!
tableLabel: String = null
streamPath: String = null
}
input UpdatePoolHallCameraInput {
id: ID!
name: String = null
tableLabel: String = null
status: String = null
}
input FinalizePlayerAssignmentsInput {
videoId: Int!
clusterAssignments: [ClusterAssignmentInput!]! = []
@@ -1199,6 +1342,7 @@ input FinalizePlayerAssignmentsInput {
input ClusterAssignmentInput {
clusterId: Int!
userId: Int = null
score: Int = null
}
input ShotMoveInput {