From a882f98d9102b8f0a1c6af15465adc0d6af672f4 Mon Sep 17 00:00:00 2001 From: dean Date: Fri, 7 Nov 2025 12:16:01 -0800 Subject: [PATCH] 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. --- src/index.tsx | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/schema.gql | 42 ++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/src/index.tsx b/src/index.tsx index 92b7f38..529ede5 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -2286,6 +2286,7 @@ export type Mutation = { createSubscription: CreateSubscriptionResultGql; createUploadStream: CreateUploadStreamReturn; deleteComment: Scalars["Boolean"]["output"]; + deleteNotification: Scalars["Boolean"]["output"]; deleteTags: Scalars["Boolean"]["output"]; deleteUser: Scalars["Boolean"]["output"]; deleteVideo: Scalars["Boolean"]["output"]; @@ -2300,6 +2301,9 @@ export type Mutation = { getHlsInitUploadLink: GetUploadLinkReturn; getProfileImageUploadLink: GetProfileUploadLinkReturn; getUploadLink: GetUploadLinkReturn; + markAllNotificationsAsRead: Scalars["Boolean"]["output"]; + markNotificationAsRead: Scalars["Boolean"]["output"]; + markNotificationsAsRead: Scalars["Boolean"]["output"]; reactToVideo: Scalars["Boolean"]["output"]; reportContent: Scalars["Boolean"]["output"]; retireTags: Scalars["Boolean"]["output"]; @@ -2346,6 +2350,10 @@ export type MutationDeleteCommentArgs = { videoId: Scalars["Int"]["input"]; }; +export type MutationDeleteNotificationArgs = { + notificationId: Scalars["Int"]["input"]; +}; + export type MutationDeleteTagsArgs = { tagsToDelete: Array; videoId: Scalars["Int"]["input"]; @@ -2401,6 +2409,14 @@ export type MutationGetUploadLinkArgs = { videoId: Scalars["Int"]["input"]; }; +export type MutationMarkNotificationAsReadArgs = { + notificationId: Scalars["Int"]["input"]; +}; + +export type MutationMarkNotificationsAsReadArgs = { + notificationIds: Array; +}; + export type MutationReactToVideoArgs = { reaction?: InputMaybe; videoId: Scalars["Int"]["input"]; @@ -2441,6 +2457,39 @@ export type NoInitForChunkedUploadErr = { segmentType: StreamSegmentTypeEnum; }; +export type NotificationConnection = { + __typename?: "NotificationConnection"; + hasMore: Scalars["Boolean"]["output"]; + notifications: Array; + totalCount: Scalars["Int"]["output"]; + unreadCount: Scalars["Int"]["output"]; +}; + +export type NotificationFilters = { + isRead?: InputMaybe; + notificationTypes?: InputMaybe>; +}; + +export type NotificationGql = { + __typename?: "NotificationGQL"; + actor: UserGql; + comment?: Maybe; + createdAt: Scalars["DateTime"]["output"]; + id: Scalars["Int"]["output"]; + isRead: Scalars["Boolean"]["output"]; + notificationType: NotificationTypeEnum; + reactionType?: Maybe; + readAt?: Maybe; + videoId?: Maybe; +}; + +export enum NotificationTypeEnum { + Comment = "COMMENT", + CommentReply = "COMMENT_REPLY", + Follow = "FOLLOW", + Reaction = "REACTION", +} + export type OtherErrorNeedsNote = { __typename?: "OtherErrorNeedsNote"; msg?: Maybe; @@ -2550,6 +2599,8 @@ export type Query = { getVideo: VideoGql; getVideoMakePercentageIntervals: Array; getVideos: Array; + notifications: NotificationConnection; + unreadNotificationCount: Scalars["Int"]["output"]; waitFor: Scalars["Float"]["output"]; }; @@ -2689,6 +2740,12 @@ export type QueryGetVideosArgs = { videoIds: Array; }; +export type QueryNotificationsArgs = { + filters?: InputMaybe; + limit?: Scalars["Int"]["input"]; + offset?: Scalars["Int"]["input"]; +}; + export type QueryWaitForArgs = { duration: Scalars["Float"]["input"]; }; @@ -3170,16 +3227,25 @@ export type VideoFeedInputGql = | { allUsers: Scalars["Boolean"]["input"]; followedByUserId?: never; + home?: never; userId?: never; } | { allUsers?: never; followedByUserId: Scalars["Int"]["input"]; + home?: never; userId?: never; } | { allUsers?: never; followedByUserId?: never; + home: Scalars["Boolean"]["input"]; + userId?: never; + } + | { + allUsers?: never; + followedByUserId?: never; + home?: never; userId: Scalars["Int"]["input"]; }; diff --git a/src/schema.gql b/src/schema.gql index 9ca10fe..715cf0d 100644 --- a/src/schema.gql +++ b/src/schema.gql @@ -28,6 +28,12 @@ type Query { when: DateTime = null ): CountLeaderboardGQL! getMedals(scope: MedalScope!, userId: Int = null): RequestedMedalsGQL! + notifications( + limit: Int! = 20 + offset: Int! = 0 + filters: NotificationFilters = null + ): NotificationConnection! + unreadNotificationCount: Int! getRuns( filterInput: RunFilterInput! runIds: [Int!] = null @@ -647,6 +653,7 @@ input VideoFeedInputGQL @oneOf { followedByUserId: Int userId: Int allUsers: Boolean + home: Boolean } type MakePercentageIntervalGQL { @@ -715,6 +722,37 @@ input MedalScope @oneOf { datetimeRange: DatetimeRangeAggregationInput } +type NotificationConnection { + notifications: [NotificationGQL!]! + totalCount: Int! + unreadCount: Int! + hasMore: Boolean! +} + +type NotificationGQL { + id: Int! + notificationType: NotificationTypeEnum! + actor: UserGQL! + videoId: Int + comment: CommentGQL + reactionType: String + isRead: Boolean! + createdAt: DateTime! + readAt: DateTime +} + +enum NotificationTypeEnum { + COMMENT + COMMENT_REPLY + REACTION + FOLLOW +} + +input NotificationFilters { + isRead: Boolean = null + notificationTypes: [NotificationTypeEnum!] = null +} + type GetRunsResult { runs: [RunGQL!]! count: Int @@ -916,6 +954,10 @@ type Mutation { reason: ReportReasonEnum! customReason: String = null ): Boolean! + markNotificationAsRead(notificationId: Int!): Boolean! + markAllNotificationsAsRead: Boolean! + markNotificationsAsRead(notificationIds: [Int!]!): Boolean! + deleteNotification(notificationId: Int!): Boolean! addAnnotationToShot( shotId: Int! annotationName: String!