Compare commits

..

15 Commits

Author SHA1 Message Date
Dean Wenstrand
901f4222a4 Let shot filters select a single player within a video
All checks were successful
Tests / Tests (pull_request) Successful in 11s
Adds playerIndex to FilterInput, backing ShooterIdentity.player_index --
the cluster-assigned shooter, 0-based and stable only within one video.

Deliberately separate from userId, which is populated only once a cluster
has been confirmed against a registered account. Multiplayer sessions are
usually played against someone without a Railbird login, so filtering by
userId cannot express "the other player" in the common case; the cluster
index always can.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 13:14:00 -07:00
0f6e7b28d4 Merge pull request 'Return the shot's start frame image from getShotTableState' (#321) from ivan/shot-table-state-frame-image into master 2026-08-06 19:11:59 +00:00
9b6b08c1ec Return the shot's start frame image from getShotTableState
All checks were successful
Tests / Tests (pull_request) Successful in 12s
Shot Lab was showing the video's single thumbnail next to every shot's
layout. The backend now returns the frame the layout was detected in as
a base64 JPEG (frameImage), selected by the GetShotTableState operation.
Also dedupes ShareLinkGQL/CreateShotShareLinkInput, which appeared twice
in the committed schema after a bad merge.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-06 09:56:38 -07:00
0f01c71a43 Merge pull request 'Add Session Coach generation status query' (#320) from loewy/session-coach-status-polling-gql into master
Reviewed-on: #320
2026-08-06 06:40:20 +00:00
238ade1384 Add Session Coach generation status query
All checks were successful
Tests / Tests (pull_request) Successful in 40s
2026-08-05 12:08:51 -07:00
fe9b9ac9cc Merge pull request 'Select the detection audit payload on getTableState' (#315) from dean/shot-lab-detection-boxes-gql into master 2026-08-05 07:34:14 +00:00
0cf074b35b Merge pull request 'Revert premature Shot Lab GQL reconciliation (#317)' (#319) from loewy/revert-shot-boxes-gql-317 into master 2026-08-05 06:43:21 +00:00
e3d7a5aaac Revert "Merge pull request 'Restore Shot Lab detection box GraphQL fields' (#317) from loewy/fix-forward-shot-boxes-gql into master"
All checks were successful
Tests / Tests (pull_request) Successful in 1m8s
This reverts commit 65151d4b6b, reversing
changes made to 926b2aab7a.
2026-08-04 23:16:10 -07:00
65151d4b6b Merge pull request 'Restore Shot Lab detection box GraphQL fields' (#317) from loewy/fix-forward-shot-boxes-gql into master 2026-08-05 05:55:55 +00:00
15fe6e5c3d Restore Shot Lab detection box GraphQL fields
All checks were successful
Tests / Tests (pull_request) Successful in 1m39s
2026-08-04 22:45:33 -07:00
926b2aab7a Merge pull request 'Generate Session Coach feedback operations' (#313) from loewy/session-coach-feedback-gql into master
Reviewed-on: #313
2026-08-05 03:57:42 +00:00
bbe50cacb7 Generate Session Coach feedback operations
All checks were successful
Tests / Tests (pull_request) Successful in 29s
2026-08-04 15:07:56 -07:00
1953248639 Regenerate Session Coach feedback schema 2026-08-04 15:07:54 -07:00
032fa6b898 Merge pull request 'Add pastDueUsers, newPaidToday, and cohort paidTotal to usage stats' (#314) from dean/usage-stats-pastdue-alltime into master
Reviewed-on: #314
2026-08-01 02:42:49 +00:00
Dean Wenstrand
81565b7676 Add pastDueUsers, newPaidToday, and cohort paidTotal to usage stats
All checks were successful
Tests / Tests (pull_request) Successful in 4m48s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 22:28:53 -04:00
5 changed files with 431 additions and 13 deletions

View File

@@ -265,6 +265,7 @@ export type CohortStatsGql = {
activated7d: Scalars["Int"]["output"];
activatedNotPaid7d: Scalars["Int"]["output"];
paid7d: Scalars["Int"]["output"];
paidTotal: Scalars["Int"]["output"];
signups: Scalars["Int"]["output"];
weekStart: Scalars["Date"]["output"];
};
@@ -475,6 +476,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -519,6 +521,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -563,6 +566,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -607,6 +611,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -651,6 +656,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -695,6 +701,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -739,6 +746,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -783,6 +791,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -827,6 +836,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -871,6 +881,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -915,6 +926,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -959,6 +971,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -1003,6 +1016,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -1047,6 +1061,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -1091,6 +1106,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -1135,6 +1151,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -1179,6 +1196,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -1223,6 +1241,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -1267,6 +1286,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -1311,6 +1331,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -1355,6 +1376,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -1399,6 +1421,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -1443,6 +1466,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -1487,6 +1511,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -1531,6 +1556,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -1575,6 +1601,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -1619,6 +1646,7 @@ export type FilterInput =
missAngleInDegrees: FloatRangeFilter;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -1663,6 +1691,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter: FilterInput;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -1707,6 +1736,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters: Array<FilterInput>;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -1751,6 +1781,52 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex: Array<Scalars["Int"]["input"]>;
runId?: never;
runLength?: never;
shotDirection?: never;
spinType?: never;
tableSize?: never;
tags?: never;
targetPocketAngle?: never;
targetPocketAngleDirection?: never;
targetPocketDistance?: never;
totalDistance?: never;
userId?: never;
username?: never;
videoId?: never;
}
| {
andFilters?: never;
annotations?: never;
backcut?: never;
bankAngle?: never;
bankDistance?: never;
createdAt?: never;
cueAngleAfterObject?: never;
cueBallSpeed?: never;
cueObjectAngle?: never;
cueObjectDistance?: never;
cueSpeedAfterObject?: never;
difficulty?: never;
falsePositiveScore?: never;
fargoRating?: never;
intendedPocketType?: never;
isBreakHeuristic?: never;
isDirect?: never;
isLeft?: never;
isLeftMiss?: never;
isRight?: never;
isRightMiss?: never;
isStraight?: never;
kickAngle?: never;
kickDistance?: never;
make?: never;
marginOfErrorInDegrees?: never;
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId: Array<Scalars["Int"]["input"]>;
runLength?: never;
shotDirection?: never;
@@ -1795,6 +1871,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength: FloatRangeFilter;
shotDirection?: never;
@@ -1839,6 +1916,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection: Array<ShotDirectionEnum>;
@@ -1883,6 +1961,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -1927,6 +2006,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -1971,6 +2051,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -2015,6 +2096,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -2059,6 +2141,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -2103,6 +2186,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -2147,6 +2231,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -2191,6 +2276,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -2235,6 +2321,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -2279,6 +2366,7 @@ export type FilterInput =
missAngleInDegrees?: never;
notFilter?: never;
orFilters?: never;
playerIndex?: never;
runId?: never;
runLength?: never;
shotDirection?: never;
@@ -2323,6 +2411,8 @@ export type FunnelStatsGql = {
freemiumEpoch: Scalars["Date"]["output"];
newPaid7d: Scalars["Int"]["output"];
newPaid30d: Scalars["Int"]["output"];
newPaidToday: Scalars["Int"]["output"];
pastDueUsers: Scalars["Int"]["output"];
payingUsers: Scalars["Int"]["output"];
retentionPrevActive: Scalars["Int"]["output"];
retentionReturned: Scalars["Int"]["output"];
@@ -2579,6 +2669,7 @@ export type Mutation = {
startChallenge: ChallengeEntry;
submitCancellationFeedback: Scalars["Boolean"]["output"];
submitChallengeEntry: ChallengeEntry;
submitSessionCoachFeedback: SessionCoachFeedbackGql;
syncAppleSubscription: SyncAppleSubscriptionResultGql;
undismissChallenge: Scalars["Boolean"]["output"];
unfollowUser: UserGql;
@@ -2840,6 +2931,12 @@ export type MutationSubmitChallengeEntryArgs = {
videoId: Scalars["ID"]["input"];
};
export type MutationSubmitSessionCoachFeedbackArgs = {
generationId: Scalars["ID"]["input"];
response: SessionCoachFeedbackResponseEnum;
videoId: Scalars["Int"]["input"];
};
export type MutationSyncAppleSubscriptionArgs = {
input: SyncAppleSubscriptionInputGql;
};
@@ -3122,6 +3219,7 @@ export type Query = {
getResolvedTier: ResolvedTierGql;
getRuns: GetRunsResult;
getSessionCoach: SessionCoachGql;
getSessionCoachGenerationStatus?: Maybe<SessionCoachGenerationStatusGql>;
getShotAnnotationTypes: Array<ShotAnnotationTypeGql>;
getShotTableState: ShotTableStateGql;
getShots: Array<ShotGql>;
@@ -3283,6 +3381,11 @@ export type QueryGetSessionCoachArgs = {
videoId: Scalars["Int"]["input"];
};
export type QueryGetSessionCoachGenerationStatusArgs = {
generationId: Scalars["ID"]["input"];
videoId: Scalars["Int"]["input"];
};
export type QueryGetShotAnnotationTypesArgs = {
errorTypes?: InputMaybe<Scalars["Boolean"]["input"]>;
};
@@ -3685,6 +3788,19 @@ export type SessionCoachEvidenceGql = {
score: Scalars["Float"]["output"];
};
export type SessionCoachFeedbackGql = {
__typename?: "SessionCoachFeedbackGQL";
response: SessionCoachFeedbackResponseEnum;
updatedAt: Scalars["DateTime"]["output"];
};
export enum SessionCoachFeedbackResponseEnum {
AlreadyWorkingOnThis = "ALREADY_WORKING_ON_THIS",
Helpful = "HELPFUL",
NotEnoughEvidence = "NOT_ENOUGH_EVIDENCE",
WrongFocus = "WRONG_FOCUS",
}
export type SessionCoachGql = {
__typename?: "SessionCoachGQL";
agentGeneration?: Maybe<SessionCoachGenerationGql>;
@@ -3737,6 +3853,7 @@ export type SessionCoachGenerationGql = {
createdAt: Scalars["DateTime"]["output"];
decision?: Maybe<SessionCoachDecisionGql>;
diagnostics?: Maybe<SessionCoachGenerationDiagnosticsGql>;
feedback?: Maybe<SessionCoachFeedbackGql>;
id: Scalars["ID"]["output"];
state: SessionCoachGenerationStateEnum;
};
@@ -3748,6 +3865,18 @@ export enum SessionCoachGenerationStateEnum {
Running = "RUNNING",
}
export type SessionCoachGenerationStatusGql = {
__typename?: "SessionCoachGenerationStatusGQL";
completedAt?: Maybe<Scalars["DateTime"]["output"]>;
createdAt: Scalars["DateTime"]["output"];
enqueueCount: Scalars["Int"]["output"];
id: Scalars["ID"]["output"];
lastEnqueuedAt?: Maybe<Scalars["DateTime"]["output"]>;
startedAt?: Maybe<Scalars["DateTime"]["output"]>;
state: SessionCoachGenerationStateEnum;
updatedAt: Scalars["DateTime"]["output"];
};
export enum SessionCoachIneligibilityReasonEnum {
Multiplayer = "MULTIPLAYER",
}
@@ -3840,6 +3969,7 @@ export type ShotProjectionGql = {
export type ShotTableStateGql = {
__typename?: "ShotTableStateGQL";
frameImage?: Maybe<Scalars["String"]["output"]>;
frameIndex: Scalars["Int"]["output"];
shotId: Scalars["Int"]["output"];
tableState: TableStateGql;
@@ -7205,10 +7335,35 @@ export type GetSessionCoachQuery = {
abstentionReason?: string | null;
synthesisRationale?: string | null;
} | null;
feedback?: {
__typename?: "SessionCoachFeedbackGQL";
response: SessionCoachFeedbackResponseEnum;
updatedAt: any;
} | null;
} | null;
};
};
export type GetSessionCoachGenerationStatusQueryVariables = Exact<{
videoId: Scalars["Int"]["input"];
generationId: Scalars["ID"]["input"];
}>;
export type GetSessionCoachGenerationStatusQuery = {
__typename?: "Query";
getSessionCoachGenerationStatus?: {
__typename?: "SessionCoachGenerationStatusGQL";
id: string;
state: SessionCoachGenerationStateEnum;
createdAt: any;
updatedAt: any;
startedAt?: any | null;
lastEnqueuedAt?: any | null;
enqueueCount: number;
completedAt?: any | null;
} | null;
};
export type RequestSessionCoachGenerationMutationVariables = Exact<{
videoId: Scalars["Int"]["input"];
}>;
@@ -7262,10 +7417,30 @@ export type RequestSessionCoachGenerationMutation = {
abstentionReason?: string | null;
synthesisRationale?: string | null;
} | null;
feedback?: {
__typename?: "SessionCoachFeedbackGQL";
response: SessionCoachFeedbackResponseEnum;
updatedAt: any;
} | null;
} | null;
};
};
export type SubmitSessionCoachFeedbackMutationVariables = Exact<{
videoId: Scalars["Int"]["input"];
generationId: Scalars["ID"]["input"];
response: SessionCoachFeedbackResponseEnum;
}>;
export type SubmitSessionCoachFeedbackMutation = {
__typename?: "Mutation";
submitSessionCoachFeedback: {
__typename?: "SessionCoachFeedbackGQL";
response: SessionCoachFeedbackResponseEnum;
updatedAt: any;
};
};
export type SessionCoachCandidateFieldsFragment = {
__typename?: "SessionCoachCandidateGQL";
id: string;
@@ -7343,6 +7518,11 @@ export type SessionCoachGenerationFieldsFragment = {
abstentionReason?: string | null;
synthesisRationale?: string | null;
} | null;
feedback?: {
__typename?: "SessionCoachFeedbackGQL";
response: SessionCoachFeedbackResponseEnum;
updatedAt: any;
} | null;
};
export type CreateShotShareLinkMutationVariables = Exact<{
@@ -7643,6 +7823,7 @@ export type GetShotTableStateQuery = {
shotId: number;
videoId: number;
frameIndex: number;
frameImage?: string | null;
tableState: {
__typename?: "TableStateGQL";
identifierToPosition: Array<Array<number>>;
@@ -8268,6 +8449,8 @@ export type GetUsageStatsQuery = {
activationCohort: number;
activationActivated: number;
payingUsers: number;
pastDueUsers: number;
newPaidToday: number;
newPaid7d: number;
newPaid30d: number;
retentionPrevActive: number;
@@ -8280,6 +8463,7 @@ export type GetUsageStatsQuery = {
activated7d: number;
paid7d: number;
activatedNotPaid7d: number;
paidTotal: number;
}>;
windows: Array<{
__typename?: "UsageStatsWindowGQL";
@@ -10062,6 +10246,10 @@ export const SessionCoachGenerationFieldsFragmentDoc = gql`
abstentionReason
synthesisRationale
}
feedback {
response
updatedAt
}
}
`;
export const PlayerClusterShotFieldsFragmentDoc = gql`
@@ -15862,6 +16050,90 @@ export type GetSessionCoachQueryResult = Apollo.QueryResult<
GetSessionCoachQuery,
GetSessionCoachQueryVariables
>;
export const GetSessionCoachGenerationStatusDocument = gql`
query GetSessionCoachGenerationStatus($videoId: Int!, $generationId: ID!) {
getSessionCoachGenerationStatus(
videoId: $videoId
generationId: $generationId
) {
id
state
createdAt
updatedAt
startedAt
lastEnqueuedAt
enqueueCount
completedAt
}
}
`;
/**
* __useGetSessionCoachGenerationStatusQuery__
*
* To run a query within a React component, call `useGetSessionCoachGenerationStatusQuery` and pass it any options that fit your needs.
* When your component renders, `useGetSessionCoachGenerationStatusQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useGetSessionCoachGenerationStatusQuery({
* variables: {
* videoId: // value for 'videoId'
* generationId: // value for 'generationId'
* },
* });
*/
export function useGetSessionCoachGenerationStatusQuery(
baseOptions: Apollo.QueryHookOptions<
GetSessionCoachGenerationStatusQuery,
GetSessionCoachGenerationStatusQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<
GetSessionCoachGenerationStatusQuery,
GetSessionCoachGenerationStatusQueryVariables
>(GetSessionCoachGenerationStatusDocument, options);
}
export function useGetSessionCoachGenerationStatusLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
GetSessionCoachGenerationStatusQuery,
GetSessionCoachGenerationStatusQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<
GetSessionCoachGenerationStatusQuery,
GetSessionCoachGenerationStatusQueryVariables
>(GetSessionCoachGenerationStatusDocument, options);
}
export function useGetSessionCoachGenerationStatusSuspenseQuery(
baseOptions?: Apollo.SuspenseQueryHookOptions<
GetSessionCoachGenerationStatusQuery,
GetSessionCoachGenerationStatusQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useSuspenseQuery<
GetSessionCoachGenerationStatusQuery,
GetSessionCoachGenerationStatusQueryVariables
>(GetSessionCoachGenerationStatusDocument, options);
}
export type GetSessionCoachGenerationStatusQueryHookResult = ReturnType<
typeof useGetSessionCoachGenerationStatusQuery
>;
export type GetSessionCoachGenerationStatusLazyQueryHookResult = ReturnType<
typeof useGetSessionCoachGenerationStatusLazyQuery
>;
export type GetSessionCoachGenerationStatusSuspenseQueryHookResult = ReturnType<
typeof useGetSessionCoachGenerationStatusSuspenseQuery
>;
export type GetSessionCoachGenerationStatusQueryResult = Apollo.QueryResult<
GetSessionCoachGenerationStatusQuery,
GetSessionCoachGenerationStatusQueryVariables
>;
export const RequestSessionCoachGenerationDocument = gql`
mutation RequestSessionCoachGeneration($videoId: Int!) {
requestSessionCoachGeneration(videoId: $videoId) {
@@ -15917,6 +16189,68 @@ export type RequestSessionCoachGenerationMutationOptions =
RequestSessionCoachGenerationMutation,
RequestSessionCoachGenerationMutationVariables
>;
export const SubmitSessionCoachFeedbackDocument = gql`
mutation SubmitSessionCoachFeedback(
$videoId: Int!
$generationId: ID!
$response: SessionCoachFeedbackResponseEnum!
) {
submitSessionCoachFeedback(
videoId: $videoId
generationId: $generationId
response: $response
) {
response
updatedAt
}
}
`;
export type SubmitSessionCoachFeedbackMutationFn = Apollo.MutationFunction<
SubmitSessionCoachFeedbackMutation,
SubmitSessionCoachFeedbackMutationVariables
>;
/**
* __useSubmitSessionCoachFeedbackMutation__
*
* To run a mutation, you first call `useSubmitSessionCoachFeedbackMutation` within a React component and pass it any options that fit your needs.
* When your component renders, `useSubmitSessionCoachFeedbackMutation` returns a tuple that includes:
* - A mutate function that you can call at any time to execute the mutation
* - An object with fields that represent the current status of the mutation's execution
*
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
*
* @example
* const [submitSessionCoachFeedbackMutation, { data, loading, error }] = useSubmitSessionCoachFeedbackMutation({
* variables: {
* videoId: // value for 'videoId'
* generationId: // value for 'generationId'
* response: // value for 'response'
* },
* });
*/
export function useSubmitSessionCoachFeedbackMutation(
baseOptions?: Apollo.MutationHookOptions<
SubmitSessionCoachFeedbackMutation,
SubmitSessionCoachFeedbackMutationVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useMutation<
SubmitSessionCoachFeedbackMutation,
SubmitSessionCoachFeedbackMutationVariables
>(SubmitSessionCoachFeedbackDocument, options);
}
export type SubmitSessionCoachFeedbackMutationHookResult = ReturnType<
typeof useSubmitSessionCoachFeedbackMutation
>;
export type SubmitSessionCoachFeedbackMutationResult =
Apollo.MutationResult<SubmitSessionCoachFeedbackMutation>;
export type SubmitSessionCoachFeedbackMutationOptions =
Apollo.BaseMutationOptions<
SubmitSessionCoachFeedbackMutation,
SubmitSessionCoachFeedbackMutationVariables
>;
export const CreateShotShareLinkDocument = gql`
mutation CreateShotShareLink(
$shotIds: [Int!]!
@@ -16270,6 +16604,7 @@ export const GetShotTableStateDocument = gql`
shotId
videoId
frameIndex
frameImage
tableState {
...TableStateLayoutFields
}
@@ -17430,6 +17765,8 @@ export const GetUsageStatsDocument = gql`
activationCohort
activationActivated
payingUsers
pastDueUsers
newPaidToday
newPaid7d
newPaid30d
retentionPrevActive
@@ -17441,6 +17778,7 @@ export const GetUsageStatsDocument = gql`
activated7d
paid7d
activatedNotPaid7d
paidTotal
}
totalUsers
totalVideos

View File

@@ -36,6 +36,22 @@ query GetSessionCoach($videoId: Int!) {
}
}
query GetSessionCoachGenerationStatus($videoId: Int!, $generationId: ID!) {
getSessionCoachGenerationStatus(
videoId: $videoId
generationId: $generationId
) {
id
state
createdAt
updatedAt
startedAt
lastEnqueuedAt
enqueueCount
completedAt
}
}
mutation RequestSessionCoachGeneration($videoId: Int!) {
requestSessionCoachGeneration(videoId: $videoId) {
state
@@ -45,6 +61,21 @@ mutation RequestSessionCoachGeneration($videoId: Int!) {
}
}
mutation SubmitSessionCoachFeedback(
$videoId: Int!
$generationId: ID!
$response: SessionCoachFeedbackResponseEnum!
) {
submitSessionCoachFeedback(
videoId: $videoId
generationId: $generationId
response: $response
) {
response
updatedAt
}
}
fragment SessionCoachCandidateFields on SessionCoachCandidateGQL {
id
rank
@@ -117,4 +148,8 @@ fragment SessionCoachGenerationFields on SessionCoachGenerationGQL {
abstentionReason
synthesisRationale
}
feedback {
response
updatedAt
}
}

View File

@@ -44,9 +44,10 @@ fragment DetectionPocketPointFields on PocketPointsGQL {
# 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.
# Only the photo query selects this. The shot query returns its source
# frame (frameImage) but positions there come from the stored homography,
# so there are no fresh boxes to draw; the live query returns no image at
# all and is polled every five seconds.
fragment TableStateDetectionFields on TableStateGQL {
identifierToBox {
...DetectionBoxFields
@@ -97,6 +98,9 @@ query GetShotTableState($shotId: Int!) {
shotId
videoId
frameIndex
# The b64 JPEG frame the layout was detected in, so the client can show
# the shot's actual starting frame instead of the video's thumbnail.
frameImage
tableState {
...TableStateLayoutFields
}

View File

@@ -7,6 +7,8 @@ query getUsageStats($days: Int! = 30) {
activationCohort
activationActivated
payingUsers
pastDueUsers
newPaidToday
newPaid7d
newPaid30d
retentionPrevActive
@@ -18,6 +20,7 @@ query getUsageStats($days: Int! = 30) {
activated7d
paid7d
activatedNotPaid7d
paidTotal
}
totalUsers
totalVideos

View File

@@ -214,6 +214,7 @@ type CohortStatsGQL {
activated7d: Int!
paid7d: Int!
activatedNotPaid7d: Int!
paidTotal: Int!
}
type CommentGQL {
@@ -249,11 +250,6 @@ input CreatePoolHallCameraInput {
claimTermsText: String = null
}
input CreateShotShareLinkInput {
shotIds: [Int!]!
paddingSeconds: Float = null
}
input CreatePoolHallInput {
name: String!
address: String = null
@@ -264,6 +260,11 @@ input CreatePoolHallInput {
timezone: String = null
}
input CreateShotShareLinkInput {
shotIds: [Int!]!
paddingSeconds: Float = null
}
type CreateSubscriptionResultGQL {
checkoutUrl: String!
sessionId: String!
@@ -407,6 +408,7 @@ input FilterInput @oneOf {
userId: [Int!]
runId: [Int!]
username: [String!]
playerIndex: [Int!]
fargoRating: FloatRangeFilter
make: [Boolean!]
tags: [VideoTagInput!]
@@ -464,6 +466,8 @@ type FunnelStatsGQL {
activationCohort: Int!
activationActivated: Int!
payingUsers: Int!
pastDueUsers: Int!
newPaidToday: Int!
newPaid7d: Int!
newPaid30d: Int!
retentionPrevActive: Int!
@@ -728,6 +732,11 @@ type Mutation {
shotId: Int!
fieldsToEdit: EditableShotFieldInputGQL!
): EditShotReturn!
submitSessionCoachFeedback(
videoId: Int!
generationId: ID!
response: SessionCoachFeedbackResponseEnum!
): SessionCoachFeedbackGQL!
requestSessionCoachGeneration(videoId: Int!): SessionCoachGQL!
getProfileImageUploadLink(
fileExt: String = ".png"
@@ -1068,6 +1077,10 @@ type Query {
limit: Int! = 500
countRespectsLimit: Boolean! = false
): GetRunsResult!
getSessionCoachGenerationStatus(
videoId: Int!
generationId: ID!
): SessionCoachGenerationStatusGQL
getSessionCoach(videoId: Int!): SessionCoachGQL!
videoPlayerClusters(videoId: Int!): [PlayerClusterGQL!]!
getShotAnnotationTypes(errorTypes: Boolean = false): [ShotAnnotationTypeGQL!]!
@@ -1387,6 +1400,18 @@ type SessionCoachEvidenceGQL {
baselineMakePercentage: Float
}
type SessionCoachFeedbackGQL {
response: SessionCoachFeedbackResponseEnum!
updatedAt: DateTime!
}
enum SessionCoachFeedbackResponseEnum {
HELPFUL
WRONG_FOCUS
ALREADY_WORKING_ON_THIS
NOT_ENOUGH_EVIDENCE
}
type SessionCoachGQL {
videoId: Int!
processingId: Int
@@ -1438,6 +1463,7 @@ type SessionCoachGenerationGQL {
completedAt: DateTime
decision: SessionCoachDecisionGQL
diagnostics: SessionCoachGenerationDiagnosticsGQL
feedback: SessionCoachFeedbackGQL
}
enum SessionCoachGenerationStateEnum {
@@ -1447,6 +1473,17 @@ enum SessionCoachGenerationStateEnum {
FAILED
}
type SessionCoachGenerationStatusGQL {
id: ID!
state: SessionCoachGenerationStateEnum!
createdAt: DateTime!
updatedAt: DateTime!
startedAt: DateTime
lastEnqueuedAt: DateTime
enqueueCount: Int!
completedAt: DateTime
}
enum SessionCoachIneligibilityReasonEnum {
MULTIPLAYER
}
@@ -1459,6 +1496,11 @@ enum SessionCoachStateEnum {
FAILED
}
type ShareLinkGQL {
slug: String!
url: String!
}
type ShotAnnotationGQL {
shotId: Int!
type: ShotAnnotationTypeGQL!
@@ -1527,6 +1569,7 @@ type ShotTableStateGQL {
videoId: Int!
frameIndex: Int!
tableState: TableStateGQL!
frameImage: String
}
input ShotsOrderingComponent @oneOf {
@@ -1920,11 +1963,6 @@ type UserSubscriptionStatusGQL {
stripeSubscriptionId: String
}
type ShareLinkGQL {
slug: String!
url: String!
}
type VideoExportJobGQL {
id: Int!
videoId: Int!