Compare commits

...

17 Commits

Author SHA1 Message Date
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
11 changed files with 8640 additions and 1041 deletions

5423
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -15,7 +15,7 @@
"@apollo/client": "^3.11.10",
"@graphql-codegen/cli": "^5.0.0",
"@graphql-codegen/typescript": "^4.0.1",
"@graphql-codegen/typescript-operations": "^4.0.1",
"@graphql-codegen/typescript-operations": "^5.0.4",
"@graphql-codegen/typescript-react-apollo": "^4.2.0",
"graphql": "^16.8.1",
"pbjs": "^0.0.14",

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,282 @@
query GetChallenges {
challenges {
id
name
description
minimumShots
startDate
endDate
createdAt
updatedAt
requiredTableSize
requiredPocketSize
isPublic
maxAttempts
ruleSet {
id
name
description
}
createdBy {
id
username
profileImageUri
}
}
}
query GetMyDismissedChallenges {
myDismissedChallenges {
id
name
description
minimumShots
startDate
endDate
createdAt
updatedAt
requiredTableSize
requiredPocketSize
isPublic
maxAttempts
ruleSet {
id
name
description
}
createdBy {
id
username
profileImageUri
}
}
}
query GetChallenge($id: ID!) {
challenge(id: $id) {
id
name
description
minimumShots
startDate
endDate
createdAt
updatedAt
requiredTableSize
requiredPocketSize
isPublic
maxAttempts
participantCount
ruleSet {
id
name
description
}
createdBy {
id
username
profileImageUri
}
invitations {
id
status
createdAt
invitee {
id
username
profileImageUri
}
inviter {
id
username
profileImageUri
}
}
}
}
query GetRuleSets {
ruleSets {
id
name
description
}
}
query GetChallengeLeaderboard($challengeId: ID!, $limit: Int) {
challengeLeaderboard(challengeId: $challengeId, limit: $limit) {
id
status
shotsCount
makesCount
makeRate
qualified
createdAt
attemptCount
user {
id
username
profileImageUri
}
video {
id
createdAt
}
}
}
query GetMyChallengeInvitations {
myChallengeInvitations {
id
status
createdAt
challenge {
id
name
description
startDate
endDate
createdBy {
id
username
profileImageUri
}
}
inviter {
id
username
profileImageUri
}
}
}
query GetMyChallengeEntries {
myChallengeEntries {
id
status
shotsCount
makesCount
makeRate
qualified
createdAt
challenge {
id
name
}
video {
id
}
}
}
mutation CreateRuleSet($name: String!, $description: String) {
createRuleSet(name: $name, description: $description) {
id
name
description
}
}
mutation CreateChallenge(
$name: String!
$ruleSetId: ID!
$minimumShots: Int!
$startDate: DateTime!
$endDate: DateTime!
$description: String
$requiredTableSize: Float
$requiredPocketSize: Float
$isPublic: Boolean! = false
$maxAttempts: Int
) {
createChallenge(
name: $name
ruleSetId: $ruleSetId
minimumShots: $minimumShots
startDate: $startDate
endDate: $endDate
description: $description
requiredTableSize: $requiredTableSize
requiredPocketSize: $requiredPocketSize
isPublic: $isPublic
maxAttempts: $maxAttempts
) {
id
name
description
requiredTableSize
requiredPocketSize
isPublic
maxAttempts
}
}
mutation InviteUsersToChallenge($challengeId: ID!, $userIds: [ID!]!) {
inviteUsersToChallenge(challengeId: $challengeId, userIds: $userIds) {
id
status
inviter {
id
username
}
}
}
mutation RespondToChallengeInvitation($invitationId: ID!, $accept: Boolean!) {
respondToChallengeInvitation(invitationId: $invitationId, accept: $accept) {
id
status
challenge {
id
}
}
}
mutation StartChallenge($challengeId: ID!) {
startChallenge(challengeId: $challengeId) {
id
status
createdAt
challenge {
id
name
}
}
}
mutation SubmitChallengeEntry($entryId: ID!, $videoId: ID!) {
submitChallengeEntry(entryId: $entryId, videoId: $videoId) {
id
status
qualified
makeRate
shotsCount
makesCount
video {
id
}
}
}
mutation RecalculateChallengeEntry($entryId: ID!) {
recalculateChallengeEntry(entryId: $entryId) {
id
status
qualified
makeRate
shotsCount
makesCount
}
}
query IsChallengeDismissed($challengeId: ID!) {
isChallengeDismissed(challengeId: $challengeId)
}
mutation DismissChallenge($challengeId: ID!) {
dismissChallenge(challengeId: $challengeId)
}
mutation UndismissChallenge($challengeId: ID!) {
undismissChallenge(challengeId: $challengeId)
}

