Compare commits

..

9 Commits

Author SHA1 Message Date
Dean Wenstrand
66fb4d3dcc Add spinTypeBreakdown (draw/center/follow) to PlayerSummaryFields
All checks were successful
Tests / Tests (pull_request) Successful in 9s
2026-06-16 14:36:56 -07:00
Dean Wenstrand
0c1afbcc76 Add runLengths to PlayerSummaryFields (per-player run distribution)
All checks were successful
Tests / Tests (pull_request) Successful in 10s
2026-06-16 13:39:04 -07:00
Dean Wenstrand
53fa982bcf GetDrillRunLeaderboard: table/pocket filter vars + setup fields
All checks were successful
Tests / Tests (pull_request) Successful in 9s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 21:19:37 -07:00
Dean Wenstrand
7ec6ec9193 GetMyDrillRuns: include run date (video.createdAt)
All checks were successful
Tests / Tests (pull_request) Successful in 9s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 19:42:02 -07:00
Dean Wenstrand
0ce333525c Add GetMyDrillRuns query + generated types
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 19:33:56 -07:00
Dean Wenstrand
d1ed5b308b Add GetDrillRunLeaderboard query + generated types
All checks were successful
Tests / Tests (pull_request) Successful in 9s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 16:42:52 -07:00
af68bc3c60 Add Apple subscription sync schema
All checks were successful
Tests / Tests (pull_request) Successful in 10s
2026-06-11 18:40:54 -07:00
f12d2c5a1b Merge pull request 'Add Apple entitlement source enum' (#252) from apple-entitlement-source-enum into master
Reviewed-on: #252
2026-06-11 21:55:26 +00:00
4e3f649a2c Add Apple entitlement source enum
All checks were successful
Tests / Tests (pull_request) Successful in 28s
2026-06-11 12:43:03 -07:00
5 changed files with 484 additions and 8 deletions

View File

@@ -79,6 +79,12 @@ export enum AlignedIntervalEnum {
Year = "YEAR",
}
export type AppleIapSubscriptionOptionsGql = {
__typename?: "AppleIapSubscriptionOptionsGQL";
enabled: Scalars["Boolean"]["output"];
proMonthlyProductId?: Maybe<Scalars["String"]["output"]>;
};
export type BankFeaturesGql = {
__typename?: "BankFeaturesGQL";
bankAngle: Scalars["Float"]["output"];
@@ -304,6 +310,14 @@ export type DoesNotOwnShotErrOtherErrorNeedsNote =
| DoesNotOwnShotErr
| OtherErrorNeedsNote;
export type DrillRunLeaderboardGql = {
__typename?: "DrillRunLeaderboardGQL";
entries: Array<RunGql>;
totalPlayers: Scalars["Int"]["output"];
youRank?: Maybe<Scalars["Int"]["output"]>;
youRun?: Maybe<RunGql>;
};
export type EditShotReturn = {
__typename?: "EditShotReturn";
error?: Maybe<DoesNotOwnShotErr>;
@@ -331,6 +345,7 @@ export type EditableShotFieldInputGql = {
export enum EntitlementSourceTypeEnum {
Admin = "ADMIN",
AlphaLegacy = "ALPHA_LEGACY",
Apple = "APPLE",
Manual = "MANUAL",
Stripe = "STRIPE",
}
@@ -2429,6 +2444,7 @@ export type Mutation = {
startChallenge: ChallengeEntry;
submitCancellationFeedback: Scalars["Boolean"]["output"];
submitChallengeEntry: ChallengeEntry;
syncAppleSubscription: SyncAppleSubscriptionResultGql;
undismissChallenge: Scalars["Boolean"]["output"];
unfollowUser: UserGql;
updateShotAnnotations: UpdateShotAnnotationReturn;
@@ -2632,6 +2648,10 @@ export type MutationSubmitChallengeEntryArgs = {
videoId: Scalars["ID"]["input"];
};
export type MutationSyncAppleSubscriptionArgs = {
input: SyncAppleSubscriptionInputGql;
};
export type MutationUndismissChallengeArgs = {
challengeId: Scalars["ID"]["input"];
};
@@ -2732,7 +2752,9 @@ export type PlayerSummaryGql = {
makePercentage: Scalars["Float"]["output"];
profileImageUri?: Maybe<Scalars["String"]["output"]>;
representativeFullFrameUrl?: Maybe<Scalars["String"]["output"]>;
runLengths: Array<Scalars["Int"]["output"]>;
score?: Maybe<Scalars["Int"]["output"]>;
spinTypeBreakdown: SpinTypeBreakdownGql;
totalShots: Scalars["Int"]["output"];
totalShotsMade: Scalars["Int"]["output"];
userId?: Maybe<Scalars["Int"]["output"]>;
@@ -2814,15 +2836,18 @@ export type Query = {
challenges: Array<Challenge>;
doesUsernameExist: Scalars["Boolean"]["output"];
getAggregatedShotMetrics: Array<AggregateResultGql>;
getAppleAppAccountToken: Scalars["String"]["output"];
getAvailableSubscriptionOptions: StripeSubscriptionOptionsGql;
getBucketSet?: Maybe<BucketSetGql>;
getDeployedConfig: DeployedConfigGql;
getDrillRunLeaderboard: DrillRunLeaderboardGql;
getFeedVideos: VideoHistoryGql;
getGameTypeTagMetrics: Array<GameTypeTagMetric>;
getLoggedInUser?: Maybe<UserGql>;
getLongestRunsLeaderboard: RunLeaderboardGql;
getMakesLeaderboard: CountLeaderboardGql;
getMedals: RequestedMedalsGql;
getMyDrillRuns: Array<RunGql>;
getOrderedShots: GetShotsResult;
getPlayTime: UserPlayTimeGql;
getQuotaStatus: QuotaStatusGql;
@@ -2878,6 +2903,16 @@ export type QueryGetBucketSetArgs = {
keyName: Scalars["String"]["input"];
};
export type QueryGetDrillRunLeaderboardArgs = {
drillTag: Scalars["String"]["input"];
interval?: InputMaybe<TimeInterval>;
limit?: Scalars["Int"]["input"];
pocketSizeMax?: InputMaybe<Scalars["Float"]["input"]>;
pocketSizeMin?: InputMaybe<Scalars["Float"]["input"]>;
tableSizeMax?: InputMaybe<Scalars["Float"]["input"]>;
tableSizeMin?: InputMaybe<Scalars["Float"]["input"]>;
};
export type QueryGetFeedVideosArgs = {
after?: InputMaybe<Scalars["String"]["input"]>;
feedInput?: InputMaybe<VideoFeedInputGql>;
@@ -2908,6 +2943,11 @@ export type QueryGetMedalsArgs = {
userId?: InputMaybe<Scalars["Int"]["input"]>;
};
export type QueryGetMyDrillRunsArgs = {
drillTag: Scalars["String"]["input"];
limit?: Scalars["Int"]["input"];
};
export type QueryGetOrderedShotsArgs = {
countRespectsLimit?: Scalars["Boolean"]["input"];
filterInput: FilterInput;
@@ -3306,6 +3346,13 @@ export type ShotsOrderingComponent =
videoId: IntOrdering;
};
export type SpinTypeBreakdownGql = {
__typename?: "SpinTypeBreakdownGQL";
center: Scalars["Int"]["output"];
draw: Scalars["Int"]["output"];
follow: Scalars["Int"]["output"];
};
export type SpinTypeCountsGql = {
__typename?: "SpinTypeCountsGQL";
center: Scalars["Int"]["output"];
@@ -3353,6 +3400,7 @@ export type StripeProductGql = {
export type StripeSubscriptionOptionsGql = {
__typename?: "StripeSubscriptionOptionsGQL";
appleIap: AppleIapSubscriptionOptionsGql;
products: Array<StripeProductGql>;
trialPeriodDays?: Maybe<Scalars["Int"]["output"]>;
};
@@ -3377,6 +3425,27 @@ export type SuccessfulAddAddShotAnnotationErrors =
| AddShotAnnotationErrors
| SuccessfulAdd;
export type SyncAppleSubscriptionInputGql = {
signedRenewalInfo?: InputMaybe<Scalars["String"]["input"]>;
signedTransactionInfo: Scalars["String"]["input"];
};
export type SyncAppleSubscriptionResultGql = {
__typename?: "SyncAppleSubscriptionResultGQL";
appleStatus?: Maybe<Scalars["Int"]["output"]>;
entitlementEndsAt?: Maybe<Scalars["DateTime"]["output"]>;
entitlementSource?: Maybe<EntitlementSourceTypeEnum>;
entitlementStartsAt?: Maybe<Scalars["DateTime"]["output"]>;
errorCode?: Maybe<Scalars["String"]["output"]>;
errorMessage?: Maybe<Scalars["String"]["output"]>;
expiresAt?: Maybe<Scalars["DateTime"]["output"]>;
hasActiveSubscription: Scalars["Boolean"]["output"];
latestTransactionId?: Maybe<Scalars["String"]["output"]>;
ok: Scalars["Boolean"]["output"];
originalTransactionId?: Maybe<Scalars["String"]["output"]>;
productId?: Maybe<Scalars["String"]["output"]>;
};
export type TableStateGql = {
__typename?: "TableStateGQL";
homography?: Maybe<HomographyInfoGql>;
@@ -4228,8 +4297,15 @@ export type GetFeedQuery = {
makePercentage: number;
score?: number | null;
longestRun: number;
runLengths: Array<number>;
averageDifficulty?: number | null;
averageTimeBetweenShots?: number | null;
spinTypeBreakdown: {
__typename?: "SpinTypeBreakdownGQL";
draw: number;
center: number;
follow: number;
};
}>;
currentProcessing?: {
__typename?: "VideoProcessingGQL";
@@ -4332,8 +4408,15 @@ export type VideoCardFieldsFragment = {
makePercentage: number;
score?: number | null;
longestRun: number;
runLengths: Array<number>;
averageDifficulty?: number | null;
averageTimeBetweenShots?: number | null;
spinTypeBreakdown: {
__typename?: "SpinTypeBreakdownGQL";
draw: number;
center: number;
follow: number;
};
}>;
currentProcessing?: {
__typename?: "VideoProcessingGQL";
@@ -4468,8 +4551,15 @@ export type GetVideoFeedQuery = {
makePercentage: number;
score?: number | null;
longestRun: number;
runLengths: Array<number>;
averageDifficulty?: number | null;
averageTimeBetweenShots?: number | null;
spinTypeBreakdown: {
__typename?: "SpinTypeBreakdownGQL";
draw: number;
center: number;
follow: number;
};
}>;
currentProcessing?: {
__typename?: "VideoProcessingGQL";
@@ -4573,6 +4663,64 @@ export type GetRunsLeaderboardQuery = {
};
};
export type GetDrillRunLeaderboardQueryVariables = Exact<{
drillTag: Scalars["String"]["input"];
interval?: InputMaybe<TimeInterval>;
limit?: InputMaybe<Scalars["Int"]["input"]>;
tableSizeMin?: InputMaybe<Scalars["Float"]["input"]>;
tableSizeMax?: InputMaybe<Scalars["Float"]["input"]>;
pocketSizeMin?: InputMaybe<Scalars["Float"]["input"]>;
pocketSizeMax?: InputMaybe<Scalars["Float"]["input"]>;
}>;
export type GetDrillRunLeaderboardQuery = {
__typename?: "Query";
getDrillRunLeaderboard: {
__typename?: "DrillRunLeaderboardGQL";
youRank?: number | null;
totalPlayers: number;
entries: Array<{
__typename?: "RunGQL";
id: number;
runLength: number;
videoId: number;
video: {
__typename?: "VideoGQL";
tableSize: number;
pocketSize?: number | null;
};
user: {
__typename?: "UserGQL";
id: number;
username: string;
profileImageUri?: string | null;
};
}>;
youRun?: {
__typename?: "RunGQL";
id: number;
runLength: number;
videoId: number;
} | null;
};
};
export type GetMyDrillRunsQueryVariables = Exact<{
drillTag: Scalars["String"]["input"];
limit?: InputMaybe<Scalars["Int"]["input"]>;
}>;
export type GetMyDrillRunsQuery = {
__typename?: "Query";
getMyDrillRuns: Array<{
__typename?: "RunGQL";
id: number;
runLength: number;
videoId: number;
video: { __typename?: "VideoGQL"; createdAt?: any | null };
}>;
};
export type GetVideoMakePercentageIntervalsQueryVariables = Exact<{
videoId: Scalars["ID"]["input"];
intervalDuration: Scalars["Int"]["input"];
@@ -5053,8 +5201,15 @@ export type PlayerSummaryFieldsFragment = {
makePercentage: number;
score?: number | null;
longestRun: number;
runLengths: Array<number>;
averageDifficulty?: number | null;
averageTimeBetweenShots?: number | null;
spinTypeBreakdown: {
__typename?: "SpinTypeBreakdownGQL";
draw: number;
center: number;
follow: number;
};
};
export type PlayerClusterShotFieldsFragment = {
@@ -6178,8 +6333,15 @@ export type GetVideoDetailsQuery = {
makePercentage: number;
score?: number | null;
longestRun: number;
runLengths: Array<number>;
averageDifficulty?: number | null;
averageTimeBetweenShots?: number | null;
spinTypeBreakdown: {
__typename?: "SpinTypeBreakdownGQL";
draw: number;
center: number;
follow: number;
};
}>;
};
};
@@ -6297,8 +6459,15 @@ export type GetVideoCardQuery = {
makePercentage: number;
score?: number | null;
longestRun: number;
runLengths: Array<number>;
averageDifficulty?: number | null;
averageTimeBetweenShots?: number | null;
spinTypeBreakdown: {
__typename?: "SpinTypeBreakdownGQL";
draw: number;
center: number;
follow: number;
};
}>;
currentProcessing?: {
__typename?: "VideoProcessingGQL";
@@ -6896,10 +7065,8 @@ export type UploadStreamWithDetailsFragment = {
startTime?: any | null;
stream?: {
__typename?: "UploadStreamGQL";
id: string;
isCompleted: boolean;
lastIntendedSegmentBound?: number | null;
streamSegmentType: StreamSegmentTypeEnum;
uploadCompletionCursor: number;
uploadsCompleted: number;
} | null;
@@ -6922,10 +7089,8 @@ export type GetUploadStreamsWithDetailsQuery = {
startTime?: any | null;
stream?: {
__typename?: "UploadStreamGQL";
id: string;
isCompleted: boolean;
lastIntendedSegmentBound?: number | null;
streamSegmentType: StreamSegmentTypeEnum;
uploadCompletionCursor: number;
uploadsCompleted: number;
} | null;
@@ -6950,6 +7115,12 @@ export const PlayerSummaryFieldsFragmentDoc = gql`
makePercentage
score
longestRun
runLengths
spinTypeBreakdown {
draw
center
follow
}
averageDifficulty
averageTimeBetweenShots
}
@@ -7313,10 +7484,8 @@ export const UploadStreamWithDetailsFragmentDoc = gql`
name
startTime
stream {
id
isCompleted
lastIntendedSegmentBound
streamSegmentType
uploadCompletionCursor
uploadsCompleted
}
@@ -9553,6 +9722,200 @@ export type GetRunsLeaderboardQueryResult = Apollo.QueryResult<
GetRunsLeaderboardQuery,
GetRunsLeaderboardQueryVariables
>;
export const GetDrillRunLeaderboardDocument = gql`
query GetDrillRunLeaderboard(
$drillTag: String!
$interval: TimeInterval
$limit: Int = 50
$tableSizeMin: Float
$tableSizeMax: Float
$pocketSizeMin: Float
$pocketSizeMax: Float
) {
getDrillRunLeaderboard(
drillTag: $drillTag
interval: $interval
limit: $limit
tableSizeMin: $tableSizeMin
tableSizeMax: $tableSizeMax
pocketSizeMin: $pocketSizeMin
pocketSizeMax: $pocketSizeMax
) {
entries {
id
runLength
videoId
video {
tableSize
pocketSize
}
user {
id
username
profileImageUri
}
}
youRun {
id
runLength
videoId
}
youRank
totalPlayers
}
}
`;
/**
* __useGetDrillRunLeaderboardQuery__
*
* To run a query within a React component, call `useGetDrillRunLeaderboardQuery` and pass it any options that fit your needs.
* When your component renders, `useGetDrillRunLeaderboardQuery` 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 } = useGetDrillRunLeaderboardQuery({
* variables: {
* drillTag: // value for 'drillTag'
* interval: // value for 'interval'
* limit: // value for 'limit'
* tableSizeMin: // value for 'tableSizeMin'
* tableSizeMax: // value for 'tableSizeMax'
* pocketSizeMin: // value for 'pocketSizeMin'
* pocketSizeMax: // value for 'pocketSizeMax'
* },
* });
*/
export function useGetDrillRunLeaderboardQuery(
baseOptions: Apollo.QueryHookOptions<
GetDrillRunLeaderboardQuery,
GetDrillRunLeaderboardQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<
GetDrillRunLeaderboardQuery,
GetDrillRunLeaderboardQueryVariables
>(GetDrillRunLeaderboardDocument, options);
}
export function useGetDrillRunLeaderboardLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
GetDrillRunLeaderboardQuery,
GetDrillRunLeaderboardQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<
GetDrillRunLeaderboardQuery,
GetDrillRunLeaderboardQueryVariables
>(GetDrillRunLeaderboardDocument, options);
}
export function useGetDrillRunLeaderboardSuspenseQuery(
baseOptions?: Apollo.SuspenseQueryHookOptions<
GetDrillRunLeaderboardQuery,
GetDrillRunLeaderboardQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useSuspenseQuery<
GetDrillRunLeaderboardQuery,
GetDrillRunLeaderboardQueryVariables
>(GetDrillRunLeaderboardDocument, options);
}
export type GetDrillRunLeaderboardQueryHookResult = ReturnType<
typeof useGetDrillRunLeaderboardQuery
>;
export type GetDrillRunLeaderboardLazyQueryHookResult = ReturnType<
typeof useGetDrillRunLeaderboardLazyQuery
>;
export type GetDrillRunLeaderboardSuspenseQueryHookResult = ReturnType<
typeof useGetDrillRunLeaderboardSuspenseQuery
>;
export type GetDrillRunLeaderboardQueryResult = Apollo.QueryResult<
GetDrillRunLeaderboardQuery,
GetDrillRunLeaderboardQueryVariables
>;
export const GetMyDrillRunsDocument = gql`
query GetMyDrillRuns($drillTag: String!, $limit: Int = 50) {
getMyDrillRuns(drillTag: $drillTag, limit: $limit) {
id
runLength
videoId
video {
createdAt
}
}
}
`;
/**
* __useGetMyDrillRunsQuery__
*
* To run a query within a React component, call `useGetMyDrillRunsQuery` and pass it any options that fit your needs.
* When your component renders, `useGetMyDrillRunsQuery` 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 } = useGetMyDrillRunsQuery({
* variables: {
* drillTag: // value for 'drillTag'
* limit: // value for 'limit'
* },
* });
*/
export function useGetMyDrillRunsQuery(
baseOptions: Apollo.QueryHookOptions<
GetMyDrillRunsQuery,
GetMyDrillRunsQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<GetMyDrillRunsQuery, GetMyDrillRunsQueryVariables>(
GetMyDrillRunsDocument,
options,
);
}
export function useGetMyDrillRunsLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
GetMyDrillRunsQuery,
GetMyDrillRunsQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<GetMyDrillRunsQuery, GetMyDrillRunsQueryVariables>(
GetMyDrillRunsDocument,
options,
);
}
export function useGetMyDrillRunsSuspenseQuery(
baseOptions?: Apollo.SuspenseQueryHookOptions<
GetMyDrillRunsQuery,
GetMyDrillRunsQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useSuspenseQuery<
GetMyDrillRunsQuery,
GetMyDrillRunsQueryVariables
>(GetMyDrillRunsDocument, options);
}
export type GetMyDrillRunsQueryHookResult = ReturnType<
typeof useGetMyDrillRunsQuery
>;
export type GetMyDrillRunsLazyQueryHookResult = ReturnType<
typeof useGetMyDrillRunsLazyQuery
>;
export type GetMyDrillRunsSuspenseQueryHookResult = ReturnType<
typeof useGetMyDrillRunsSuspenseQuery
>;
export type GetMyDrillRunsQueryResult = Apollo.QueryResult<
GetMyDrillRunsQuery,
GetMyDrillRunsQueryVariables
>;
export const GetVideoMakePercentageIntervalsDocument = gql`
query GetVideoMakePercentageIntervals(
$videoId: ID!

View File

@@ -30,3 +30,56 @@ query GetRunsLeaderboard($interval: TimeInterval, $when: DateTime) {
}
}
}
query GetDrillRunLeaderboard(
$drillTag: String!
$interval: TimeInterval
$limit: Int = 50
$tableSizeMin: Float
$tableSizeMax: Float
$pocketSizeMin: Float
$pocketSizeMax: Float
) {
getDrillRunLeaderboard(
drillTag: $drillTag
interval: $interval
limit: $limit
tableSizeMin: $tableSizeMin
tableSizeMax: $tableSizeMax
pocketSizeMin: $pocketSizeMin
pocketSizeMax: $pocketSizeMax
) {
entries {
id
runLength
videoId
video {
tableSize
pocketSize
}
user {
id
username
profileImageUri
}
}
youRun {
id
runLength
videoId
}
youRank
totalPlayers
}
}
query GetMyDrillRuns($drillTag: String!, $limit: Int = 50) {
getMyDrillRuns(drillTag: $drillTag, limit: $limit) {
id
runLength
videoId
video {
createdAt
}
}
}

View File

@@ -9,6 +9,12 @@ fragment PlayerSummaryFields on PlayerSummaryGQL {
makePercentage
score
longestRun
runLengths
spinTypeBreakdown {
draw
center
follow
}
averageDifficulty
averageTimeBetweenShots
}

View File

@@ -113,10 +113,8 @@ fragment UploadStreamWithDetails on VideoGQL {
name
startTime
stream {
id
isCompleted
lastIntendedSegmentBound
streamSegmentType
uploadCompletionCursor
uploadsCompleted
}

View File

@@ -31,6 +31,16 @@ type Query {
limit: Int! = 50
requiredTags: [String!] = null
): RunLeaderboardGQL!
getDrillRunLeaderboard(
drillTag: String!
interval: TimeInterval = null
limit: Int! = 50
tableSizeMin: Float = null
tableSizeMax: Float = null
pocketSizeMin: Float = null
pocketSizeMax: Float = null
): DrillRunLeaderboardGQL!
getMyDrillRuns(drillTag: String!, limit: Int! = 50): [RunGQL!]!
getMakesLeaderboard(
interval: TimeInterval = null
when: DateTime = null
@@ -98,6 +108,7 @@ type Query {
): UserRelationshipsResult!
getAvailableSubscriptionOptions: StripeSubscriptionOptionsGQL!
getUserSubscriptionStatus: UserSubscriptionStatusGQL!
getAppleAppAccountToken: String!
getQuotaStatus: QuotaStatusGQL!
getPlayTime(userId: Int!, filters: VideoFilterInput = null): UserPlayTimeGQL!
getUserVideos(
@@ -677,10 +688,18 @@ type PlayerSummaryGQL {
makePercentage: Float!
score: Int
longestRun: Int!
runLengths: [Int!]!
spinTypeBreakdown: SpinTypeBreakdownGQL!
averageDifficulty: Float
averageTimeBetweenShots: Float
}
type SpinTypeBreakdownGQL {
draw: Int!
center: Int!
follow: Int!
}
type DeployedConfigGQL {
allowNewUsers: Boolean!
firebase: Boolean!
@@ -749,6 +768,13 @@ type RunLeaderboardGQL {
entries: [RunGQL!]!
}
type DrillRunLeaderboardGQL {
entries: [RunGQL!]!
youRun: RunGQL
youRank: Int
totalPlayers: Int!
}
type CountLeaderboardGQL {
entries: [UserShotCountEntry!]!
}
@@ -983,6 +1009,7 @@ type UserRelationship {
type StripeSubscriptionOptionsGQL {
products: [StripeProductGQL!]!
trialPeriodDays: Int
appleIap: AppleIapSubscriptionOptionsGQL!
}
type StripeProductGQL {
@@ -1003,6 +1030,11 @@ type StripePriceGQL {
active: Boolean!
}
type AppleIapSubscriptionOptionsGQL {
enabled: Boolean!
proMonthlyProductId: String
}
type UserSubscriptionStatusGQL {
hasActiveSubscription: Boolean!
entitlementSource: EntitlementSourceTypeEnum
@@ -1020,6 +1052,7 @@ enum EntitlementSourceTypeEnum {
ADMIN
MANUAL
STRIPE
APPLE
ALPHA_LEGACY
}
@@ -1175,6 +1208,9 @@ type Mutation {
retireTags(tagIds: [Int!]!): Boolean!
ensureStripeCustomerExists: UserGQL!
deleteUser: Boolean!
syncAppleSubscription(
input: SyncAppleSubscriptionInputGQL!
): SyncAppleSubscriptionResultGQL!
createSubscription(priceId: String!): CreateSubscriptionResultGQL!
createCustomerPortalSession: CreateCustomerPortalSessionResultGQL!
cancelSubscription: UserSubscriptionStatusGQL!
@@ -1328,6 +1364,26 @@ input EditUserInputGQL {
agreesToMarketing: Boolean = null
}
type SyncAppleSubscriptionResultGQL {
ok: Boolean!
errorCode: String
errorMessage: String
hasActiveSubscription: Boolean!
entitlementSource: EntitlementSourceTypeEnum
entitlementStartsAt: DateTime
entitlementEndsAt: DateTime
appleStatus: Int
originalTransactionId: String
latestTransactionId: String
productId: String
expiresAt: DateTime
}
input SyncAppleSubscriptionInputGQL {
signedTransactionInfo: String!
signedRenewalInfo: String = null
}
type CreateSubscriptionResultGQL {
checkoutUrl: String!
sessionId: String!