Compare commits

..

26 Commits

Author SHA1 Message Date
dean
5fbc134c42 feat: Add following and followers to GetUser query
All checks were successful
Tests / Tests (pull_request) Successful in 21s
Adds following and followers arrays to the GetUser query to support
displaying follower/following counts on other users' profiles.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 15:53:59 -08:00
f67288d387 Merge pull request 'feat: Add GetAverageDifficultyForVideo query' (#216) from dean/add-avg-difficulty-for-video-query into master
Reviewed-on: #216
2025-12-16 04:22:56 +00:00
dean
71425a22a6 feat: Add GetAverageDifficultyForVideo query
All checks were successful
Tests / Tests (pull_request) Successful in 10s
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 20:21:22 -08:00
47339caa74 Merge pull request 'Add averageDifficulty to aggregate operation' (#215) from loewy/add-average-difficulty-field into master
Reviewed-on: #215
2025-12-16 04:06:05 +00:00
3c8be1d647 add averageDifficulty to aggregate operation
All checks were successful
Tests / Tests (pull_request) Successful in 10s
2025-12-10 11:43:58 -08:00
09267529cd Merge pull request 'dean/add-challenge-feature' (#212) from dean/add-challenge-feature into master
Reviewed-on: #212
2025-12-10 19:11:18 +00:00
2b48ec48d5 Merge pull request 'feat: Add pocketSize to VideoCardFields fragment' (#214) from dean/feed-pocket-size-v2 into master
Reviewed-on: #214
2025-12-10 18:33:30 +00:00
dean
93bf341c0f feat: Add pocketSize to VideoCardFields fragment
All checks were successful
Tests / Tests (pull_request) Successful in 8s
2025-12-10 10:31:36 -08:00
dean
9f735b61bb chore: Regenerate schema from backend
All checks were successful
Tests / Tests (pull_request) Successful in 9s
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 08:49:41 -08:00
dean
fda8e4eb9d feat: Add challenge dismissal GQL operations
All checks were successful
Tests / Tests (pull_request) Successful in 9s
- Add DismissChallenge/UndismissChallenge mutations
- Add IsChallengeDismissed query
- Add GetMyDismissedChallenges query
- Update schema with dismissal fields

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 08:23:27 -08:00
dean
338c45d59b feat: Add challenge invitations fields and fix feed hasFollowing query
- Add invitations query fields with invitee data to GetChallenge
- Add participantCount field to GetChallenge
- Add GetMyChallengeEntries query (was missing)
- Add hasFollowing to GetVideoFeed response (fixes feed ordering)
- Sync schema with backend challenge types

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 08:22:50 -08:00
dean
e879c5008f chore: Regenerate GraphQL TypeScript types
Regenerate gql/src/index.tsx to match current schema and fix
codegen hash mismatch in CI.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 08:22:50 -08:00
dean
2d67e076f7 Add GraphQL queries and mutations for challenge feature
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 08:22:49 -08:00
dean
ec534769f3 chore: regenerate schema and types 2025-11-28 08:22:49 -08:00
dean
f8e8c7b7d4 merge: sync gql submodule, keeping local notification fragment 2025-11-28 08:22:49 -08:00
dean
372fd6e26b schema: add challenge notification types 2025-11-28 08:22:49 -08:00
dean
d9ef5ed79a WIP: Add challenges feature schema and operations
- Add Challenge, ChallengeEntry, ChallengeInvitation, RuleSet types
- Add queries: challenges, challenge, challengeLeaderboard, myChallengeInvitations, myChallengeEntries, ruleSets
- Add mutations: createChallenge, createRuleSet, inviteUsersToChallenge, respondToChallengeInvitation, startChallenge, submitChallengeEntry, recalculateChallengeEntry

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 08:22:49 -08:00
f14e117416 Merge pull request 'Add videoId field to playlist in ShotWithAllFeatures fragment' (#210) from loewy/add-video-id-to-playlist-shots-fragment into master
Reviewed-on: #210
2025-11-12 22:16:58 +00:00
3d86c13291 add videoId field to playlist in shot w features fragment
All checks were successful
Tests / Tests (pull_request) Successful in 10s
2025-11-12 14:08:49 -08:00
e63f8600aa Merge pull request 'Add pocket size field to GraphQL schema and operations' (#209) from dean/add-pocket-size-clean into master
Reviewed-on: #209
2025-11-11 21:09:28 +00:00
7e822446da Merge branch 'master' into dean/add-pocket-size-clean
All checks were successful
Tests / Tests (pull_request) Successful in 9s
2025-11-11 20:34:55 +00:00
75ecc5894b Merge pull request 'dean/add-home-feed-option' (#208) from dean/add-home-feed-option into master
Reviewed-on: #208
2025-11-10 22:09:09 +00:00
73c8dd5f57 Merge branch 'master' into dean/add-home-feed-option
All checks were successful
Tests / Tests (pull_request) Successful in 10s
2025-11-10 19:55:16 +00:00
dean
e4223a1a17 Add isFollowedByCurrentUser field and hasFollowing to feed query
All checks were successful
Tests / Tests (pull_request) Successful in 9s
Updated GraphQL operations to support efficient follow status checking:
- Added isFollowedByCurrentUser field to UserSocialsFields fragment
- Removed nested followers array from UserSocialsFields (over-fetching)
- Simplified followUser/unfollowUser mutations to return minimal data
- Added hasFollowing field to GetVideoFeed query for feed mode detection
- Updated getUserFollowingFollowers query to include isFollowedByCurrentUser

These changes enable the mobile app to:
- Display correct follow/unfollow button states without client-side lookups
- Differentiate between "Following" and "Popular" feed modes
- Reduce payload size by removing unnecessary nested data

Backend handles efficient resolution via request-scoped caching.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-09 23:44:39 -08:00
dean
83b7f0d0f6 Add home feed and isFollowedByCurrentUser field
- Add home option to VideoFeedInputGQL for automatic feed selection
- Add hasFollowing field to VideoHistoryGQL response
- Add isFollowedByCurrentUser field to UserGQL to replace followers N+1

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-08 09:15:39 -08:00
dean
a882f98d91 Add home option to VideoFeedInputGQL
The home feed option enables smart feed selection on the backend:
- If user has following: returns FOLLOWING feed
- If user has no following: returns ALL feed
- Always includes hasFollowing flag so frontend knows which feed it got

This allows frontend to make a single query instead of needing to
check following status separately.
2025-11-07 12:16:01 -08:00
7 changed files with 1104 additions and 1034 deletions

View File

@@ -3535,6 +3535,7 @@ export type GetAggregatedShotMetricsQuery = {
__typename?: "TargetMetricsGQL";
count: number;
makePercentage?: number | null;
averageDifficulty?: number | null;
};
}>;
};
@@ -4017,6 +4018,7 @@ export type GetFeedQuery = {
private: boolean;
elapsedTime?: number | null;
tableSize: number;
pocketSize?: number | null;
owner?: {
__typename?: "UserGQL";
id: number;
@@ -4118,6 +4120,7 @@ export type VideoCardFieldsFragment = {
private: boolean;
elapsedTime?: number | null;
tableSize: number;
pocketSize?: number | null;
owner?: {
__typename?: "UserGQL";
id: number;
@@ -4215,6 +4218,7 @@ export type GetVideoFeedQuery = {
private: boolean;
elapsedTime?: number | null;
tableSize: number;
pocketSize?: number | null;
owner?: {
__typename?: "UserGQL";
id: number;
@@ -4965,6 +4969,7 @@ export type GetShotsWithMetadataFilterResultQuery = {
} | null;
playlist?: {
__typename?: "HLSPlaylistGQL";
videoId: number;
segmentDurations: Array<number>;
} | null;
} | null;
@@ -5047,6 +5052,7 @@ export type GetShotsWithMetadataQuery = {
} | null;
playlist?: {
__typename?: "HLSPlaylistGQL";
videoId: number;
segmentDurations: Array<number>;
} | null;
} | null;
@@ -5118,6 +5124,7 @@ export type GetShotsByIdsQuery = {
} | null;
playlist?: {
__typename?: "HLSPlaylistGQL";
videoId: number;
segmentDurations: Array<number>;
} | null;
} | null;
@@ -5182,6 +5189,7 @@ export type ShotWithAllFeaturesFragment = {
} | null;
playlist?: {
__typename?: "HLSPlaylistGQL";
videoId: number;
segmentDurations: Array<number>;
} | null;
} | null;
@@ -5263,6 +5271,7 @@ export type EditShotMutation = {
} | null;
playlist?: {
__typename?: "HLSPlaylistGQL";
videoId: number;
segmentDurations: Array<number>;
} | null;
} | null;
@@ -5378,6 +5387,8 @@ export type GetUserQuery = {
updatedAt?: any | null;
videosPrivateByDefault?: boolean | null;
agreesToMarketing?: boolean | null;
following?: Array<{ __typename?: "UserGQL"; id: number }> | null;
followers?: Array<{ __typename?: "UserGQL"; id: number }> | null;
} | null;
};
@@ -5449,21 +5460,7 @@ export type FollowUserMutationVariables = Exact<{
export type FollowUserMutation = {
__typename?: "Mutation";
followUser: {
__typename?: "UserGQL";
username: string;
id: number;
following?: Array<{
__typename?: "UserGQL";
id: number;
username: string;
}> | null;
followers?: Array<{
__typename?: "UserGQL";
id: number;
username: string;
}> | null;
};
followUser: { __typename?: "UserGQL"; id: number; username: string };
};
export type UnfollowUserMutationVariables = Exact<{
@@ -5472,21 +5469,7 @@ export type UnfollowUserMutationVariables = Exact<{
export type UnfollowUserMutation = {
__typename?: "Mutation";
unfollowUser: {
__typename?: "UserGQL";
username: string;
id: number;
following?: Array<{
__typename?: "UserGQL";
id: number;
username: string;
}> | null;
followers?: Array<{
__typename?: "UserGQL";
id: number;
username: string;
}> | null;
};
unfollowUser: { __typename?: "UserGQL"; id: number; username: string };
};
export type GetUserFollowingFollowersQueryVariables = Exact<{
@@ -5503,12 +5486,14 @@ export type GetUserFollowingFollowersQuery = {
id: number;
username: string;
profileImageUri?: string | null;
isFollowedByCurrentUser?: boolean | null;
}> | null;
followers?: Array<{
__typename?: "UserGQL";
id: number;
username: string;
profileImageUri?: string | null;
isFollowedByCurrentUser?: boolean | null;
}> | null;
} | null;
};
@@ -5703,6 +5688,7 @@ export type GetVideoDetailsQuery = {
endTime?: any | null;
makePercentage: number;
medianRun?: number | null;
averageDifficulty?: number | null;
startTime?: any | null;
totalShots: number;
totalShotsMade: number;
@@ -5968,6 +5954,19 @@ export type GetMedianRunForVideoQuery = {
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 = {
__typename?: "UploadStreamGQL";
id: string;
@@ -6403,6 +6402,7 @@ export const VideoCardFieldsFragmentDoc = gql`
streamSegmentType
}
tableSize
pocketSize
tags {
tagClasses {
name
@@ -6551,6 +6551,7 @@ export const ShotWithAllFeaturesFragmentDoc = gql`
streamSegmentType
}
playlist {
videoId
segmentDurations
}
}
@@ -6709,6 +6710,7 @@ export const GetAggregatedShotMetricsDocument = gql`
targetMetrics {
count
makePercentage
averageDifficulty
}
}
}
@@ -10894,6 +10896,12 @@ export const GetUserDocument = gql`
query GetUser($userId: Int!) {
getUser(userId: $userId) {
...UserFragment
following {
id
}
followers {
id
}
}
}
${UserFragmentFragmentDoc}
@@ -11280,16 +11288,8 @@ export type GetUserTagsQueryResult = Apollo.QueryResult<
export const FollowUserDocument = gql`
mutation followUser($followedUserId: Int!) {
followUser(followedUserId: $followedUserId) {
username
id
following {
id
username
}
followers {
id
username
}
username
}
}
`;
@@ -11339,16 +11339,8 @@ export type FollowUserMutationOptions = Apollo.BaseMutationOptions<
export const UnfollowUserDocument = gql`
mutation unfollowUser($followedUserId: Int!) {
unfollowUser(followedUserId: $followedUserId) {
username
id
following {
id
username
}
followers {
id
username
}
username
}
}
`;
@@ -11403,11 +11395,13 @@ export const GetUserFollowingFollowersDocument = gql`
id
username
profileImageUri
isFollowedByCurrentUser
}
followers {
id
username
profileImageUri
isFollowedByCurrentUser
}
}
}
@@ -11987,6 +11981,7 @@ export const GetVideoDetailsDocument = gql`
endTime
makePercentage
medianRun
averageDifficulty
startTime
totalShots
totalShotsMade
@@ -12640,6 +12635,80 @@ export type GetMedianRunForVideoQueryResult = Apollo.QueryResult<
GetMedianRunForVideoQuery,
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`
query GetVideoForClipTimes($videoId: Int!) {
getVideo(videoId: $videoId) {

View File

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

View File

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

View File

@@ -192,6 +192,7 @@ fragment ShotWithAllFeatures on ShotGQL {
streamSegmentType
}
playlist {
videoId
segmentDurations
}
}

View File

@@ -34,6 +34,12 @@ query getLoggedInUser {
query GetUser($userId: Int!) {
getUser(userId: $userId) {
...UserFragment
following {
id
}
followers {
id
}
}
}
@@ -88,31 +94,15 @@ query GetUserTags {
mutation followUser($followedUserId: Int!) {
followUser(followedUserId: $followedUserId) {
username
id
following {
id
username
}
followers {
id
username
}
username
}
}
mutation unfollowUser($followedUserId: Int!) {
unfollowUser(followedUserId: $followedUserId) {
username
id
following {
id
username
}
followers {
id
username
}
username
}
}
@@ -123,11 +113,13 @@ query getUserFollowingFollowers {
id
username
profileImageUri
isFollowedByCurrentUser
}
followers {
id
username
profileImageUri
isFollowedByCurrentUser
}
}
}

View File

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

File diff suppressed because it is too large Load Diff