View File

@@ -19,11 +19,7 @@ fragment UserSocialsFields on UserGQL {
id
username
profileImageUri
followers {
id
username
profileImageUri
}
isFollowedByCurrentUser
}
fragment VideoCardFields on VideoGQL {
@@ -116,5 +112,6 @@ query GetVideoFeed(
hasNextPage
endCursor
}
hasFollowing
}
}

View File

@@ -42,6 +42,11 @@ fragment Notification on NotificationGQL {
profileImageUri
}
videoId
challengeId
challenge {
id
name
}
comment {
id
message

View File

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

View File

@@ -88,31 +88,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 +107,13 @@ query getUserFollowingFollowers {
id
username
profileImageUri
isFollowedByCurrentUser
}
followers {
id
username
profileImageUri
isFollowedByCurrentUser
}
}
}
@@ -177,3 +163,13 @@ fragment UserFragment on UserGQL {
videosPrivateByDefault
agreesToMarketing
}
query GetUsersMatching(
$matchString: String = null
$limit: Int = null
$after: String = null
) {
getUsersMatching(matchString: $matchString, limit: $limit, after: $after) {
...UserFragment
}
}

View File

@@ -88,11 +88,7 @@ fragment UserSocialsFields on UserGQL {
id
username
profileImageUri
followers {
id
username
profileImageUri
}
isFollowedByCurrentUser
}
query GetVideoSocialDetailsById($videoId: Int!) {

File diff suppressed because it is too large Load Diff

View File

@@ -603,6 +603,18 @@
lodash "~4.17.0"
tslib "~2.5.0"
"@graphql-codegen/plugin-helpers@^6.0.0":
version "6.0.0"
resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-6.0.0.tgz#8a913c82a95b9ff36b2180f7c56611cc56268625"
integrity sha512-Z7P89vViJvQakRyMbq/JF2iPLruRFOwOB6IXsuSvV/BptuuEd7fsGPuEf8bdjjDxUY0pJZnFN8oC7jIQ8p9GKA==
dependencies:
"@graphql-tools/utils" "^10.0.0"
change-case-all "1.0.15"
common-tags "1.8.2"
import-from "4.0.0"
lodash "~4.17.0"
tslib "~2.6.0"
"@graphql-codegen/schema-ast@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-4.0.0.tgz#5d60996c87b64f81847da8fcb2d8ef50ede89755"
@@ -612,16 +624,25 @@
"@graphql-tools/utils" "^10.0.0"
tslib "~2.5.0"
"@graphql-codegen/typescript-operations@^4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-4.0.1.tgz#930af3e2d2ae8ff06de696291be28fe7046a2fef"
integrity sha512-GpUWWdBVUec/Zqo23aFLBMrXYxN2irypHqDcKjN78JclDPdreasAEPcIpMfqf4MClvpmvDLy4ql+djVAwmkjbw==
"@graphql-codegen/schema-ast@^5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-5.0.0.tgz#9708d7484a01bb3a502685126708d7b139fd0721"
integrity sha512-jn7Q3PKQc0FxXjbpo9trxzlz/GSFQWxL042l0iC8iSbM/Ar+M7uyBwMtXPsev/3Razk+osQyreghIz0d2+6F7Q==
dependencies:
"@graphql-codegen/plugin-helpers" "^5.0.0"
"@graphql-codegen/typescript" "^4.0.1"
"@graphql-codegen/visitor-plugin-common" "4.0.1"
"@graphql-codegen/plugin-helpers" "^6.0.0"
"@graphql-tools/utils" "^10.0.0"
tslib "~2.6.0"
"@graphql-codegen/typescript-operations@^5.0.4":
version "5.0.4"
resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-5.0.4.tgz#052680d41f4bab3bb85af8e906c43bde29239e7b"
integrity sha512-5Bu/BTmyNjdSfSLLBKjC0+4XWcY01uotVcnVIWIxxRdIHoRxnTW6PUkT5CoPHP5r/Uoo3OvIJxh+0LYSH5suwA==
dependencies:
"@graphql-codegen/plugin-helpers" "^6.0.0"
"@graphql-codegen/typescript" "^5.0.4"
"@graphql-codegen/visitor-plugin-common" "6.1.2"
auto-bind "~4.0.0"
tslib "~2.5.0"
tslib "~2.6.0"
"@graphql-codegen/typescript-react-apollo@^4.2.0":
version "4.2.0"
@@ -645,6 +666,17 @@
auto-bind "~4.0.0"
tslib "~2.5.0"
"@graphql-codegen/typescript@^5.0.4":
version "5.0.4"
resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-5.0.4.tgz#a7f51505445568fd737b01ce7fad515c2b89897f"
integrity sha512-q6S8hX+aR4BzeGgolac4gp22rBnXbLhedmOwT1UBT9e3lGNmNpYC7WJUEzAPjWf6z1lRSNmojLlwEjTnffhKNA==
dependencies:
"@graphql-codegen/plugin-helpers" "^6.0.0"
"@graphql-codegen/schema-ast" "^5.0.0"
"@graphql-codegen/visitor-plugin-common" "6.1.2"
auto-bind "~4.0.0"
tslib "~2.6.0"
"@graphql-codegen/visitor-plugin-common@2.13.1":
version "2.13.1"
resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.13.1.tgz#2228660f6692bcdb96b1f6d91a0661624266b76b"
@@ -677,6 +709,22 @@
parse-filepath "^1.0.2"
tslib "~2.5.0"
"@graphql-codegen/visitor-plugin-common@6.1.2":
version "6.1.2"
resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-6.1.2.tgz#e614d6606402a152686d6f90e6363150a7a55bde"
integrity sha512-zYdrhJKgk8kqE1Xz5/m/Ua42zk+rIvYB/FHh3dE1AhZ6b1IDqgKjF3LnkT+K2qenf9EfT4yNjXd5CEKMeXfHyg==
dependencies:
"@graphql-codegen/plugin-helpers" "^6.0.0"
"@graphql-tools/optimize" "^2.0.0"
"@graphql-tools/relay-operation-optimizer" "^7.0.0"
"@graphql-tools/utils" "^10.0.0"
auto-bind "~4.0.0"
change-case-all "1.0.15"
dependency-graph "^1.0.0"
graphql-tag "^2.11.0"
parse-filepath "^1.0.2"
tslib "~2.6.0"
"@graphql-tools/apollo-engine-loader@^8.0.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-8.0.0.tgz#ac1f351cbe41508411784f25757f5557b0f27489"
@@ -1649,6 +1697,11 @@ dependency-graph@^0.11.0:
resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.11.0.tgz#ac0ce7ed68a54da22165a85e97a01d53f5eb2e27"
integrity sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==
dependency-graph@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-1.0.0.tgz#bb5e85aec1310bc13b22dbd76e3196c4ee4c10d2"
integrity sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==
detect-indent@^6.0.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6"