diff --git a/packages/client/lens-v2.graphql b/packages/client/lens-v2.graphql index ede0adac49..3877cd215e 100644 --- a/packages/client/lens-v2.graphql +++ b/packages/client/lens-v2.graphql @@ -2964,7 +2964,7 @@ input ValidatePublicationMetadataRequest { scalar NotificationId type FutureProofNotification { - id: String + id: NotificationId! } type ReactionNotification { @@ -3000,7 +3000,7 @@ type ProfileActedResult { actions: OpenActionResult! } -type ActedNotificaion { +type ActedNotification { id: NotificationId! actions: [ProfileActedResult!]! publication: AnyPublication! @@ -3021,7 +3021,7 @@ union Notification = | CommentNotification | MirrorNotification | QuoteNotification - | ActedNotificaion + | ActedNotification | FollowNotification | MentionNotification | FutureProofNotification diff --git a/packages/client/src/feed/graphql/feed.generated.ts b/packages/client/src/feed/graphql/feed.generated.ts index 3e2e0f6e7b..4c10ab1831 100644 --- a/packages/client/src/feed/graphql/feed.generated.ts +++ b/packages/client/src/feed/graphql/feed.generated.ts @@ -8,6 +8,10 @@ import { PaginatedResultInfoFragment, ProfileFragment, CommentFragment, + CollectOpenActionResultFragment, + NftDropOpenActionFragment, + UnknownOpenActionResultFragment, + MirrorFragment, } from '../../graphql/fragments.generated'; import { GraphQLClient } from 'graphql-request'; import { GraphQLClientRequestHeaders } from 'graphql-request/build/cjs/types'; @@ -20,6 +24,10 @@ import { PaginatedResultInfoFragmentDoc, ProfileFragmentDoc, CommentFragmentDoc, + CollectOpenActionResultFragmentDoc, + NftDropOpenActionFragmentDoc, + UnknownOpenActionResultFragmentDoc, + MirrorFragmentDoc, } from '../../graphql/fragments.generated'; export type ElectedMirrorFragment = { mirrorId: string; diff --git a/packages/client/src/notifications/Notifications.ts b/packages/client/src/notifications/Notifications.ts new file mode 100644 index 0000000000..410458ec81 --- /dev/null +++ b/packages/client/src/notifications/Notifications.ts @@ -0,0 +1,78 @@ +import type { PromiseResult } from '@lens-protocol/shared-kernel'; + +import type { Authentication } from '../authentication'; +import type { LensConfig } from '../consts/config'; +import type { CredentialsExpiredError, NotAuthenticatedError } from '../consts/errors'; +import { FetchGraphQLClient } from '../graphql/FetchGraphQLClient'; +import type { NotificationRequest } from '../graphql/types.generated'; +import { + PaginatedResult, + buildImageTransformsFromConfig, + buildPaginatedQueryResult, + requireAuthHeaders, +} from '../helpers'; +import { + ActedNotificationFragment, + CommentNotificationFragment, + FollowNotificationFragment, + FutureProofNotificationFragment, + getSdk, + MentionNotificationFragment, + MirrorNotificationFragment, + QuoteNotificationFragment, + ReactionNotificationFragment, + Sdk, +} from './graphql/notifications.generated'; + +export type NotificationFragment = + | ActedNotificationFragment + | CommentNotificationFragment + | FollowNotificationFragment + | FutureProofNotificationFragment + | MentionNotificationFragment + | MirrorNotificationFragment + | QuoteNotificationFragment + | ReactionNotificationFragment; + +/** + * Notifications on activity for a profile including collects, comment, new followers, and mirrors. + * + * @group LensClient Modules + */ +export class Notifications { + private readonly authentication: Authentication | undefined; + private readonly sdk: Sdk; + + constructor( + private readonly config: LensConfig, + authentication: Authentication, + ) { + const client = new FetchGraphQLClient(config.environment.gqlEndpoint); + + this.sdk = getSdk(client); + this.authentication = authentication; + } + + async fetch( + request: NotificationRequest, + observerId?: string, + ): PromiseResult< + PaginatedResult, + CredentialsExpiredError | NotAuthenticatedError + > { + return requireAuthHeaders(this.authentication, async (headers) => { + return buildPaginatedQueryResult(async (currRequest) => { + const result = await this.sdk.Notifications( + { + request: currRequest, + observerId, + ...buildImageTransformsFromConfig(this.config.mediaTransforms), + }, + headers, + ); + + return result.data.result; + }, request); + }); + } +} diff --git a/packages/client/src/notifications/graphql/notifications.generated.ts b/packages/client/src/notifications/graphql/notifications.generated.ts new file mode 100644 index 0000000000..64114766f2 --- /dev/null +++ b/packages/client/src/notifications/graphql/notifications.generated.ts @@ -0,0 +1,328 @@ +// @ts-nocheck +import * as Types from '../../graphql/types.generated'; + +import { + ProfileFieldsFragment, + PostFragment, + QuoteFragment, + PaginatedResultInfoFragment, + ProfileFragment, + CommentFragment, + CollectOpenActionResultFragment, + NftDropOpenActionFragment, + UnknownOpenActionResultFragment, + MirrorFragment, +} from '../../graphql/fragments.generated'; +import { GraphQLClient } from 'graphql-request'; +import { GraphQLClientRequestHeaders } from 'graphql-request/build/cjs/types'; +import { print } from 'graphql'; +import gql from 'graphql-tag'; +import { + ProfileFieldsFragmentDoc, + PostFragmentDoc, + QuoteFragmentDoc, + PaginatedResultInfoFragmentDoc, + ProfileFragmentDoc, + CommentFragmentDoc, + CollectOpenActionResultFragmentDoc, + NftDropOpenActionFragmentDoc, + UnknownOpenActionResultFragmentDoc, + MirrorFragmentDoc, +} from '../../graphql/fragments.generated'; +export type ReactionNotificationFragment = { + id: string; + reactions: Array<{ + profile: ProfileFieldsFragment; + reactions: Array<{ reaction: Types.ReactionTypes; reactedAt: string } | null>; + }>; + publication: CommentFragment | PostFragment | QuoteFragment; +}; + +export type CommentNotificationFragment = { id: string; comment: CommentFragment }; + +export type MirrorNotificationFragment = { + id: string; + mirrors: Array<{ mirrorId: string; mirroredAt: string; profile: ProfileFieldsFragment }>; + publication: CommentFragment | PostFragment | QuoteFragment; +}; + +export type QuoteNotificationFragment = { id: string; quote: QuoteFragment }; + +export type ActedNotificationFragment = { + id: string; + actions: Array<{ + profile: ProfileFieldsFragment; + actions: + | CollectOpenActionResultFragment + | NftDropOpenActionFragment + | UnknownOpenActionResultFragment; + }>; + publication: CommentFragment | MirrorFragment | PostFragment | QuoteFragment; +}; + +export type FollowNotificationFragment = { id: string; followers: Array }; + +export type MentionNotificationFragment = { + id: string; + publication: CommentFragment | PostFragment | QuoteFragment; +}; + +export type FutureProofNotificationFragment = { id: string }; + +export type NotificationsQueryVariables = Types.Exact<{ + request: Types.NotificationRequest; + observerId?: Types.InputMaybe; + publicationImageTransform?: Types.InputMaybe; + profileCoverTransform?: Types.InputMaybe; + profilePictureTransform?: Types.InputMaybe; +}>; + +export type NotificationsQuery = { + result: { + items: Array< + | ActedNotificationFragment + | CommentNotificationFragment + | FollowNotificationFragment + | FutureProofNotificationFragment + | MentionNotificationFragment + | MirrorNotificationFragment + | QuoteNotificationFragment + | ReactionNotificationFragment + >; + pageInfo: PaginatedResultInfoFragment; + }; +}; + +export const ReactionNotificationFragmentDoc = gql` + fragment ReactionNotification on ReactionNotification { + id + reactions { + profile { + ...ProfileFields + } + reactions { + reaction + reactedAt + } + } + publication { + ... on Post { + ...Post + } + ... on Comment { + ...Comment + } + ... on Quote { + ...Quote + } + } + } + ${ProfileFieldsFragmentDoc} + ${PostFragmentDoc} + ${CommentFragmentDoc} + ${QuoteFragmentDoc} +`; +export const CommentNotificationFragmentDoc = gql` + fragment CommentNotification on CommentNotification { + id + comment { + ...Comment + } + } + ${CommentFragmentDoc} +`; +export const MirrorNotificationFragmentDoc = gql` + fragment MirrorNotification on MirrorNotification { + id + mirrors { + mirrorId + mirroredAt + profile { + ...ProfileFields + } + } + publication { + ... on Post { + ...Post + } + ... on Comment { + ...Comment + } + ... on Quote { + ...Quote + } + } + } + ${ProfileFieldsFragmentDoc} + ${PostFragmentDoc} + ${CommentFragmentDoc} + ${QuoteFragmentDoc} +`; +export const QuoteNotificationFragmentDoc = gql` + fragment QuoteNotification on QuoteNotification { + id + quote { + ...Quote + } + } + ${QuoteFragmentDoc} +`; +export const ActedNotificationFragmentDoc = gql` + fragment ActedNotification on ActedNotification { + id + actions { + profile { + ...ProfileFields + } + actions { + ... on CollectOpenActionResult { + ...CollectOpenActionResult + } + ... on NftDropOpenAction { + ...NftDropOpenAction + } + ... on UnknownOpenActionResult { + ...UnknownOpenActionResult + } + } + } + publication { + ... on Post { + ...Post + } + ... on Comment { + ...Comment + } + ... on Mirror { + ...Mirror + } + ... on Quote { + ...Quote + } + } + } + ${ProfileFieldsFragmentDoc} + ${CollectOpenActionResultFragmentDoc} + ${NftDropOpenActionFragmentDoc} + ${UnknownOpenActionResultFragmentDoc} + ${PostFragmentDoc} + ${CommentFragmentDoc} + ${MirrorFragmentDoc} + ${QuoteFragmentDoc} +`; +export const FollowNotificationFragmentDoc = gql` + fragment FollowNotification on FollowNotification { + id + followers { + ...ProfileFields + } + } + ${ProfileFieldsFragmentDoc} +`; +export const MentionNotificationFragmentDoc = gql` + fragment MentionNotification on MentionNotification { + id + publication { + ... on Post { + ...Post + } + ... on Comment { + ...Comment + } + ... on Quote { + ...Quote + } + } + } + ${PostFragmentDoc} + ${CommentFragmentDoc} + ${QuoteFragmentDoc} +`; +export const FutureProofNotificationFragmentDoc = gql` + fragment FutureProofNotification on FutureProofNotification { + id + } +`; +export const NotificationsDocument = gql` + query Notifications( + $request: NotificationRequest! + $observerId: ProfileId + $publicationImageTransform: ImageTransform = {} + $profileCoverTransform: ImageTransform = {} + $profilePictureTransform: ImageTransform = {} + ) { + result: notifications(request: $request) { + items { + ... on ReactionNotification { + ...ReactionNotification + } + ... on CommentNotification { + ...CommentNotification + } + ... on MirrorNotification { + ...MirrorNotification + } + ... on QuoteNotification { + ...QuoteNotification + } + ... on ActedNotification { + ...ActedNotification + } + ... on FollowNotification { + ...FollowNotification + } + ... on MentionNotification { + ...MentionNotification + } + ... on FutureProofNotification { + ...FutureProofNotification + } + } + pageInfo { + ...PaginatedResultInfo + } + } + } + ${ReactionNotificationFragmentDoc} + ${CommentNotificationFragmentDoc} + ${MirrorNotificationFragmentDoc} + ${QuoteNotificationFragmentDoc} + ${ActedNotificationFragmentDoc} + ${FollowNotificationFragmentDoc} + ${MentionNotificationFragmentDoc} + ${FutureProofNotificationFragmentDoc} + ${PaginatedResultInfoFragmentDoc} +`; + +export type SdkFunctionWrapper = ( + action: (requestHeaders?: Record) => Promise, + operationName: string, + operationType?: string, +) => Promise; + +const defaultWrapper: SdkFunctionWrapper = (action, _operationName, _operationType) => action(); +const NotificationsDocumentString = print(NotificationsDocument); +export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper = defaultWrapper) { + return { + Notifications( + variables: NotificationsQueryVariables, + requestHeaders?: GraphQLClientRequestHeaders, + ): Promise<{ + data: NotificationsQuery; + extensions?: any; + headers: Dom.Headers; + status: number; + }> { + return withWrapper( + (wrappedRequestHeaders) => + client.rawRequest(NotificationsDocumentString, variables, { + ...requestHeaders, + ...wrappedRequestHeaders, + }), + 'Notifications', + 'query', + ); + }, + }; +} +export type Sdk = ReturnType; diff --git a/packages/client/src/notifications/graphql/notifications.graphql b/packages/client/src/notifications/graphql/notifications.graphql new file mode 100644 index 0000000000..e49e8813da --- /dev/null +++ b/packages/client/src/notifications/graphql/notifications.graphql @@ -0,0 +1,160 @@ +fragment ReactionNotification on ReactionNotification { + id + reactions { + profile { + ...ProfileFields + } + reactions { + reaction + reactedAt + } + } + publication { + ... on Post { + ...Post + } + ... on Comment { + ...Comment + } + ... on Quote { + ...Quote + } + } +} + +fragment CommentNotification on CommentNotification { + id + comment { + ...Comment + } +} + +fragment MirrorNotification on MirrorNotification { + id + mirrors { + mirrorId + mirroredAt + profile { + ...ProfileFields + } + } + publication { + ... on Post { + ...Post + } + ... on Comment { + ...Comment + } + ... on Quote { + ...Quote + } + } +} + +fragment QuoteNotification on QuoteNotification { + id + quote { + ...Quote + } +} + +fragment ActedNotification on ActedNotification { + id + actions { + profile { + ...ProfileFields + } + actions { + ... on CollectOpenActionResult { + ...CollectOpenActionResult + } + ... on NftDropOpenAction { + ...NftDropOpenAction + } + ... on UnknownOpenActionResult { + ...UnknownOpenActionResult + } + } + } + publication { + ... on Post { + ...Post + } + ... on Comment { + ...Comment + } + ... on Mirror { + ...Mirror + } + ... on Quote { + ...Quote + } + } +} + +fragment FollowNotification on FollowNotification { + id + followers { + ...ProfileFields + } +} + +fragment MentionNotification on MentionNotification { + id + publication { + ... on Post { + ...Post + } + ... on Comment { + ...Comment + } + ... on Quote { + ...Quote + } + } +} + +fragment FutureProofNotification on FutureProofNotification { + id +} + +# queries +query Notifications( + $request: NotificationRequest! + $observerId: ProfileId + $publicationImageTransform: ImageTransform = {} + $profileCoverTransform: ImageTransform = {} + $profilePictureTransform: ImageTransform = {} +) { + result: notifications(request: $request) { + items { + ... on ReactionNotification { + ...ReactionNotification + } + ... on CommentNotification { + ...CommentNotification + } + ... on MirrorNotification { + ...MirrorNotification + } + ... on QuoteNotification { + ...QuoteNotification + } + ... on ActedNotification { + ...ActedNotification + } + ... on FollowNotification { + ...FollowNotification + } + ... on MentionNotification { + ...MentionNotification + } + ... on FutureProofNotification { + ...FutureProofNotification + } + } + pageInfo { + ...PaginatedResultInfo + } + } +} diff --git a/packages/client/src/notifications/index.ts b/packages/client/src/notifications/index.ts new file mode 100644 index 0000000000..e7825351af --- /dev/null +++ b/packages/client/src/notifications/index.ts @@ -0,0 +1 @@ +export * from './Notifications';