Compare commits

...

14 Commits

Author SHA1 Message Date
bf21a314f7 Add Apple IAP subscription options schema
All checks were successful
Tests / Tests (pull_request) Successful in 9s
2026-06-15 18:18:52 -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
7cef75b2ca Merge pull request 'dean/get-video-card-query' (#250) from dean/get-video-card-query into master
Reviewed-on: #250
2026-06-09 05:05:52 +00:00
Dean Wenstrand
6d5cd9b1ed Add lean GetLastSessionDate + GetShotClipRanges queries
All checks were successful
Tests / Tests (pull_request) Successful in 24s
- GetLastSessionDate: most-recent session startTime only, for the Home
  recency nudge (was pulling the full VideoCardFields payload).
- GetShotClipRanges / ShotClipRange: per-shot frame/time window only, for
  the inline session player's condensed playback (was pulling
  ShotWithAllFeatures — features, serialized paths, annotations — x500).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 14:27:42 -07:00
Dean Wenstrand
d59e21c10e Add GetVideoCard query (single-video VideoCardFields)
Returns the full VideoCardFields fragment for one video id, so the
session-detail meta header can share one source of truth (and the
normalized Apollo cache) with the feed card instead of stitching
together GetVideoDetails + GetVideoSocialDetailsById.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 14:04:33 -07:00
9358205327 Merge pull request 'Add quotaEnforcementEnabled to deployed config' (#249) from loewy/expose-quota-enforcement-config into master
Reviewed-on: #249
2026-05-28 23:18:01 +00:00
88634a32e9 add quotaEnforcementEnabled to deployed config
All checks were successful
Tests / Tests (pull_request) Successful in 10s
2026-05-28 14:52:51 -07:00
f7200a2e9f Merge pull request 'Add upload quota status operation' (#248) from loewy/quota-status-buckets into master
Reviewed-on: #248
2026-05-28 21:46:56 +00:00
7d839c0fa6 Add upload quota status operation
All checks were successful
Tests / Tests (pull_request) Successful in 10s
2026-05-27 16:52:43 -07:00
ab1e604871 Merge pull request 'Add quota bucket status fields' (#247) from loewy/quota-status-buckets into master
Reviewed-on: #247
2026-05-26 02:47:40 +00: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
8 changed files with 927 additions and 0 deletions

View File

@@ -79,6 +79,12 @@ export enum AlignedIntervalEnum {
Year = "YEAR", Year = "YEAR",
} }
export type AppleIapSubscriptionOptionsGql = {
__typename?: "AppleIapSubscriptionOptionsGQL";
enabled: Scalars["Boolean"]["output"];
proMonthlyProductId?: Maybe<Scalars["String"]["output"]>;
};
export type BankFeaturesGql = { export type BankFeaturesGql = {
__typename?: "BankFeaturesGQL"; __typename?: "BankFeaturesGQL";
bankAngle: Scalars["Float"]["output"]; bankAngle: Scalars["Float"]["output"];
@@ -290,6 +296,7 @@ export type DeployedConfigGql = {
environment: Scalars["String"]["output"]; environment: Scalars["String"]["output"];
firebase: Scalars["Boolean"]["output"]; firebase: Scalars["Boolean"]["output"];
minimumAllowedAppVersion: Scalars["String"]["output"]; minimumAllowedAppVersion: Scalars["String"]["output"];
quotaEnforcementEnabled: Scalars["Boolean"]["output"];
subscriptionGatingEnabled: Scalars["Boolean"]["output"]; subscriptionGatingEnabled: Scalars["Boolean"]["output"];
}; };
@@ -303,6 +310,14 @@ export type DoesNotOwnShotErrOtherErrorNeedsNote =
| DoesNotOwnShotErr | DoesNotOwnShotErr
| OtherErrorNeedsNote; | OtherErrorNeedsNote;
export type DrillRunLeaderboardGql = {
__typename?: "DrillRunLeaderboardGQL";
entries: Array<RunGql>;
totalPlayers: Scalars["Int"]["output"];
youRank?: Maybe<Scalars["Int"]["output"]>;
youRun?: Maybe<RunGql>;
};
export type EditShotReturn = { export type EditShotReturn = {
__typename?: "EditShotReturn"; __typename?: "EditShotReturn";
error?: Maybe<DoesNotOwnShotErr>; error?: Maybe<DoesNotOwnShotErr>;
@@ -330,6 +345,7 @@ export type EditableShotFieldInputGql = {
export enum EntitlementSourceTypeEnum { export enum EntitlementSourceTypeEnum {
Admin = "ADMIN", Admin = "ADMIN",
AlphaLegacy = "ALPHA_LEGACY", AlphaLegacy = "ALPHA_LEGACY",
Apple = "APPLE",
Manual = "MANUAL", Manual = "MANUAL",
Stripe = "STRIPE", Stripe = "STRIPE",
} }
@@ -2428,6 +2444,7 @@ export type Mutation = {
startChallenge: ChallengeEntry; startChallenge: ChallengeEntry;
submitCancellationFeedback: Scalars["Boolean"]["output"]; submitCancellationFeedback: Scalars["Boolean"]["output"];
submitChallengeEntry: ChallengeEntry; submitChallengeEntry: ChallengeEntry;
syncAppleSubscription: SyncAppleSubscriptionResultGql;
undismissChallenge: Scalars["Boolean"]["output"]; undismissChallenge: Scalars["Boolean"]["output"];
unfollowUser: UserGql; unfollowUser: UserGql;
updateShotAnnotations: UpdateShotAnnotationReturn; updateShotAnnotations: UpdateShotAnnotationReturn;
@@ -2631,6 +2648,10 @@ export type MutationSubmitChallengeEntryArgs = {
videoId: Scalars["ID"]["input"]; videoId: Scalars["ID"]["input"];
}; };
export type MutationSyncAppleSubscriptionArgs = {
input: SyncAppleSubscriptionInputGql;
};
export type MutationUndismissChallengeArgs = { export type MutationUndismissChallengeArgs = {
challengeId: Scalars["ID"]["input"]; challengeId: Scalars["ID"]["input"];
}; };
@@ -2813,9 +2834,11 @@ export type Query = {
challenges: Array<Challenge>; challenges: Array<Challenge>;
doesUsernameExist: Scalars["Boolean"]["output"]; doesUsernameExist: Scalars["Boolean"]["output"];
getAggregatedShotMetrics: Array<AggregateResultGql>; getAggregatedShotMetrics: Array<AggregateResultGql>;
getAppleAppAccountToken: Scalars["String"]["output"];
getAvailableSubscriptionOptions: StripeSubscriptionOptionsGql; getAvailableSubscriptionOptions: StripeSubscriptionOptionsGql;
getBucketSet?: Maybe<BucketSetGql>; getBucketSet?: Maybe<BucketSetGql>;
getDeployedConfig: DeployedConfigGql; getDeployedConfig: DeployedConfigGql;
getDrillRunLeaderboard: DrillRunLeaderboardGql;
getFeedVideos: VideoHistoryGql; getFeedVideos: VideoHistoryGql;
getGameTypeTagMetrics: Array<GameTypeTagMetric>; getGameTypeTagMetrics: Array<GameTypeTagMetric>;
getLoggedInUser?: Maybe<UserGql>; getLoggedInUser?: Maybe<UserGql>;
@@ -2877,6 +2900,12 @@ export type QueryGetBucketSetArgs = {
keyName: Scalars["String"]["input"]; keyName: Scalars["String"]["input"];
}; };
export type QueryGetDrillRunLeaderboardArgs = {
drillTag: Scalars["String"]["input"];
interval?: InputMaybe<TimeInterval>;
limit?: Scalars["Int"]["input"];
};
export type QueryGetFeedVideosArgs = { export type QueryGetFeedVideosArgs = {
after?: InputMaybe<Scalars["String"]["input"]>; after?: InputMaybe<Scalars["String"]["input"]>;
feedInput?: InputMaybe<VideoFeedInputGql>; feedInput?: InputMaybe<VideoFeedInputGql>;
@@ -3023,15 +3052,29 @@ export type QueryWaitForArgs = {
duration: Scalars["Float"]["input"]; duration: Scalars["Float"]["input"];
}; };
export type QuotaBucketStatusGql = {
__typename?: "QuotaBucketStatusGQL";
appliesToUploadKind: Scalars["String"]["output"];
canUpload: Scalars["Boolean"]["output"];
durationLimitSeconds?: Maybe<Scalars["Int"]["output"]>;
durationRemainingSeconds?: Maybe<Scalars["Float"]["output"]>;
durationUsedSeconds: Scalars["Float"]["output"];
periodEnd: Scalars["DateTime"]["output"];
periodStart: Scalars["DateTime"]["output"];
quotaKey: Scalars["String"]["output"];
};
export type QuotaStatusGql = { export type QuotaStatusGql = {
__typename?: "QuotaStatusGQL"; __typename?: "QuotaStatusGQL";
canUpload: Scalars["Boolean"]["output"]; canUpload: Scalars["Boolean"]["output"];
durationLimitSeconds?: Maybe<Scalars["Int"]["output"]>; durationLimitSeconds?: Maybe<Scalars["Int"]["output"]>;
durationRemainingSeconds?: Maybe<Scalars["Float"]["output"]>; durationRemainingSeconds?: Maybe<Scalars["Float"]["output"]>;
durationUsedSeconds: Scalars["Float"]["output"]; durationUsedSeconds: Scalars["Float"]["output"];
importQuotaBuckets: Array<QuotaBucketStatusGql>;
maxVideoDurationSeconds?: Maybe<Scalars["Int"]["output"]>; maxVideoDurationSeconds?: Maybe<Scalars["Int"]["output"]>;
periodEnd: Scalars["DateTime"]["output"]; periodEnd: Scalars["DateTime"]["output"];
periodStart: Scalars["DateTime"]["output"]; periodStart: Scalars["DateTime"]["output"];
recordingQuotaBuckets: Array<QuotaBucketStatusGql>;
tierName: Scalars["String"]["output"]; tierName: Scalars["String"]["output"];
}; };
@@ -3338,6 +3381,7 @@ export type StripeProductGql = {
export type StripeSubscriptionOptionsGql = { export type StripeSubscriptionOptionsGql = {
__typename?: "StripeSubscriptionOptionsGQL"; __typename?: "StripeSubscriptionOptionsGQL";
appleIap: AppleIapSubscriptionOptionsGql;
products: Array<StripeProductGql>; products: Array<StripeProductGql>;
trialPeriodDays?: Maybe<Scalars["Int"]["output"]>; trialPeriodDays?: Maybe<Scalars["Int"]["output"]>;
}; };
@@ -3362,6 +3406,27 @@ export type SuccessfulAddAddShotAnnotationErrors =
| AddShotAnnotationErrors | AddShotAnnotationErrors
| SuccessfulAdd; | 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 = { export type TableStateGql = {
__typename?: "TableStateGQL"; __typename?: "TableStateGQL";
homography?: Maybe<HomographyInfoGql>; homography?: Maybe<HomographyInfoGql>;
@@ -4119,6 +4184,7 @@ export type GetDeployedConfigQuery = {
firebase: boolean; firebase: boolean;
minimumAllowedAppVersion: string; minimumAllowedAppVersion: string;
subscriptionGatingEnabled: boolean; subscriptionGatingEnabled: boolean;
quotaEnforcementEnabled: boolean;
defaultAndroidRecordingFormat: StreamSegmentTypeEnum; defaultAndroidRecordingFormat: StreamSegmentTypeEnum;
bucketUrl: string; bucketUrl: string;
bannerMessages: Array<{ bannerMessages: Array<{
@@ -4377,6 +4443,24 @@ export type GetVideoFeedSessionCountQuery = {
}; };
}; };
export type GetLastSessionDateQueryVariables = Exact<{
filters?: InputMaybe<VideoFilterInput>;
includePrivate?: InputMaybe<IncludePrivateEnum>;
feedInput?: InputMaybe<VideoFeedInputGql>;
}>;
export type GetLastSessionDateQuery = {
__typename?: "Query";
getFeedVideos: {
__typename?: "VideoHistoryGQL";
videos: Array<{
__typename?: "VideoGQL";
id: number;
startTime?: any | null;
}>;
};
};
export type GetVideoFeedQueryVariables = Exact<{ export type GetVideoFeedQueryVariables = Exact<{
limit?: Scalars["Int"]["input"]; limit?: Scalars["Int"]["input"];
after?: InputMaybe<Scalars["String"]["input"]>; after?: InputMaybe<Scalars["String"]["input"]>;
@@ -4539,6 +4623,39 @@ export type GetRunsLeaderboardQuery = {
}; };
}; };
export type GetDrillRunLeaderboardQueryVariables = Exact<{
drillTag: Scalars["String"]["input"];
interval?: InputMaybe<TimeInterval>;
limit?: InputMaybe<Scalars["Int"]["input"]>;
}>;
export type GetDrillRunLeaderboardQuery = {
__typename?: "Query";
getDrillRunLeaderboard: {
__typename?: "DrillRunLeaderboardGQL";
youRank?: number | null;
totalPlayers: number;
entries: Array<{
__typename?: "RunGQL";
id: number;
runLength: number;
videoId: number;
user: {
__typename?: "UserGQL";
id: number;
username: string;
profileImageUri?: string | null;
};
}>;
youRun?: {
__typename?: "RunGQL";
id: number;
runLength: number;
videoId: number;
} | null;
};
};
export type GetVideoMakePercentageIntervalsQueryVariables = Exact<{ export type GetVideoMakePercentageIntervalsQueryVariables = Exact<{
videoId: Scalars["ID"]["input"]; videoId: Scalars["ID"]["input"];
intervalDuration: Scalars["Int"]["input"]; intervalDuration: Scalars["Int"]["input"];
@@ -5462,6 +5579,39 @@ export type GetShotsByIdsQuery = {
}>; }>;
}; };
export type ShotClipRangeFragment = {
__typename?: "ShotGQL";
id: number;
videoId: number;
startFrame: number;
endFrame: number;
startTime: number;
endTime: number;
};
export type GetShotClipRangesQueryVariables = Exact<{
filterInput: FilterInput;
shotsOrdering?: InputMaybe<GetShotsOrdering>;
limit?: InputMaybe<Scalars["Int"]["input"]>;
}>;
export type GetShotClipRangesQuery = {
__typename?: "Query";
getOrderedShots: {
__typename?: "GetShotsResult";
count?: number | null;
shots: Array<{
__typename?: "ShotGQL";
id: number;
videoId: number;
startFrame: number;
endFrame: number;
startTime: number;
endTime: number;
}>;
};
};
export type ShotWithAllFeaturesFragment = { export type ShotWithAllFeaturesFragment = {
__typename?: "ShotGQL"; __typename?: "ShotGQL";
id: number; id: number;
@@ -5733,6 +5883,47 @@ export type GetUserPlayTimeQuery = {
getPlayTime: { __typename?: "UserPlayTimeGQL"; totalSeconds: number }; getPlayTime: { __typename?: "UserPlayTimeGQL"; totalSeconds: number };
}; };
export type GetUploadQuotaStatusQueryVariables = Exact<{
[key: string]: never;
}>;
export type GetUploadQuotaStatusQuery = {
__typename?: "Query";
getQuotaStatus: {
__typename?: "QuotaStatusGQL";
tierName: string;
periodStart: any;
periodEnd: any;
durationUsedSeconds: number;
durationLimitSeconds?: number | null;
maxVideoDurationSeconds?: number | null;
durationRemainingSeconds?: number | null;
canUpload: boolean;
importQuotaBuckets: Array<{
__typename?: "QuotaBucketStatusGQL";
quotaKey: string;
appliesToUploadKind: string;
periodStart: any;
periodEnd: any;
durationUsedSeconds: number;
durationLimitSeconds?: number | null;
durationRemainingSeconds?: number | null;
canUpload: boolean;
}>;
recordingQuotaBuckets: Array<{
__typename?: "QuotaBucketStatusGQL";
quotaKey: string;
appliesToUploadKind: string;
periodStart: any;
periodEnd: any;
durationUsedSeconds: number;
durationLimitSeconds?: number | null;
durationRemainingSeconds?: number | null;
canUpload: boolean;
}>;
};
};
export type GetUsernamesQueryVariables = Exact<{ export type GetUsernamesQueryVariables = Exact<{
matchString: Scalars["String"]["input"]; matchString: Scalars["String"]["input"];
limit?: InputMaybe<Scalars["Int"]["input"]>; limit?: InputMaybe<Scalars["Int"]["input"]>;
@@ -6140,6 +6331,102 @@ export type GetVideoSocialDetailsByIdQuery = {
}; };
}; };
export type GetVideoCardQueryVariables = Exact<{
videoId: Scalars["Int"]["input"];
}>;
export type GetVideoCardQuery = {
__typename?: "Query";
getVideo: {
__typename?: "VideoGQL";
id: number;
name?: string | null;
screenshotUri?: string | null;
totalShots: number;
makePercentage: number;
averageTimeBetweenShots?: number | null;
averageDifficulty?: number | null;
startTime?: any | null;
private: boolean;
elapsedTime?: number | null;
tableSize: number;
pocketSize?: number | null;
owner?: {
__typename?: "UserGQL";
id: number;
username: string;
profileImageUri?: string | null;
} | null;
stream?: {
__typename?: "UploadStreamGQL";
id: string;
lastIntendedSegmentBound?: number | null;
streamSegmentType: StreamSegmentTypeEnum;
} | null;
tags: Array<{
__typename?: "VideoTag";
name: string;
tagClasses: Array<{ __typename?: "VideoTagClass"; name: string }>;
}>;
playerSummaries: Array<{
__typename?: "PlayerSummaryGQL";
clusterId: number;
userId?: number | null;
username?: string | null;
profileImageUri?: string | null;
representativeFullFrameUrl?: string | null;
totalShots: number;
totalShotsMade: number;
makePercentage: number;
score?: number | null;
longestRun: number;
averageDifficulty?: number | null;
averageTimeBetweenShots?: number | null;
}>;
currentProcessing?: {
__typename?: "VideoProcessingGQL";
id: number;
status: ProcessingStatusEnum;
} | null;
reactions: Array<{
__typename?: "ReactionGQL";
videoId: number;
reaction: ReactionEnum;
user: {
__typename?: "UserGQL";
id: number;
username: string;
profileImageUri?: string | null;
isFollowedByCurrentUser?: boolean | null;
};
}>;
comments: Array<{
__typename?: "CommentGQL";
id: number;
message: string;
user: {
__typename?: "UserGQL";
id: number;
username: string;
profileImageUri?: string | null;
isFollowedByCurrentUser?: boolean | null;
};
replies: Array<{
__typename?: "CommentGQL";
id: number;
message: string;
user: {
__typename?: "UserGQL";
id: number;
username: string;
profileImageUri?: string | null;
isFollowedByCurrentUser?: boolean | null;
};
}>;
}>;
};
};
export type GetVideosQueryVariables = Exact<{ export type GetVideosQueryVariables = Exact<{
videoIds: Array<Scalars["Int"]["input"]> | Scalars["Int"]["input"]; videoIds: Array<Scalars["Int"]["input"]> | Scalars["Int"]["input"];
}>; }>;
@@ -6892,6 +7179,16 @@ export const PlayerClusterFieldsFragmentDoc = gql`
} }
${PlayerClusterShotFieldsFragmentDoc} ${PlayerClusterShotFieldsFragmentDoc}
`; `;
export const ShotClipRangeFragmentDoc = gql`
fragment ShotClipRange on ShotGQL {
id
videoId
startFrame
endFrame
startTime @client
endTime @client
}
`;
export const ShotWithAllFeaturesFragmentDoc = gql` export const ShotWithAllFeaturesFragmentDoc = gql`
fragment ShotWithAllFeatures on ShotGQL { fragment ShotWithAllFeatures on ShotGQL {
id id
@@ -8577,6 +8874,7 @@ export const GetDeployedConfigDocument = gql`
firebase firebase
minimumAllowedAppVersion minimumAllowedAppVersion
subscriptionGatingEnabled subscriptionGatingEnabled
quotaEnforcementEnabled
bannerMessages { bannerMessages {
color color
dismissible dismissible
@@ -8976,6 +9274,93 @@ export type GetVideoFeedSessionCountQueryResult = Apollo.QueryResult<
GetVideoFeedSessionCountQuery, GetVideoFeedSessionCountQuery,
GetVideoFeedSessionCountQueryVariables GetVideoFeedSessionCountQueryVariables
>; >;
export const GetLastSessionDateDocument = gql`
query GetLastSessionDate(
$filters: VideoFilterInput = null
$includePrivate: IncludePrivateEnum = MINE
$feedInput: VideoFeedInputGQL = null
) {
getFeedVideos(
limit: 1
filters: $filters
includePrivate: $includePrivate
feedInput: $feedInput
) {
videos {
id
startTime
}
}
}
`;
/**
* __useGetLastSessionDateQuery__
*
* To run a query within a React component, call `useGetLastSessionDateQuery` and pass it any options that fit your needs.
* When your component renders, `useGetLastSessionDateQuery` 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 } = useGetLastSessionDateQuery({
* variables: {
* filters: // value for 'filters'
* includePrivate: // value for 'includePrivate'
* feedInput: // value for 'feedInput'
* },
* });
*/
export function useGetLastSessionDateQuery(
baseOptions?: Apollo.QueryHookOptions<
GetLastSessionDateQuery,
GetLastSessionDateQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<
GetLastSessionDateQuery,
GetLastSessionDateQueryVariables
>(GetLastSessionDateDocument, options);
}
export function useGetLastSessionDateLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
GetLastSessionDateQuery,
GetLastSessionDateQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<
GetLastSessionDateQuery,
GetLastSessionDateQueryVariables
>(GetLastSessionDateDocument, options);
}
export function useGetLastSessionDateSuspenseQuery(
baseOptions?: Apollo.SuspenseQueryHookOptions<
GetLastSessionDateQuery,
GetLastSessionDateQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useSuspenseQuery<
GetLastSessionDateQuery,
GetLastSessionDateQueryVariables
>(GetLastSessionDateDocument, options);
}
export type GetLastSessionDateQueryHookResult = ReturnType<
typeof useGetLastSessionDateQuery
>;
export type GetLastSessionDateLazyQueryHookResult = ReturnType<
typeof useGetLastSessionDateLazyQuery
>;
export type GetLastSessionDateSuspenseQueryHookResult = ReturnType<
typeof useGetLastSessionDateSuspenseQuery
>;
export type GetLastSessionDateQueryResult = Apollo.QueryResult<
GetLastSessionDateQuery,
GetLastSessionDateQueryVariables
>;
export const GetVideoFeedDocument = gql` export const GetVideoFeedDocument = gql`
query GetVideoFeed( query GetVideoFeed(
$limit: Int! = 5 $limit: Int! = 5
@@ -9245,6 +9630,105 @@ export type GetRunsLeaderboardQueryResult = Apollo.QueryResult<
GetRunsLeaderboardQuery, GetRunsLeaderboardQuery,
GetRunsLeaderboardQueryVariables GetRunsLeaderboardQueryVariables
>; >;
export const GetDrillRunLeaderboardDocument = gql`
query GetDrillRunLeaderboard(
$drillTag: String!
$interval: TimeInterval
$limit: Int = 50
) {
getDrillRunLeaderboard(
drillTag: $drillTag
interval: $interval
limit: $limit
) {
entries {
id
runLength
videoId
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'
* },
* });
*/
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 GetVideoMakePercentageIntervalsDocument = gql` export const GetVideoMakePercentageIntervalsDocument = gql`
query GetVideoMakePercentageIntervals( query GetVideoMakePercentageIntervals(
$videoId: ID! $videoId: ID!
@@ -11279,6 +11763,93 @@ export type GetShotsByIdsQueryResult = Apollo.QueryResult<
GetShotsByIdsQuery, GetShotsByIdsQuery,
GetShotsByIdsQueryVariables GetShotsByIdsQueryVariables
>; >;
export const GetShotClipRangesDocument = gql`
query GetShotClipRanges(
$filterInput: FilterInput!
$shotsOrdering: GetShotsOrdering
$limit: Int
) {
getOrderedShots(
filterInput: $filterInput
shotsOrdering: $shotsOrdering
limit: $limit
) {
count
shots {
...ShotClipRange
}
}
}
${ShotClipRangeFragmentDoc}
`;
/**
* __useGetShotClipRangesQuery__
*
* To run a query within a React component, call `useGetShotClipRangesQuery` and pass it any options that fit your needs.
* When your component renders, `useGetShotClipRangesQuery` 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 } = useGetShotClipRangesQuery({
* variables: {
* filterInput: // value for 'filterInput'
* shotsOrdering: // value for 'shotsOrdering'
* limit: // value for 'limit'
* },
* });
*/
export function useGetShotClipRangesQuery(
baseOptions: Apollo.QueryHookOptions<
GetShotClipRangesQuery,
GetShotClipRangesQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<
GetShotClipRangesQuery,
GetShotClipRangesQueryVariables
>(GetShotClipRangesDocument, options);
}
export function useGetShotClipRangesLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
GetShotClipRangesQuery,
GetShotClipRangesQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<
GetShotClipRangesQuery,
GetShotClipRangesQueryVariables
>(GetShotClipRangesDocument, options);
}
export function useGetShotClipRangesSuspenseQuery(
baseOptions?: Apollo.SuspenseQueryHookOptions<
GetShotClipRangesQuery,
GetShotClipRangesQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useSuspenseQuery<
GetShotClipRangesQuery,
GetShotClipRangesQueryVariables
>(GetShotClipRangesDocument, options);
}
export type GetShotClipRangesQueryHookResult = ReturnType<
typeof useGetShotClipRangesQuery
>;
export type GetShotClipRangesLazyQueryHookResult = ReturnType<
typeof useGetShotClipRangesLazyQuery
>;
export type GetShotClipRangesSuspenseQueryHookResult = ReturnType<
typeof useGetShotClipRangesSuspenseQuery
>;
export type GetShotClipRangesQueryResult = Apollo.QueryResult<
GetShotClipRangesQuery,
GetShotClipRangesQueryVariables
>;
export const EditShotDocument = gql` export const EditShotDocument = gql`
mutation EditShot($shotId: Int!, $fieldsToEdit: EditableShotFieldInputGQL!) { mutation EditShot($shotId: Int!, $fieldsToEdit: EditableShotFieldInputGQL!) {
editShot(shotId: $shotId, fieldsToEdit: $fieldsToEdit) { editShot(shotId: $shotId, fieldsToEdit: $fieldsToEdit) {
@@ -11768,6 +12339,105 @@ export type GetUserPlayTimeQueryResult = Apollo.QueryResult<
GetUserPlayTimeQuery, GetUserPlayTimeQuery,
GetUserPlayTimeQueryVariables GetUserPlayTimeQueryVariables
>; >;
export const GetUploadQuotaStatusDocument = gql`
query GetUploadQuotaStatus {
getQuotaStatus {
tierName
periodStart
periodEnd
durationUsedSeconds
durationLimitSeconds
maxVideoDurationSeconds
durationRemainingSeconds
canUpload
importQuotaBuckets {
quotaKey
appliesToUploadKind
periodStart
periodEnd
durationUsedSeconds
durationLimitSeconds
durationRemainingSeconds
canUpload
}
recordingQuotaBuckets {
quotaKey
appliesToUploadKind
periodStart
periodEnd
durationUsedSeconds
durationLimitSeconds
durationRemainingSeconds
canUpload
}
}
}
`;
/**
* __useGetUploadQuotaStatusQuery__
*
* To run a query within a React component, call `useGetUploadQuotaStatusQuery` and pass it any options that fit your needs.
* When your component renders, `useGetUploadQuotaStatusQuery` 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 } = useGetUploadQuotaStatusQuery({
* variables: {
* },
* });
*/
export function useGetUploadQuotaStatusQuery(
baseOptions?: Apollo.QueryHookOptions<
GetUploadQuotaStatusQuery,
GetUploadQuotaStatusQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<
GetUploadQuotaStatusQuery,
GetUploadQuotaStatusQueryVariables
>(GetUploadQuotaStatusDocument, options);
}
export function useGetUploadQuotaStatusLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
GetUploadQuotaStatusQuery,
GetUploadQuotaStatusQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<
GetUploadQuotaStatusQuery,
GetUploadQuotaStatusQueryVariables
>(GetUploadQuotaStatusDocument, options);
}
export function useGetUploadQuotaStatusSuspenseQuery(
baseOptions?: Apollo.SuspenseQueryHookOptions<
GetUploadQuotaStatusQuery,
GetUploadQuotaStatusQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useSuspenseQuery<
GetUploadQuotaStatusQuery,
GetUploadQuotaStatusQueryVariables
>(GetUploadQuotaStatusDocument, options);
}
export type GetUploadQuotaStatusQueryHookResult = ReturnType<
typeof useGetUploadQuotaStatusQuery
>;
export type GetUploadQuotaStatusLazyQueryHookResult = ReturnType<
typeof useGetUploadQuotaStatusLazyQuery
>;
export type GetUploadQuotaStatusSuspenseQueryHookResult = ReturnType<
typeof useGetUploadQuotaStatusSuspenseQuery
>;
export type GetUploadQuotaStatusQueryResult = Apollo.QueryResult<
GetUploadQuotaStatusQuery,
GetUploadQuotaStatusQueryVariables
>;
export const GetUsernamesDocument = gql` export const GetUsernamesDocument = gql`
query getUsernames( query getUsernames(
$matchString: String! $matchString: String!
@@ -12994,6 +13664,80 @@ export type GetVideoSocialDetailsByIdQueryResult = Apollo.QueryResult<
GetVideoSocialDetailsByIdQuery, GetVideoSocialDetailsByIdQuery,
GetVideoSocialDetailsByIdQueryVariables GetVideoSocialDetailsByIdQueryVariables
>; >;
export const GetVideoCardDocument = gql`
query GetVideoCard($videoId: Int!) {
getVideo(videoId: $videoId) {
...VideoCardFields
}
}
${VideoCardFieldsFragmentDoc}
`;
/**
* __useGetVideoCardQuery__
*
* To run a query within a React component, call `useGetVideoCardQuery` and pass it any options that fit your needs.
* When your component renders, `useGetVideoCardQuery` 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 } = useGetVideoCardQuery({
* variables: {
* videoId: // value for 'videoId'
* },
* });
*/
export function useGetVideoCardQuery(
baseOptions: Apollo.QueryHookOptions<
GetVideoCardQuery,
GetVideoCardQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<GetVideoCardQuery, GetVideoCardQueryVariables>(
GetVideoCardDocument,
options,
);
}
export function useGetVideoCardLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
GetVideoCardQuery,
GetVideoCardQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<GetVideoCardQuery, GetVideoCardQueryVariables>(
GetVideoCardDocument,
options,
);
}
export function useGetVideoCardSuspenseQuery(
baseOptions?: Apollo.SuspenseQueryHookOptions<
GetVideoCardQuery,
GetVideoCardQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useSuspenseQuery<GetVideoCardQuery, GetVideoCardQueryVariables>(
GetVideoCardDocument,
options,
);
}
export type GetVideoCardQueryHookResult = ReturnType<
typeof useGetVideoCardQuery
>;
export type GetVideoCardLazyQueryHookResult = ReturnType<
typeof useGetVideoCardLazyQuery
>;
export type GetVideoCardSuspenseQueryHookResult = ReturnType<
typeof useGetVideoCardSuspenseQuery
>;
export type GetVideoCardQueryResult = Apollo.QueryResult<
GetVideoCardQuery,
GetVideoCardQueryVariables
>;
export const GetVideosDocument = gql` export const GetVideosDocument = gql`
query GetVideos($videoIds: [Int!]!) { query GetVideos($videoIds: [Int!]!) {
getVideos(videoIds: $videoIds) { getVideos(videoIds: $videoIds) {

View File

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

View File

@@ -101,6 +101,27 @@ query GetVideoFeedSessionCount(
} }
} }
# Minimal query for the Home recency nudge ("you haven't recorded in N days").
# Only the most recent session's start time — avoids pulling the full
# VideoCardFields payload (reactions, comments, player summaries, etc.).
query GetLastSessionDate(
$filters: VideoFilterInput = null
$includePrivate: IncludePrivateEnum = MINE
$feedInput: VideoFeedInputGQL = null
) {
getFeedVideos(
limit: 1
filters: $filters
includePrivate: $includePrivate
feedInput: $feedInput
) {
videos {
id
startTime
}
}
}
query GetVideoFeed( query GetVideoFeed(
$limit: Int! = 5 $limit: Int! = 5
$after: String = null $after: String = null

View File

@@ -30,3 +30,33 @@ query GetRunsLeaderboard($interval: TimeInterval, $when: DateTime) {
} }
} }
} }
query GetDrillRunLeaderboard(
$drillTag: String!
$interval: TimeInterval
$limit: Int = 50
) {
getDrillRunLeaderboard(
drillTag: $drillTag
interval: $interval
limit: $limit
) {
entries {
id
runLength
videoId
user {
id
username
profileImageUri
}
}
youRun {
id
runLength
videoId
}
youRank
totalPlayers
}
}

View File

@@ -132,6 +132,38 @@ query GetShotsByIds($ids: [Int!]!) {
} }
} }
# Lightweight clip boundaries for condensed session playback. The inline
# session player only needs each shot's frame/time window to seek between
# shots — this skips the heavy ShotWithAllFeatures payload (cue/pocketing
# features, serialized shot paths, annotations, nested video/playlist). The
# startTime/endTime @client resolvers derive their values from the frame
# fields + the video (looked up internally), so this is all they require.
fragment ShotClipRange on ShotGQL {
id
videoId
startFrame
endFrame
startTime @client
endTime @client
}
query GetShotClipRanges(
$filterInput: FilterInput!
$shotsOrdering: GetShotsOrdering
$limit: Int
) {
getOrderedShots(
filterInput: $filterInput
shotsOrdering: $shotsOrdering
limit: $limit
) {
count
shots {
...ShotClipRange
}
}
}
fragment ShotWithAllFeatures on ShotGQL { fragment ShotWithAllFeatures on ShotGQL {
id id
videoId videoId

View File

@@ -49,6 +49,39 @@ query GetUserPlayTime($userId: Int!, $filters: VideoFilterInput) {
} }
} }
query GetUploadQuotaStatus {
getQuotaStatus {
tierName
periodStart
periodEnd
durationUsedSeconds
durationLimitSeconds
maxVideoDurationSeconds
durationRemainingSeconds
canUpload
importQuotaBuckets {
quotaKey
appliesToUploadKind
periodStart
periodEnd
durationUsedSeconds
durationLimitSeconds
durationRemainingSeconds
canUpload
}
recordingQuotaBuckets {
quotaKey
appliesToUploadKind
periodStart
periodEnd
durationUsedSeconds
durationLimitSeconds
durationRemainingSeconds
canUpload
}
}
}
query getUsernames( query getUsernames(
$matchString: String! $matchString: String!
$limit: Int = null $limit: Int = null

View File

@@ -139,6 +139,15 @@ query GetVideoSocialDetailsById($videoId: Int!) {
} }
} }
# Full card payload for a single video — reuses the same VideoCardFields
# fragment the feed list uses, so the session-detail meta header shares one
# source of truth (and the normalized Apollo cache) with the feed card.
query GetVideoCard($videoId: Int!) {
getVideo(videoId: $videoId) {
...VideoCardFields
}
}
query GetVideos($videoIds: [Int!]!) { query GetVideos($videoIds: [Int!]!) {
getVideos(videoIds: $videoIds) { getVideos(videoIds: $videoIds) {
...VideoStreamMetadata ...VideoStreamMetadata

View File

@@ -31,6 +31,11 @@ type Query {
limit: Int! = 50 limit: Int! = 50
requiredTags: [String!] = null requiredTags: [String!] = null
): RunLeaderboardGQL! ): RunLeaderboardGQL!
getDrillRunLeaderboard(
drillTag: String!
interval: TimeInterval = null
limit: Int! = 50
): DrillRunLeaderboardGQL!
getMakesLeaderboard( getMakesLeaderboard(
interval: TimeInterval = null interval: TimeInterval = null
when: DateTime = null when: DateTime = null
@@ -98,6 +103,7 @@ type Query {
): UserRelationshipsResult! ): UserRelationshipsResult!
getAvailableSubscriptionOptions: StripeSubscriptionOptionsGQL! getAvailableSubscriptionOptions: StripeSubscriptionOptionsGQL!
getUserSubscriptionStatus: UserSubscriptionStatusGQL! getUserSubscriptionStatus: UserSubscriptionStatusGQL!
getAppleAppAccountToken: String!
getQuotaStatus: QuotaStatusGQL! getQuotaStatus: QuotaStatusGQL!
getPlayTime(userId: Int!, filters: VideoFilterInput = null): UserPlayTimeGQL! getPlayTime(userId: Int!, filters: VideoFilterInput = null): UserPlayTimeGQL!
getUserVideos( getUserVideos(
@@ -688,6 +694,7 @@ type DeployedConfigGQL {
environment: String! environment: String!
minimumAllowedAppVersion: String! minimumAllowedAppVersion: String!
subscriptionGatingEnabled: Boolean! subscriptionGatingEnabled: Boolean!
quotaEnforcementEnabled: Boolean!
bannerMessages: [BannerGQL!]! bannerMessages: [BannerGQL!]!
defaultAndroidRecordingFormat: StreamSegmentTypeEnum! defaultAndroidRecordingFormat: StreamSegmentTypeEnum!
bucketUrl: String! bucketUrl: String!
@@ -748,6 +755,13 @@ type RunLeaderboardGQL {
entries: [RunGQL!]! entries: [RunGQL!]!
} }
type DrillRunLeaderboardGQL {
entries: [RunGQL!]!
youRun: RunGQL
youRank: Int
totalPlayers: Int!
}
type CountLeaderboardGQL { type CountLeaderboardGQL {
entries: [UserShotCountEntry!]! entries: [UserShotCountEntry!]!
} }
@@ -982,6 +996,7 @@ type UserRelationship {
type StripeSubscriptionOptionsGQL { type StripeSubscriptionOptionsGQL {
products: [StripeProductGQL!]! products: [StripeProductGQL!]!
trialPeriodDays: Int trialPeriodDays: Int
appleIap: AppleIapSubscriptionOptionsGQL!
} }
type StripeProductGQL { type StripeProductGQL {
@@ -1002,6 +1017,11 @@ type StripePriceGQL {
active: Boolean! active: Boolean!
} }
type AppleIapSubscriptionOptionsGQL {
enabled: Boolean!
proMonthlyProductId: String
}
type UserSubscriptionStatusGQL { type UserSubscriptionStatusGQL {
hasActiveSubscription: Boolean! hasActiveSubscription: Boolean!
entitlementSource: EntitlementSourceTypeEnum entitlementSource: EntitlementSourceTypeEnum
@@ -1019,6 +1039,7 @@ enum EntitlementSourceTypeEnum {
ADMIN ADMIN
MANUAL MANUAL
STRIPE STRIPE
APPLE
ALPHA_LEGACY ALPHA_LEGACY
} }
@@ -1040,6 +1061,19 @@ type QuotaStatusGQL {
durationUsedSeconds: Float! durationUsedSeconds: Float!
durationLimitSeconds: Int durationLimitSeconds: Int
maxVideoDurationSeconds: 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 durationRemainingSeconds: Float
canUpload: Boolean! canUpload: Boolean!
} }
@@ -1161,6 +1195,9 @@ type Mutation {
retireTags(tagIds: [Int!]!): Boolean! retireTags(tagIds: [Int!]!): Boolean!
ensureStripeCustomerExists: UserGQL! ensureStripeCustomerExists: UserGQL!
deleteUser: Boolean! deleteUser: Boolean!
syncAppleSubscription(
input: SyncAppleSubscriptionInputGQL!
): SyncAppleSubscriptionResultGQL!
createSubscription(priceId: String!): CreateSubscriptionResultGQL! createSubscription(priceId: String!): CreateSubscriptionResultGQL!
createCustomerPortalSession: CreateCustomerPortalSessionResultGQL! createCustomerPortalSession: CreateCustomerPortalSessionResultGQL!
cancelSubscription: UserSubscriptionStatusGQL! cancelSubscription: UserSubscriptionStatusGQL!
@@ -1314,6 +1351,26 @@ input EditUserInputGQL {
agreesToMarketing: Boolean = null 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 { type CreateSubscriptionResultGQL {
checkoutUrl: String! checkoutUrl: String!
sessionId: String! sessionId: String!