Compare commits

..

2 Commits

Author SHA1 Message Date
dean
63e1a7090a feat: Add auth handoff mutation operations
Adds GraphQL operations for:
- CreateAuthHandoffToken: Get handoff token for mobile-to-web auth
- ExchangeAuthHandoffToken: Exchange token for Firebase custom token

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-27 10:45:01 -08:00
dean
85c4ec6d40 feat: Add auth handoff mutations for mobile-to-web authentication
Adds GraphQL types and schema for:
- createAuthHandoffToken: Creates short-lived token for auth handoff
- exchangeAuthHandoffToken: Exchanges handoff token for Firebase custom token

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-27 10:44:38 -08:00
8 changed files with 5466 additions and 199 deletions

5423
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -79,6 +79,12 @@ export enum AlignedIntervalEnum {
Year = "YEAR", Year = "YEAR",
} }
export type AuthHandoffTokenGql = {
__typename?: "AuthHandoffTokenGQL";
expiresInSeconds: Scalars["Int"]["output"];
token: Scalars["String"]["output"];
};
export type BankFeaturesGql = { export type BankFeaturesGql = {
__typename?: "BankFeaturesGQL"; __typename?: "BankFeaturesGQL";
bankAngle: Scalars["Float"]["output"]; bankAngle: Scalars["Float"]["output"];
@@ -318,6 +324,12 @@ export type EnumAggregation = {
feature: Scalars["String"]["input"]; feature: Scalars["String"]["input"];
}; };
export type ExchangeAuthHandoffResultGql = {
__typename?: "ExchangeAuthHandoffResultGQL";
customToken: Scalars["String"]["output"];
userId: Scalars["Int"]["output"];
};
export type FilterInput = export type FilterInput =
| { | {
andFilters: Array<FilterInput>; andFilters: Array<FilterInput>;
@@ -2342,6 +2354,7 @@ export type Mutation = {
blockUser: Scalars["Boolean"]["output"]; blockUser: Scalars["Boolean"]["output"];
cancelSubscription: UserSubscriptionStatusGql; cancelSubscription: UserSubscriptionStatusGql;
commentOnVideo: Scalars["Boolean"]["output"]; commentOnVideo: Scalars["Boolean"]["output"];
createAuthHandoffToken: AuthHandoffTokenGql;
createBucketSet: BucketSetGql; createBucketSet: BucketSetGql;
createChallenge: Challenge; createChallenge: Challenge;
createRuleSet: RuleSet; createRuleSet: RuleSet;
@@ -2359,6 +2372,7 @@ export type Mutation = {
editUploadStream: Scalars["Boolean"]["output"]; editUploadStream: Scalars["Boolean"]["output"];
editUser: UserGql; editUser: UserGql;
ensureStripeCustomerExists: UserGql; ensureStripeCustomerExists: UserGql;
exchangeAuthHandoffToken: ExchangeAuthHandoffResultGql;
findPrerecordTableLayout?: Maybe<HomographyInfoGql>; findPrerecordTableLayout?: Maybe<HomographyInfoGql>;
followUser: UserGql; followUser: UserGql;
getHlsInitUploadLink: GetUploadLinkReturn; getHlsInitUploadLink: GetUploadLinkReturn;
@@ -2479,6 +2493,10 @@ export type MutationEditUserArgs = {
input: EditUserInputGql; input: EditUserInputGql;
}; };
export type MutationExchangeAuthHandoffTokenArgs = {
token: Scalars["String"]["input"];
};
export type MutationFindPrerecordTableLayoutArgs = { export type MutationFindPrerecordTableLayoutArgs = {
b64Image: Scalars["String"]["input"]; b64Image: Scalars["String"]["input"];
videoId: Scalars["Int"]["input"]; videoId: Scalars["Int"]["input"];
@@ -3557,7 +3575,6 @@ export type GetAggregatedShotMetricsQuery = {
__typename?: "TargetMetricsGQL"; __typename?: "TargetMetricsGQL";
count: number; count: number;
makePercentage?: number | null; makePercentage?: number | null;
averageDifficulty?: number | null;
}; };
}>; }>;
}; };
@@ -4040,7 +4057,6 @@ export type GetFeedQuery = {
private: boolean; private: boolean;
elapsedTime?: number | null; elapsedTime?: number | null;
tableSize: number; tableSize: number;
pocketSize?: number | null;
owner?: { owner?: {
__typename?: "UserGQL"; __typename?: "UserGQL";
id: number; id: number;
@@ -4142,7 +4158,6 @@ export type VideoCardFieldsFragment = {
private: boolean; private: boolean;
elapsedTime?: number | null; elapsedTime?: number | null;
tableSize: number; tableSize: number;
pocketSize?: number | null;
owner?: { owner?: {
__typename?: "UserGQL"; __typename?: "UserGQL";
id: number; id: number;
@@ -4240,7 +4255,6 @@ export type GetVideoFeedQuery = {
private: boolean; private: boolean;
elapsedTime?: number | null; elapsedTime?: number | null;
tableSize: number; tableSize: number;
pocketSize?: number | null;
owner?: { owner?: {
__typename?: "UserGQL"; __typename?: "UserGQL";
id: number; id: number;
@@ -4729,17 +4743,6 @@ export type CancelSubscriptionMutation = {
}; };
}; };
export type SubmitCancellationFeedbackMutationVariables = Exact<{
reasons?: InputMaybe<Array<CancellationReasonEnum> | CancellationReasonEnum>;
feedback?: InputMaybe<Scalars["String"]["input"]>;
metadata?: InputMaybe<CancellationFeedbackMetadataInput>;
}>;
export type SubmitCancellationFeedbackMutation = {
__typename?: "Mutation";
submitCancellationFeedback: boolean;
};
export type ReactToVideoMutationVariables = Exact<{ export type ReactToVideoMutationVariables = Exact<{
videoId: Scalars["Int"]["input"]; videoId: Scalars["Int"]["input"];
reaction?: InputMaybe<ReactionEnum>; reaction?: InputMaybe<ReactionEnum>;
@@ -5420,8 +5423,6 @@ export type GetUserQuery = {
updatedAt?: any | null; updatedAt?: any | null;
videosPrivateByDefault?: boolean | null; videosPrivateByDefault?: boolean | null;
agreesToMarketing?: boolean | null; agreesToMarketing?: boolean | null;
following?: Array<{ __typename?: "UserGQL"; id: number }> | null;
followers?: Array<{ __typename?: "UserGQL"; id: number }> | null;
} | null; } | null;
}; };
@@ -5721,7 +5722,6 @@ export type GetVideoDetailsQuery = {
endTime?: any | null; endTime?: any | null;
makePercentage: number; makePercentage: number;
medianRun?: number | null; medianRun?: number | null;
averageDifficulty?: number | null;
startTime?: any | null; startTime?: any | null;
totalShots: number; totalShots: number;
totalShotsMade: number; totalShotsMade: number;
@@ -5987,19 +5987,6 @@ export type GetMedianRunForVideoQuery = {
getVideo: { __typename?: "VideoGQL"; id: number; medianRun?: number | null }; getVideo: { __typename?: "VideoGQL"; id: number; medianRun?: number | null };
}; };
export type GetAverageDifficultyForVideoQueryVariables = Exact<{
videoId: Scalars["Int"]["input"];
}>;
export type GetAverageDifficultyForVideoQuery = {
__typename?: "Query";
getVideo: {
__typename?: "VideoGQL";
id: number;
averageDifficulty?: number | null;
};
};
export type StreamWithEndFramesFragment = { export type StreamWithEndFramesFragment = {
__typename?: "UploadStreamGQL"; __typename?: "UploadStreamGQL";
id: string; id: string;
@@ -6435,7 +6422,6 @@ export const VideoCardFieldsFragmentDoc = gql`
streamSegmentType streamSegmentType
} }
tableSize tableSize
pocketSize
tags { tags {
tagClasses { tagClasses {
name name
@@ -6743,7 +6729,6 @@ export const GetAggregatedShotMetricsDocument = gql`
targetMetrics { targetMetrics {
count count
makePercentage makePercentage
averageDifficulty
} }
} }
} }
@@ -9677,65 +9662,6 @@ export type CancelSubscriptionMutationOptions = Apollo.BaseMutationOptions<
CancelSubscriptionMutation, CancelSubscriptionMutation,
CancelSubscriptionMutationVariables CancelSubscriptionMutationVariables
>; >;
export const SubmitCancellationFeedbackDocument = gql`
mutation SubmitCancellationFeedback(
$reasons: [CancellationReasonEnum!]
$feedback: String
$metadata: CancellationFeedbackMetadataInput
) {
submitCancellationFeedback(
reasons: $reasons
feedback: $feedback
metadata: $metadata
)
}
`;
export type SubmitCancellationFeedbackMutationFn = Apollo.MutationFunction<
SubmitCancellationFeedbackMutation,
SubmitCancellationFeedbackMutationVariables
>;
/**
* __useSubmitCancellationFeedbackMutation__
*
* To run a mutation, you first call `useSubmitCancellationFeedbackMutation` within a React component and pass it any options that fit your needs.
* When your component renders, `useSubmitCancellationFeedbackMutation` 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 [submitCancellationFeedbackMutation, { data, loading, error }] = useSubmitCancellationFeedbackMutation({
* variables: {
* reasons: // value for 'reasons'
* feedback: // value for 'feedback'
* metadata: // value for 'metadata'
* },
* });
*/
export function useSubmitCancellationFeedbackMutation(
baseOptions?: Apollo.MutationHookOptions<
SubmitCancellationFeedbackMutation,
SubmitCancellationFeedbackMutationVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useMutation<
SubmitCancellationFeedbackMutation,
SubmitCancellationFeedbackMutationVariables
>(SubmitCancellationFeedbackDocument, options);
}
export type SubmitCancellationFeedbackMutationHookResult = ReturnType<
typeof useSubmitCancellationFeedbackMutation
>;
export type SubmitCancellationFeedbackMutationResult =
Apollo.MutationResult<SubmitCancellationFeedbackMutation>;
export type SubmitCancellationFeedbackMutationOptions =
Apollo.BaseMutationOptions<
SubmitCancellationFeedbackMutation,
SubmitCancellationFeedbackMutationVariables
>;
export const ReactToVideoDocument = gql` export const ReactToVideoDocument = gql`
mutation ReactToVideo($videoId: Int!, $reaction: ReactionEnum) { mutation ReactToVideo($videoId: Int!, $reaction: ReactionEnum) {
reactToVideo(videoId: $videoId, reaction: $reaction) reactToVideo(videoId: $videoId, reaction: $reaction)
@@ -10988,12 +10914,6 @@ export const GetUserDocument = gql`
query GetUser($userId: Int!) { query GetUser($userId: Int!) {
getUser(userId: $userId) { getUser(userId: $userId) {
...UserFragment ...UserFragment
following {
id
}
followers {
id
}
} }
} }
${UserFragmentFragmentDoc} ${UserFragmentFragmentDoc}
@@ -12073,7 +11993,6 @@ export const GetVideoDetailsDocument = gql`
endTime endTime
makePercentage makePercentage
medianRun medianRun
averageDifficulty
startTime startTime
totalShots totalShots
totalShotsMade totalShotsMade
@@ -12727,80 +12646,6 @@ export type GetMedianRunForVideoQueryResult = Apollo.QueryResult<
GetMedianRunForVideoQuery, GetMedianRunForVideoQuery,
GetMedianRunForVideoQueryVariables GetMedianRunForVideoQueryVariables
>; >;
export const GetAverageDifficultyForVideoDocument = gql`
query GetAverageDifficultyForVideo($videoId: Int!) {
getVideo(videoId: $videoId) {
id
averageDifficulty
}
}
`;
/**
* __useGetAverageDifficultyForVideoQuery__
*
* To run a query within a React component, call `useGetAverageDifficultyForVideoQuery` and pass it any options that fit your needs.
* When your component renders, `useGetAverageDifficultyForVideoQuery` 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 } = useGetAverageDifficultyForVideoQuery({
* variables: {
* videoId: // value for 'videoId'
* },
* });
*/
export function useGetAverageDifficultyForVideoQuery(
baseOptions: Apollo.QueryHookOptions<
GetAverageDifficultyForVideoQuery,
GetAverageDifficultyForVideoQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<
GetAverageDifficultyForVideoQuery,
GetAverageDifficultyForVideoQueryVariables
>(GetAverageDifficultyForVideoDocument, options);
}
export function useGetAverageDifficultyForVideoLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
GetAverageDifficultyForVideoQuery,
GetAverageDifficultyForVideoQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<
GetAverageDifficultyForVideoQuery,
GetAverageDifficultyForVideoQueryVariables
>(GetAverageDifficultyForVideoDocument, options);
}
export function useGetAverageDifficultyForVideoSuspenseQuery(
baseOptions?: Apollo.SuspenseQueryHookOptions<
GetAverageDifficultyForVideoQuery,
GetAverageDifficultyForVideoQueryVariables
>,
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useSuspenseQuery<
GetAverageDifficultyForVideoQuery,
GetAverageDifficultyForVideoQueryVariables
>(GetAverageDifficultyForVideoDocument, options);
}
export type GetAverageDifficultyForVideoQueryHookResult = ReturnType<
typeof useGetAverageDifficultyForVideoQuery
>;
export type GetAverageDifficultyForVideoLazyQueryHookResult = ReturnType<
typeof useGetAverageDifficultyForVideoLazyQuery
>;
export type GetAverageDifficultyForVideoSuspenseQueryHookResult = ReturnType<
typeof useGetAverageDifficultyForVideoSuspenseQuery
>;
export type GetAverageDifficultyForVideoQueryResult = Apollo.QueryResult<
GetAverageDifficultyForVideoQuery,
GetAverageDifficultyForVideoQueryVariables
>;
export const GetVideoForClipTimesDocument = gql` export const GetVideoForClipTimesDocument = gql`
query GetVideoForClipTimes($videoId: Int!) { query GetVideoForClipTimes($videoId: Int!) {
getVideo(videoId: $videoId) { getVideo(videoId: $videoId) {

View File

@@ -7,7 +7,6 @@ query GetAggregatedShotMetrics($aggregateInput: AggregateInputGQL!) {
targetMetrics { targetMetrics {
count count
makePercentage makePercentage
averageDifficulty
} }
} }
} }

View File

@@ -50,7 +50,6 @@ fragment VideoCardFields on VideoGQL {
streamSegmentType streamSegmentType
} }
tableSize tableSize
pocketSize
tags { tags {
tagClasses { tagClasses {
name name

View File

@@ -63,14 +63,17 @@ mutation CancelSubscription {
stripeSubscriptionId stripeSubscriptionId
} }
} }
mutation SubmitCancellationFeedback(
$reasons: [CancellationReasonEnum!] mutation CreateAuthHandoffToken {
$feedback: String createAuthHandoffToken {
$metadata: CancellationFeedbackMetadataInput token
) { expiresInSeconds
submitCancellationFeedback( }
reasons: $reasons }
feedback: $feedback
metadata: $metadata mutation ExchangeAuthHandoffToken($token: String!) {
) exchangeAuthHandoffToken(token: $token) {
customToken
userId
}
} }

View File

@@ -34,12 +34,6 @@ query getLoggedInUser {
query GetUser($userId: Int!) { query GetUser($userId: Int!) {
getUser(userId: $userId) { getUser(userId: $userId) {
...UserFragment ...UserFragment
following {
id
}
followers {
id
}
} }
} }

View File

@@ -61,7 +61,6 @@ query GetVideoDetails($videoId: Int!) {
endTime endTime
makePercentage makePercentage
medianRun medianRun
averageDifficulty
startTime startTime
totalShots totalShots
totalShotsMade totalShotsMade
@@ -212,13 +211,6 @@ query GetMedianRunForVideo($videoId: Int!) {
} }
} }
query GetAverageDifficultyForVideo($videoId: Int!) {
getVideo(videoId: $videoId) {
id
averageDifficulty
}
}
fragment StreamWithEndFrames on UploadStreamGQL { fragment StreamWithEndFrames on UploadStreamGQL {
id id
streamSegmentType streamSegmentType

View File

@@ -1077,6 +1077,8 @@ type Mutation {
feedback: String = null feedback: String = null
metadata: CancellationFeedbackMetadataInput = null metadata: CancellationFeedbackMetadataInput = null
): Boolean! ): Boolean!
createAuthHandoffToken: AuthHandoffTokenGQL!
exchangeAuthHandoffToken(token: String!): ExchangeAuthHandoffResultGQL!
findPrerecordTableLayout(b64Image: String!, videoId: Int!): HomographyInfoGQL findPrerecordTableLayout(b64Image: String!, videoId: Int!): HomographyInfoGQL
createUploadStream( createUploadStream(
videoMetadata: VideoMetadataInput! videoMetadata: VideoMetadataInput!
@@ -1216,6 +1218,16 @@ input CancellationFeedbackMetadataInput {
platform: String = null platform: String = null
} }
type AuthHandoffTokenGQL {
token: String!
expiresInSeconds: Int!
}
type ExchangeAuthHandoffResultGQL {
customToken: String!
userId: Int!
}
type CreateUploadStreamReturn { type CreateUploadStreamReturn {
videoId: Int! videoId: Int!
} }