diff --git a/packages/api/src/generated/graphql.ts b/packages/api/src/generated/graphql.ts index 86039cb1a..b52e38e24 100644 --- a/packages/api/src/generated/graphql.ts +++ b/packages/api/src/generated/graphql.ts @@ -17,6 +17,24 @@ export type Scalars = { JSON: any; }; +export type AddFollowingToLibraryError = { + __typename?: 'AddFollowingToLibraryError'; + errorCodes: Array; +}; + +export enum AddFollowingToLibraryErrorCode { + BadRequest = 'BAD_REQUEST', + NotFound = 'NOT_FOUND', + Unauthorized = 'UNAUTHORIZED' +} + +export type AddFollowingToLibraryResult = AddFollowingToLibraryError | AddFollowingToLibrarySuccess; + +export type AddFollowingToLibrarySuccess = { + __typename?: 'AddFollowingToLibrarySuccess'; + articleSavingRequest: ArticleSavingRequest; +}; + export type AddPopularReadError = { __typename?: 'AddPopularReadError'; errorCodes: Array; @@ -1305,6 +1323,7 @@ export type MoveLabelSuccess = { export type Mutation = { __typename?: 'Mutation'; + addFollowingToLibrary: AddFollowingToLibraryResult; addPopularRead: AddPopularReadResult; bulkAction: BulkActionResult; createArticle: CreateArticleResult; @@ -1368,6 +1387,11 @@ export type Mutation = { }; +export type MutationAddFollowingToLibraryArgs = { + id: Scalars['ID']; +}; + + export type MutationAddPopularReadArgs = { name: Scalars['String']; }; @@ -3429,6 +3453,10 @@ export type DirectiveResolverFn; + AddFollowingToLibraryErrorCode: AddFollowingToLibraryErrorCode; + AddFollowingToLibraryResult: ResolversTypes['AddFollowingToLibraryError'] | ResolversTypes['AddFollowingToLibrarySuccess']; + AddFollowingToLibrarySuccess: ResolverTypeWrapper; AddPopularReadError: ResolverTypeWrapper; AddPopularReadErrorCode: AddPopularReadErrorCode; AddPopularReadResult: ResolversTypes['AddPopularReadError'] | ResolversTypes['AddPopularReadSuccess']; @@ -3953,6 +3981,9 @@ export type ResolversTypes = { /** Mapping between all available schema types and the resolvers parents */ export type ResolversParentTypes = { + AddFollowingToLibraryError: AddFollowingToLibraryError; + AddFollowingToLibraryResult: ResolversParentTypes['AddFollowingToLibraryError'] | ResolversParentTypes['AddFollowingToLibrarySuccess']; + AddFollowingToLibrarySuccess: AddFollowingToLibrarySuccess; AddPopularReadError: AddPopularReadError; AddPopularReadResult: ResolversParentTypes['AddPopularReadError'] | ResolversParentTypes['AddPopularReadSuccess']; AddPopularReadSuccess: AddPopularReadSuccess; @@ -4367,6 +4398,20 @@ export type SanitizeDirectiveArgs = { export type SanitizeDirectiveResolver = DirectiveResolverFn; +export type AddFollowingToLibraryErrorResolvers = { + errorCodes?: Resolver, ParentType, ContextType>; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type AddFollowingToLibraryResultResolvers = { + __resolveType: TypeResolveFn<'AddFollowingToLibraryError' | 'AddFollowingToLibrarySuccess', ParentType, ContextType>; +}; + +export type AddFollowingToLibrarySuccessResolvers = { + articleSavingRequest?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type AddPopularReadErrorResolvers = { errorCodes?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; @@ -5308,6 +5353,7 @@ export type MoveLabelSuccessResolvers = { + addFollowingToLibrary?: Resolver>; addPopularRead?: Resolver>; bulkAction?: Resolver>; createArticle?: Resolver>; @@ -6386,6 +6432,9 @@ export type WebhooksSuccessResolvers = { + AddFollowingToLibraryError?: AddFollowingToLibraryErrorResolvers; + AddFollowingToLibraryResult?: AddFollowingToLibraryResultResolvers; + AddFollowingToLibrarySuccess?: AddFollowingToLibrarySuccessResolvers; AddPopularReadError?: AddPopularReadErrorResolvers; AddPopularReadResult?: AddPopularReadResultResolvers; AddPopularReadSuccess?: AddPopularReadSuccessResolvers; diff --git a/packages/api/src/generated/schema.graphql b/packages/api/src/generated/schema.graphql index 120c9d275..848ef48d8 100644 --- a/packages/api/src/generated/schema.graphql +++ b/packages/api/src/generated/schema.graphql @@ -1,5 +1,21 @@ directive @sanitize(allowedTags: [String], maxLength: Int, minLength: Int, pattern: String) on INPUT_FIELD_DEFINITION +type AddFollowingToLibraryError { + errorCodes: [AddFollowingToLibraryErrorCode!]! +} + +enum AddFollowingToLibraryErrorCode { + BAD_REQUEST + NOT_FOUND + UNAUTHORIZED +} + +union AddFollowingToLibraryResult = AddFollowingToLibraryError | AddFollowingToLibrarySuccess + +type AddFollowingToLibrarySuccess { + articleSavingRequest: ArticleSavingRequest! +} + type AddPopularReadError { errorCodes: [AddPopularReadErrorCode!]! } @@ -1169,6 +1185,7 @@ type MoveLabelSuccess { } type Mutation { + addFollowingToLibrary(id: ID!): AddFollowingToLibraryResult! addPopularRead(name: String!): AddPopularReadResult! bulkAction(action: BulkActionType!, async: Boolean, expectedCount: Int, labelIds: [ID!], query: String!): BulkActionResult! createArticle(input: CreateArticleInput!): CreateArticleResult! diff --git a/packages/api/src/resolvers/following/index.ts b/packages/api/src/resolvers/following/index.ts index b97951e54..d7ea0129d 100644 --- a/packages/api/src/resolvers/following/index.ts +++ b/packages/api/src/resolvers/following/index.ts @@ -1,15 +1,23 @@ +import { IsNull, Not } from 'typeorm' +import { LibraryItem } from '../../entity/library_item' import { + AddFollowingToLibraryError, + AddFollowingToLibraryErrorCode, + AddFollowingToLibrarySuccess, FeedEdge, FeedsError, FeedsErrorCode, FeedsSuccess, + MutationAddFollowingToLibraryArgs, MutationSaveFollowingArgs, QueryFeedsArgs, SaveFollowingError, SaveFollowingSuccess, } from '../../generated/graphql' import { feedRepository } from '../../repository/feed' +import { createPageSaveRequest } from '../../services/create_page_save_request' import { createFollowing } from '../../services/library_item' +import { analytics } from '../../utils/analytics' import { authorized } from '../../utils/helpers' export const feedsResolve = authorized< @@ -69,6 +77,14 @@ export const saveFollowingResolver = authorized< SaveFollowingError, MutationSaveFollowingArgs >(async (_, { input }, { uid }) => { + analytics.track({ + userId: uid, + event: 'save_following', + properties: { + url: input.url, + }, + }) + const newItem = await createFollowing(input, uid) return { @@ -82,3 +98,49 @@ export const saveFollowingResolver = authorized< }, } }) + +export const addFollowingToLibraryResolver = authorized< + AddFollowingToLibrarySuccess, + AddFollowingToLibraryError, + MutationAddFollowingToLibraryArgs +>(async (_, { id }, { authTrx, pubsub, uid }) => { + analytics.track({ + userId: uid, + event: 'add_following_to_library', + properties: { + id, + }, + }) + + const item = await authTrx((tx) => + tx.getRepository(LibraryItem).findOne({ + where: { + id, + sharedAt: Not(IsNull()), + isInLibrary: false, + }, + relations: ['user'], + }) + ) + + if (!item) { + return { + errorCodes: [AddFollowingToLibraryErrorCode.NotFound], + } + } + + const articleSavingRequest = await createPageSaveRequest({ + userId: uid, + url: item.originalUrl, + articleSavingRequestId: id, + priority: 'high', + publishedAt: item.publishedAt || undefined, + savedAt: item.savedAt || undefined, + pubsub, + }) + + return { + __typename: 'AddFollowingToLibrarySuccess', + articleSavingRequest, + } +}) diff --git a/packages/api/src/schema.ts b/packages/api/src/schema.ts index f5420fc29..f1c4ed27c 100755 --- a/packages/api/src/schema.ts +++ b/packages/api/src/schema.ts @@ -2687,6 +2687,24 @@ const schema = gql` sharedAt: Date! } + union AddFollowingToLibraryResult = + AddFollowingToLibrarySuccess + | AddFollowingToLibraryError + + type AddFollowingToLibrarySuccess { + articleSavingRequest: ArticleSavingRequest! + } + + type AddFollowingToLibraryError { + errorCodes: [AddFollowingToLibraryErrorCode!]! + } + + enum AddFollowingToLibraryErrorCode { + UNAUTHORIZED + BAD_REQUEST + NOT_FOUND + } + # Mutations type Mutation { googleLogin(input: GoogleLoginInput!): LoginResult! @@ -2791,6 +2809,7 @@ const schema = gql` input: UpdateSubscriptionInput! ): UpdateSubscriptionResult! saveFollowing(input: SaveFollowingInput!): SaveFollowingResult! + addFollowingToLibrary(id: ID!): AddFollowingToLibraryResult! } # FIXME: remove sort from feedArticles after all cached tabs are closed