Compare commits

..

5 Commits

Author SHA1 Message Date
adf05e3c28 Add computePotAim query and shot-simulation operations
All checks were successful
Tests / Tests (pull_request) Successful in 10s
Schema: computePotAim(simulationInput, targetBallId, pocket) ->
PotAimGQL, the spin/throw-compensated aim solver (regenerated from
backend strawberry definitions; reuses the existing PocketIdentifier
enum). Operations: GetTableState, SimulateShot, and ComputePotAim
documents so both clients get generated hooks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 01:44:02 -07:00
5391466e90 Merge pull request 'Add simulateShot query, cue strike and shot projection types' (#277) from shot-simulation-stubs into master
Reviewed-on: #277
2026-07-02 23:35:30 +00:00
257dcdc31a Merge pull request 'Add DismissVideoExport mutation (soft-hide exports)' (#276) from dean/export-dismiss-gql into master
Reviewed-on: #276
2026-07-02 22:38:02 +00:00
Dean Wenstrand
b41365e99e Add videoName + videoThumbnailUri to VideoExportJobFields
All checks were successful
Tests / Tests (pull_request) Successful in 13s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 14:41:59 -07:00
Dean Wenstrand
0e8233a5d5 Add DismissVideoExport mutation (soft-hide exports)
All checks were successful
Tests / Tests (pull_request) Successful in 17s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 14:18:55 -07:00
4 changed files with 506 additions and 0 deletions

View File

@@ -2436,6 +2436,7 @@ export type Mutation = {
deleteUser: Scalars["Boolean"]["output"]; deleteUser: Scalars["Boolean"]["output"];
deleteVideo: Scalars["Boolean"]["output"]; deleteVideo: Scalars["Boolean"]["output"];
dismissChallenge: Scalars["Boolean"]["output"]; dismissChallenge: Scalars["Boolean"]["output"];
dismissVideoExport: Scalars["Boolean"]["output"];
editComment: Scalars["Boolean"]["output"]; editComment: Scalars["Boolean"]["output"];
editProfileImageUri: UserGql; editProfileImageUri: UserGql;
editShot: EditShotReturn; editShot: EditShotReturn;
@@ -2544,6 +2545,10 @@ export type MutationDismissChallengeArgs = {
challengeId: Scalars["ID"]["input"]; challengeId: Scalars["ID"]["input"];
}; };
export type MutationDismissVideoExportArgs = {
jobId: Scalars["Int"]["input"];
};
export type MutationEditCommentArgs = { export type MutationEditCommentArgs = {
commentId: Scalars["Int"]["input"]; commentId: Scalars["Int"]["input"];
newMessage: Scalars["String"]["input"]; newMessage: Scalars["String"]["input"];
@@ -2839,6 +2844,18 @@ export type PocketingIntentionInfoGql = {
pocketId: PocketIdentifier; pocketId: PocketIdentifier;
}; };
export type PotAimGql = {
__typename?: "PotAimGQL";
converged: Scalars["Boolean"]["output"];
cutAngle: Scalars["Float"]["output"];
feasible: Scalars["Boolean"]["output"];
geometricPhi: Scalars["Float"]["output"];
occludingBallIds: Array<Scalars["Int"]["output"]>;
phi: Scalars["Float"]["output"];
potted: Scalars["Boolean"]["output"];
requiredPrecision: Scalars["Float"]["output"];
};
export type ProcessingFailedErr = { export type ProcessingFailedErr = {
__typename?: "ProcessingFailedErr"; __typename?: "ProcessingFailedErr";
processing: VideoProcessingGql; processing: VideoProcessingGql;
@@ -2860,6 +2877,7 @@ export type Query = {
challenge?: Maybe<Challenge>; challenge?: Maybe<Challenge>;
challengeLeaderboard: Array<ChallengeEntry>; challengeLeaderboard: Array<ChallengeEntry>;
challenges: Array<Challenge>; challenges: Array<Challenge>;
computePotAim: PotAimGql;
doesUsernameExist: Scalars["Boolean"]["output"]; doesUsernameExist: Scalars["Boolean"]["output"];
getAggregatedShotMetrics: Array<AggregateResultGql>; getAggregatedShotMetrics: Array<AggregateResultGql>;
getAppleAppAccountToken: Scalars["String"]["output"]; getAppleAppAccountToken: Scalars["String"]["output"];
@@ -2922,6 +2940,12 @@ export type QueryChallengesArgs = {
includeDismissed?: Scalars["Boolean"]["input"]; includeDismissed?: Scalars["Boolean"]["input"];
}; };
export type QueryComputePotAimArgs = {
pocket: PocketIdentifier;
simulationInput: SimulateShotInputGql;
targetBallId: Scalars["Int"]["input"];
};
export type QueryDoesUsernameExistArgs = { export type QueryDoesUsernameExistArgs = {
candidateUsername: Scalars["String"]["input"]; candidateUsername: Scalars["String"]["input"];
}; };
@@ -3764,6 +3788,8 @@ export type VideoExportJobGql = {
shotIds?: Maybe<Array<Scalars["Int"]["output"]>>; shotIds?: Maybe<Array<Scalars["Int"]["output"]>>;
status: VideoExportStatusEnum; status: VideoExportStatusEnum;
videoId: Scalars["Int"]["output"]; videoId: Scalars["Int"]["output"];
videoName?: Maybe<Scalars["String"]["output"]>;
videoThumbnailUri?: Maybe<Scalars["String"]["output"]>;
}; };
export enum VideoExportModeEnum { export enum VideoExportModeEnum {
@@ -5534,6 +5560,74 @@ export type FinalizePlayerAssignmentsMutation = {
}>; }>;
}; };
export type GetTableStateQueryVariables = Exact<{
b64Image: Scalars["String"]["input"];
tableSize?: InputMaybe<Scalars["Float"]["input"]>;
useHomography?: InputMaybe<HomographyInputGql>;
}>;
export type GetTableStateQuery = {
__typename?: "Query";
getTableState: {
__typename?: "TableStateGQL";
identifierToPosition: Array<Array<number>>;
};
};
export type SimulateShotQueryVariables = Exact<{
simulationInput: SimulateShotInputGql;
}>;
export type SimulateShotQuery = {
__typename?: "Query";
simulateShot: {
__typename?: "ShotProjectionGQL";
pottedBallIds: Array<number>;
trajectories: Array<{
__typename?: "BallTrajectoryGQL";
ballId: number;
points: Array<{
__typename?: "TrajectoryPointGQL";
time: number;
position: Array<number>;
}>;
}>;
events: Array<{
__typename?: "SimulationEventGQL";
eventType: SimulationEventType;
time: number;
ballIds: Array<number>;
position?: Array<number> | null;
}>;
finalState: Array<{
__typename?: "SimulationBallStateGQL";
ballId: number;
position: Array<number>;
}>;
};
};
export type ComputePotAimQueryVariables = Exact<{
simulationInput: SimulateShotInputGql;
targetBallId: Scalars["Int"]["input"];
pocket: PocketIdentifier;
}>;
export type ComputePotAimQuery = {
__typename?: "Query";
computePotAim: {
__typename?: "PotAimGQL";
phi: number;
geometricPhi: number;
cutAngle: number;
requiredPrecision: number;
feasible: boolean;
potted: boolean;
converged: boolean;
occludingBallIds: Array<number>;
};
};
export type GetSerializedShotPathsQueryVariables = Exact<{ export type GetSerializedShotPathsQueryVariables = Exact<{
filterInput: FilterInput; filterInput: FilterInput;
}>; }>;
@@ -7154,6 +7248,8 @@ export type VideoExportJobFieldsFragment = {
videoId: number; videoId: number;
mode: VideoExportModeEnum; mode: VideoExportModeEnum;
status: VideoExportStatusEnum; status: VideoExportStatusEnum;
videoName?: string | null;
videoThumbnailUri?: string | null;
shotIds?: Array<number> | null; shotIds?: Array<number> | null;
runId?: number | null; runId?: number | null;
downloadUrl?: string | null; downloadUrl?: string | null;
@@ -7174,6 +7270,8 @@ export type RequestVideoExportMutation = {
videoId: number; videoId: number;
mode: VideoExportModeEnum; mode: VideoExportModeEnum;
status: VideoExportStatusEnum; status: VideoExportStatusEnum;
videoName?: string | null;
videoThumbnailUri?: string | null;
shotIds?: Array<number> | null; shotIds?: Array<number> | null;
runId?: number | null; runId?: number | null;
downloadUrl?: string | null; downloadUrl?: string | null;
@@ -7183,6 +7281,15 @@ export type RequestVideoExportMutation = {
}; };
}; };
export type DismissVideoExportMutationVariables = Exact<{
jobId: Scalars["Int"]["input"];
}>;
export type DismissVideoExportMutation = {
__typename?: "Mutation";
dismissVideoExport: boolean;
};
export type VideoExportJobQueryVariables = Exact<{ export type VideoExportJobQueryVariables = Exact<{
jobId: Scalars["Int"]["input"]; jobId: Scalars["Int"]["input"];
}>; }>;
@@ -7195,6 +7302,8 @@ export type VideoExportJobQuery = {
videoId: number; videoId: number;
mode: VideoExportModeEnum; mode: VideoExportModeEnum;
status: VideoExportStatusEnum; status: VideoExportStatusEnum;
videoName?: string | null;
videoThumbnailUri?: string | null;
shotIds?: Array<number> | null; shotIds?: Array<number> | null;
runId?: number | null; runId?: number | null;
downloadUrl?: string | null; downloadUrl?: string | null;
@@ -7217,6 +7326,8 @@ export type MyVideoExportsQuery = {
videoId: number; videoId: number;
mode: VideoExportModeEnum; mode: VideoExportModeEnum;
status: VideoExportStatusEnum; status: VideoExportStatusEnum;
videoName?: string | null;
videoThumbnailUri?: string | null;
shotIds?: Array<number> | null; shotIds?: Array<number> | null;
runId?: number | null; runId?: number | null;
downloadUrl?: string | null; downloadUrl?: string | null;
@@ -7806,6 +7917,8 @@ export const VideoExportJobFieldsFragmentDoc = gql`
videoId videoId
mode mode
status status
videoName
videoThumbnailUri
shotIds shotIds
runId runId
downloadUrl downloadUrl
@@ -11774,6 +11887,269 @@ export type FinalizePlayerAssignmentsMutationOptions =
FinalizePlayerAssignmentsMutation, FinalizePlayerAssignmentsMutation,
FinalizePlayerAssignmentsMutationVariables FinalizePlayerAssignmentsMutationVariables
>; >;
export const GetTableStateDocument = gql`
query GetTableState(
$b64Image: String!
$tableSize: Float
$useHomography: HomographyInputGQL
) {
getTableState(
b64Image: $b64Image
tableSize: $tableSize
useHomography: $useHomography
) {
identifierToPosition
}
}
`;
/**
* __useGetTableStateQuery__
*
* To run a query within a React component, call `useGetTableStateQuery` and pass it any options that fit your needs.
* When your component renders, `useGetTableStateQuery` 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 } = useGetTableStateQuery({
* variables: {
* b64Image: // value for 'b64Image'
* tableSize: // value for 'tableSize'
* useHomography: // value for 'useHomography'
* },
* });
*/
export function useGetTableStateQuery(
baseOptions: Apollo.QueryHookOptions<
GetTableStateQuery,
GetTableStateQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<GetTableStateQuery, GetTableStateQueryVariables>(
GetTableStateDocument,
options,
);
}
export function useGetTableStateLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
GetTableStateQuery,
GetTableStateQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<GetTableStateQuery, GetTableStateQueryVariables>(
GetTableStateDocument,
options,
);
}
export function useGetTableStateSuspenseQuery(
baseOptions?: Apollo.SuspenseQueryHookOptions<
GetTableStateQuery,
GetTableStateQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useSuspenseQuery<
GetTableStateQuery,
GetTableStateQueryVariables
>(GetTableStateDocument, options);
}
export type GetTableStateQueryHookResult = ReturnType<
typeof useGetTableStateQuery
>;
export type GetTableStateLazyQueryHookResult = ReturnType<
typeof useGetTableStateLazyQuery
>;
export type GetTableStateSuspenseQueryHookResult = ReturnType<
typeof useGetTableStateSuspenseQuery
>;
export type GetTableStateQueryResult = Apollo.QueryResult<
GetTableStateQuery,
GetTableStateQueryVariables
>;
export const SimulateShotDocument = gql`
query SimulateShot($simulationInput: SimulateShotInputGQL!) {
simulateShot(simulationInput: $simulationInput) {
trajectories {
ballId
points {
time
position
}
}
events {
eventType
time
ballIds
position
}
finalState {
ballId
position
}
pottedBallIds
}
}
`;
/**
* __useSimulateShotQuery__
*
* To run a query within a React component, call `useSimulateShotQuery` and pass it any options that fit your needs.
* When your component renders, `useSimulateShotQuery` 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 } = useSimulateShotQuery({
* variables: {
* simulationInput: // value for 'simulationInput'
* },
* });
*/
export function useSimulateShotQuery(
baseOptions: Apollo.QueryHookOptions<
SimulateShotQuery,
SimulateShotQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<SimulateShotQuery, SimulateShotQueryVariables>(
SimulateShotDocument,
options,
);
}
export function useSimulateShotLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
SimulateShotQuery,
SimulateShotQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<SimulateShotQuery, SimulateShotQueryVariables>(
SimulateShotDocument,
options,
);
}
export function useSimulateShotSuspenseQuery(
baseOptions?: Apollo.SuspenseQueryHookOptions<
SimulateShotQuery,
SimulateShotQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useSuspenseQuery<SimulateShotQuery, SimulateShotQueryVariables>(
SimulateShotDocument,
options,
);
}
export type SimulateShotQueryHookResult = ReturnType<
typeof useSimulateShotQuery
>;
export type SimulateShotLazyQueryHookResult = ReturnType<
typeof useSimulateShotLazyQuery
>;
export type SimulateShotSuspenseQueryHookResult = ReturnType<
typeof useSimulateShotSuspenseQuery
>;
export type SimulateShotQueryResult = Apollo.QueryResult<
SimulateShotQuery,
SimulateShotQueryVariables
>;
export const ComputePotAimDocument = gql`
query ComputePotAim(
$simulationInput: SimulateShotInputGQL!
$targetBallId: Int!
$pocket: PocketIdentifier!
) {
computePotAim(
simulationInput: $simulationInput
targetBallId: $targetBallId
pocket: $pocket
) {
phi
geometricPhi
cutAngle
requiredPrecision
feasible
potted
converged
occludingBallIds
}
}
`;
/**
* __useComputePotAimQuery__
*
* To run a query within a React component, call `useComputePotAimQuery` and pass it any options that fit your needs.
* When your component renders, `useComputePotAimQuery` 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 } = useComputePotAimQuery({
* variables: {
* simulationInput: // value for 'simulationInput'
* targetBallId: // value for 'targetBallId'
* pocket: // value for 'pocket'
* },
* });
*/
export function useComputePotAimQuery(
baseOptions: Apollo.QueryHookOptions<
ComputePotAimQuery,
ComputePotAimQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<ComputePotAimQuery, ComputePotAimQueryVariables>(
ComputePotAimDocument,
options,
);
}
export function useComputePotAimLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
ComputePotAimQuery,
ComputePotAimQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<ComputePotAimQuery, ComputePotAimQueryVariables>(
ComputePotAimDocument,
options,
);
}
export function useComputePotAimSuspenseQuery(
baseOptions?: Apollo.SuspenseQueryHookOptions<
ComputePotAimQuery,
ComputePotAimQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useSuspenseQuery<
ComputePotAimQuery,
ComputePotAimQueryVariables
>(ComputePotAimDocument, options);
}
export type ComputePotAimQueryHookResult = ReturnType<
typeof useComputePotAimQuery
>;
export type ComputePotAimLazyQueryHookResult = ReturnType<
typeof useComputePotAimLazyQuery
>;
export type ComputePotAimSuspenseQueryHookResult = ReturnType<
typeof useComputePotAimSuspenseQuery
>;
export type ComputePotAimQueryResult = Apollo.QueryResult<
ComputePotAimQuery,
ComputePotAimQueryVariables
>;
export const GetSerializedShotPathsDocument = gql` export const GetSerializedShotPathsDocument = gql`
query GetSerializedShotPaths($filterInput: FilterInput!) { query GetSerializedShotPaths($filterInput: FilterInput!) {
getShots(filterInput: $filterInput) { getShots(filterInput: $filterInput) {
@@ -15276,6 +15652,54 @@ export type RequestVideoExportMutationOptions = Apollo.BaseMutationOptions<
RequestVideoExportMutation, RequestVideoExportMutation,
RequestVideoExportMutationVariables RequestVideoExportMutationVariables
>; >;
export const DismissVideoExportDocument = gql`
mutation DismissVideoExport($jobId: Int!) {
dismissVideoExport(jobId: $jobId)
}
`;
export type DismissVideoExportMutationFn = Apollo.MutationFunction<
DismissVideoExportMutation,
DismissVideoExportMutationVariables
>;
/**
* __useDismissVideoExportMutation__
*
* To run a mutation, you first call `useDismissVideoExportMutation` within a React component and pass it any options that fit your needs.
* When your component renders, `useDismissVideoExportMutation` 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 [dismissVideoExportMutation, { data, loading, error }] = useDismissVideoExportMutation({
* variables: {
* jobId: // value for 'jobId'
* },
* });
*/
export function useDismissVideoExportMutation(
baseOptions?: Apollo.MutationHookOptions<
DismissVideoExportMutation,
DismissVideoExportMutationVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useMutation<
DismissVideoExportMutation,
DismissVideoExportMutationVariables
>(DismissVideoExportDocument, options);
}
export type DismissVideoExportMutationHookResult = ReturnType<
typeof useDismissVideoExportMutation
>;
export type DismissVideoExportMutationResult =
Apollo.MutationResult<DismissVideoExportMutation>;
export type DismissVideoExportMutationOptions = Apollo.BaseMutationOptions<
DismissVideoExportMutation,
DismissVideoExportMutationVariables
>;
export const VideoExportJobDocument = gql` export const VideoExportJobDocument = gql`
query VideoExportJob($jobId: Int!) { query VideoExportJob($jobId: Int!) {
videoExportJob(jobId: $jobId) { videoExportJob(jobId: $jobId) {

View File

@@ -0,0 +1,57 @@
query GetTableState(
$b64Image: String!
$tableSize: Float
$useHomography: HomographyInputGQL
) {
getTableState(
b64Image: $b64Image
tableSize: $tableSize
useHomography: $useHomography
) {
identifierToPosition
}
}
query SimulateShot($simulationInput: SimulateShotInputGQL!) {
simulateShot(simulationInput: $simulationInput) {
trajectories {
ballId
points {
time
position
}
}
events {
eventType
time
ballIds
position
}
finalState {
ballId
position
}
pottedBallIds
}
}
query ComputePotAim(
$simulationInput: SimulateShotInputGQL!
$targetBallId: Int!
$pocket: PocketIdentifier!
) {
computePotAim(
simulationInput: $simulationInput
targetBallId: $targetBallId
pocket: $pocket
) {
phi
geometricPhi
cutAngle
requiredPrecision
feasible
potted
converged
occludingBallIds
}
}

View File

@@ -3,6 +3,8 @@ fragment VideoExportJobFields on VideoExportJobGQL {
videoId videoId
mode mode
status status
videoName
videoThumbnailUri
shotIds shotIds
runId runId
downloadUrl downloadUrl
@@ -17,6 +19,10 @@ mutation RequestVideoExport($input: RequestVideoExportInput!) {
} }
} }
mutation DismissVideoExport($jobId: Int!) {
dismissVideoExport(jobId: $jobId)
}
query VideoExportJob($jobId: Int!) { query VideoExportJob($jobId: Int!) {
videoExportJob(jobId: $jobId) { videoExportJob(jobId: $jobId) {
...VideoExportJobFields ...VideoExportJobFields

View File

@@ -67,6 +67,11 @@ type Query {
useHomography: HomographyInputGQL = null useHomography: HomographyInputGQL = null
): TableStateGQL! ): TableStateGQL!
simulateShot(simulationInput: SimulateShotInputGQL!): ShotProjectionGQL! simulateShot(simulationInput: SimulateShotInputGQL!): ShotProjectionGQL!
computePotAim(
simulationInput: SimulateShotInputGQL!
targetBallId: Int!
pocket: PocketIdentifier!
): PotAimGQL!
getOrderedShots( getOrderedShots(
filterInput: FilterInput! filterInput: FilterInput!
ids: [Int!] = null ids: [Int!] = null
@@ -1030,6 +1035,17 @@ input SimulationBallStateInputGQL {
position: [Float!]! position: [Float!]!
} }
type PotAimGQL {
phi: Float!
geometricPhi: Float!
cutAngle: Float!
requiredPrecision: Float!
feasible: Boolean!
potted: Boolean!
converged: Boolean!
occludingBallIds: [Int!]!
}
type GetShotsResult { type GetShotsResult {
shots: [ShotGQL!]! shots: [ShotGQL!]!
count: Int count: Int
@@ -1233,6 +1249,8 @@ type VideoExportJobGQL {
videoId: Int! videoId: Int!
mode: VideoExportModeEnum! mode: VideoExportModeEnum!
status: VideoExportStatusEnum! status: VideoExportStatusEnum!
videoName: String
videoThumbnailUri: String
shotIds: [Int!] shotIds: [Int!]
runId: Int runId: Int
downloadUrl: String downloadUrl: String
@@ -1358,6 +1376,7 @@ type Mutation {
metadata: CancellationFeedbackMetadataInput = null metadata: CancellationFeedbackMetadataInput = null
): Boolean! ): Boolean!
requestVideoExport(input: RequestVideoExportInput!): VideoExportJobGQL! requestVideoExport(input: RequestVideoExportInput!): VideoExportJobGQL!
dismissVideoExport(jobId: Int!): Boolean!
findPrerecordTableLayout(b64Image: String!, videoId: Int!): HomographyInfoGQL findPrerecordTableLayout(b64Image: String!, videoId: Int!): HomographyInfoGQL
createUploadStream( createUploadStream(
videoMetadata: VideoMetadataInput! videoMetadata: VideoMetadataInput!