mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
add update email form on account page
This commit is contained in:
parent
c40e64773a
commit
ee50529f95
10 changed files with 269 additions and 114 deletions
|
|
@ -252,28 +252,6 @@ export enum ContentReader {
|
|||
Web = 'WEB'
|
||||
}
|
||||
|
||||
export type ConvertToEmailError = {
|
||||
__typename?: 'ConvertToEmailError';
|
||||
errorCodes: Array<ConvertToEmailErrorCode>;
|
||||
};
|
||||
|
||||
export enum ConvertToEmailErrorCode {
|
||||
BadRequest = 'BAD_REQUEST',
|
||||
EmailAlreadyExists = 'EMAIL_ALREADY_EXISTS',
|
||||
Unauthorized = 'UNAUTHORIZED'
|
||||
}
|
||||
|
||||
export type ConvertToEmailInput = {
|
||||
email: Scalars['String'];
|
||||
};
|
||||
|
||||
export type ConvertToEmailResult = ConvertToEmailError | ConvertToEmailSuccess;
|
||||
|
||||
export type ConvertToEmailSuccess = {
|
||||
__typename?: 'ConvertToEmailSuccess';
|
||||
success: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
export type CreateArticleError = {
|
||||
__typename?: 'CreateArticleError';
|
||||
errorCodes: Array<CreateArticleErrorCode>;
|
||||
|
|
@ -1257,7 +1235,6 @@ export type Mutation = {
|
|||
__typename?: 'Mutation';
|
||||
addPopularRead: AddPopularReadResult;
|
||||
bulkAction: BulkActionResult;
|
||||
convertToEmail: ConvertToEmailResult;
|
||||
createArticle: CreateArticleResult;
|
||||
createArticleSavingRequest: CreateArticleSavingRequestResult;
|
||||
createGroup: CreateGroupResult;
|
||||
|
|
@ -1314,6 +1291,7 @@ export type Mutation = {
|
|||
setWebhook: SetWebhookResult;
|
||||
subscribe: SubscribeResult;
|
||||
unsubscribe: UnsubscribeResult;
|
||||
updateEmail: UpdateEmailResult;
|
||||
updateFilter: UpdateFilterResult;
|
||||
updateHighlight: UpdateHighlightResult;
|
||||
updateHighlightReply: UpdateHighlightReplyResult;
|
||||
|
|
@ -1344,11 +1322,6 @@ export type MutationBulkActionArgs = {
|
|||
};
|
||||
|
||||
|
||||
export type MutationConvertToEmailArgs = {
|
||||
input: ConvertToEmailInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationCreateArticleArgs = {
|
||||
input: CreateArticleInput;
|
||||
};
|
||||
|
|
@ -1620,6 +1593,11 @@ export type MutationUnsubscribeArgs = {
|
|||
};
|
||||
|
||||
|
||||
export type MutationUpdateEmailArgs = {
|
||||
input: UpdateEmailInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationUpdateFilterArgs = {
|
||||
input: UpdateFilterInput;
|
||||
};
|
||||
|
|
@ -2873,6 +2851,29 @@ export type UnsubscribeSuccess = {
|
|||
subscription: Subscription;
|
||||
};
|
||||
|
||||
export type UpdateEmailError = {
|
||||
__typename?: 'UpdateEmailError';
|
||||
errorCodes: Array<UpdateEmailErrorCode>;
|
||||
};
|
||||
|
||||
export enum UpdateEmailErrorCode {
|
||||
BadRequest = 'BAD_REQUEST',
|
||||
EmailAlreadyExists = 'EMAIL_ALREADY_EXISTS',
|
||||
Unauthorized = 'UNAUTHORIZED'
|
||||
}
|
||||
|
||||
export type UpdateEmailInput = {
|
||||
email: Scalars['String'];
|
||||
};
|
||||
|
||||
export type UpdateEmailResult = UpdateEmailError | UpdateEmailSuccess;
|
||||
|
||||
export type UpdateEmailSuccess = {
|
||||
__typename?: 'UpdateEmailSuccess';
|
||||
email: Scalars['String'];
|
||||
verificationEmailSent?: Maybe<Scalars['Boolean']>;
|
||||
};
|
||||
|
||||
export type UpdateFilterError = {
|
||||
__typename?: 'UpdateFilterError';
|
||||
errorCodes: Array<UpdateFilterErrorCode>;
|
||||
|
|
@ -3239,6 +3240,7 @@ export enum UploadImportFileType {
|
|||
|
||||
export type User = {
|
||||
__typename?: 'User';
|
||||
email?: Maybe<Scalars['String']>;
|
||||
followersCount?: Maybe<Scalars['Int']>;
|
||||
friendsCount?: Maybe<Scalars['Int']>;
|
||||
id: Scalars['ID'];
|
||||
|
|
@ -3252,6 +3254,7 @@ export type User = {
|
|||
sharedArticlesCount?: Maybe<Scalars['Int']>;
|
||||
sharedHighlightsCount?: Maybe<Scalars['Int']>;
|
||||
sharedNotesCount?: Maybe<Scalars['Int']>;
|
||||
source?: Maybe<Scalars['String']>;
|
||||
viewerIsFollowing?: Maybe<Scalars['Boolean']>;
|
||||
};
|
||||
|
||||
|
|
@ -3470,11 +3473,6 @@ export type ResolversTypes = {
|
|||
BulkActionSuccess: ResolverTypeWrapper<BulkActionSuccess>;
|
||||
BulkActionType: BulkActionType;
|
||||
ContentReader: ContentReader;
|
||||
ConvertToEmailError: ResolverTypeWrapper<ConvertToEmailError>;
|
||||
ConvertToEmailErrorCode: ConvertToEmailErrorCode;
|
||||
ConvertToEmailInput: ConvertToEmailInput;
|
||||
ConvertToEmailResult: ResolversTypes['ConvertToEmailError'] | ResolversTypes['ConvertToEmailSuccess'];
|
||||
ConvertToEmailSuccess: ResolverTypeWrapper<ConvertToEmailSuccess>;
|
||||
CreateArticleError: ResolverTypeWrapper<CreateArticleError>;
|
||||
CreateArticleErrorCode: CreateArticleErrorCode;
|
||||
CreateArticleInput: CreateArticleInput;
|
||||
|
|
@ -3846,6 +3844,11 @@ export type ResolversTypes = {
|
|||
UnsubscribeErrorCode: UnsubscribeErrorCode;
|
||||
UnsubscribeResult: ResolversTypes['UnsubscribeError'] | ResolversTypes['UnsubscribeSuccess'];
|
||||
UnsubscribeSuccess: ResolverTypeWrapper<UnsubscribeSuccess>;
|
||||
UpdateEmailError: ResolverTypeWrapper<UpdateEmailError>;
|
||||
UpdateEmailErrorCode: UpdateEmailErrorCode;
|
||||
UpdateEmailInput: UpdateEmailInput;
|
||||
UpdateEmailResult: ResolversTypes['UpdateEmailError'] | ResolversTypes['UpdateEmailSuccess'];
|
||||
UpdateEmailSuccess: ResolverTypeWrapper<UpdateEmailSuccess>;
|
||||
UpdateFilterError: ResolverTypeWrapper<UpdateFilterError>;
|
||||
UpdateFilterErrorCode: UpdateFilterErrorCode;
|
||||
UpdateFilterInput: UpdateFilterInput;
|
||||
|
|
@ -3969,10 +3972,6 @@ export type ResolversParentTypes = {
|
|||
BulkActionError: BulkActionError;
|
||||
BulkActionResult: ResolversParentTypes['BulkActionError'] | ResolversParentTypes['BulkActionSuccess'];
|
||||
BulkActionSuccess: BulkActionSuccess;
|
||||
ConvertToEmailError: ConvertToEmailError;
|
||||
ConvertToEmailInput: ConvertToEmailInput;
|
||||
ConvertToEmailResult: ResolversParentTypes['ConvertToEmailError'] | ResolversParentTypes['ConvertToEmailSuccess'];
|
||||
ConvertToEmailSuccess: ConvertToEmailSuccess;
|
||||
CreateArticleError: CreateArticleError;
|
||||
CreateArticleInput: CreateArticleInput;
|
||||
CreateArticleResult: ResolversParentTypes['CreateArticleError'] | ResolversParentTypes['CreateArticleSuccess'];
|
||||
|
|
@ -4263,6 +4262,10 @@ export type ResolversParentTypes = {
|
|||
UnsubscribeError: UnsubscribeError;
|
||||
UnsubscribeResult: ResolversParentTypes['UnsubscribeError'] | ResolversParentTypes['UnsubscribeSuccess'];
|
||||
UnsubscribeSuccess: UnsubscribeSuccess;
|
||||
UpdateEmailError: UpdateEmailError;
|
||||
UpdateEmailInput: UpdateEmailInput;
|
||||
UpdateEmailResult: ResolversParentTypes['UpdateEmailError'] | ResolversParentTypes['UpdateEmailSuccess'];
|
||||
UpdateEmailSuccess: UpdateEmailSuccess;
|
||||
UpdateFilterError: UpdateFilterError;
|
||||
UpdateFilterInput: UpdateFilterInput;
|
||||
UpdateFilterResult: ResolversParentTypes['UpdateFilterError'] | ResolversParentTypes['UpdateFilterSuccess'];
|
||||
|
|
@ -4519,20 +4522,6 @@ export type BulkActionSuccessResolvers<ContextType = ResolverContext, ParentType
|
|||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type ConvertToEmailErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['ConvertToEmailError'] = ResolversParentTypes['ConvertToEmailError']> = {
|
||||
errorCodes?: Resolver<Array<ResolversTypes['ConvertToEmailErrorCode']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type ConvertToEmailResultResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['ConvertToEmailResult'] = ResolversParentTypes['ConvertToEmailResult']> = {
|
||||
__resolveType: TypeResolveFn<'ConvertToEmailError' | 'ConvertToEmailSuccess', ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type ConvertToEmailSuccessResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['ConvertToEmailSuccess'] = ResolversParentTypes['ConvertToEmailSuccess']> = {
|
||||
success?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type CreateArticleErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['CreateArticleError'] = ResolversParentTypes['CreateArticleError']> = {
|
||||
errorCodes?: Resolver<Array<ResolversTypes['CreateArticleErrorCode']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
|
|
@ -5242,7 +5231,6 @@ export type MoveLabelSuccessResolvers<ContextType = ResolverContext, ParentType
|
|||
export type MutationResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['Mutation'] = ResolversParentTypes['Mutation']> = {
|
||||
addPopularRead?: Resolver<ResolversTypes['AddPopularReadResult'], ParentType, ContextType, RequireFields<MutationAddPopularReadArgs, 'name'>>;
|
||||
bulkAction?: Resolver<ResolversTypes['BulkActionResult'], ParentType, ContextType, RequireFields<MutationBulkActionArgs, 'action' | 'query'>>;
|
||||
convertToEmail?: Resolver<ResolversTypes['ConvertToEmailResult'], ParentType, ContextType, RequireFields<MutationConvertToEmailArgs, 'input'>>;
|
||||
createArticle?: Resolver<ResolversTypes['CreateArticleResult'], ParentType, ContextType, RequireFields<MutationCreateArticleArgs, 'input'>>;
|
||||
createArticleSavingRequest?: Resolver<ResolversTypes['CreateArticleSavingRequestResult'], ParentType, ContextType, RequireFields<MutationCreateArticleSavingRequestArgs, 'input'>>;
|
||||
createGroup?: Resolver<ResolversTypes['CreateGroupResult'], ParentType, ContextType, RequireFields<MutationCreateGroupArgs, 'input'>>;
|
||||
|
|
@ -5299,6 +5287,7 @@ export type MutationResolvers<ContextType = ResolverContext, ParentType extends
|
|||
setWebhook?: Resolver<ResolversTypes['SetWebhookResult'], ParentType, ContextType, RequireFields<MutationSetWebhookArgs, 'input'>>;
|
||||
subscribe?: Resolver<ResolversTypes['SubscribeResult'], ParentType, ContextType, RequireFields<MutationSubscribeArgs, 'input'>>;
|
||||
unsubscribe?: Resolver<ResolversTypes['UnsubscribeResult'], ParentType, ContextType, RequireFields<MutationUnsubscribeArgs, 'name'>>;
|
||||
updateEmail?: Resolver<ResolversTypes['UpdateEmailResult'], ParentType, ContextType, RequireFields<MutationUpdateEmailArgs, 'input'>>;
|
||||
updateFilter?: Resolver<ResolversTypes['UpdateFilterResult'], ParentType, ContextType, RequireFields<MutationUpdateFilterArgs, 'input'>>;
|
||||
updateHighlight?: Resolver<ResolversTypes['UpdateHighlightResult'], ParentType, ContextType, RequireFields<MutationUpdateHighlightArgs, 'input'>>;
|
||||
updateHighlightReply?: Resolver<ResolversTypes['UpdateHighlightReplyResult'], ParentType, ContextType, RequireFields<MutationUpdateHighlightReplyArgs, 'input'>>;
|
||||
|
|
@ -6007,6 +5996,21 @@ export type UnsubscribeSuccessResolvers<ContextType = ResolverContext, ParentTyp
|
|||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type UpdateEmailErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['UpdateEmailError'] = ResolversParentTypes['UpdateEmailError']> = {
|
||||
errorCodes?: Resolver<Array<ResolversTypes['UpdateEmailErrorCode']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type UpdateEmailResultResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['UpdateEmailResult'] = ResolversParentTypes['UpdateEmailResult']> = {
|
||||
__resolveType: TypeResolveFn<'UpdateEmailError' | 'UpdateEmailSuccess', ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type UpdateEmailSuccessResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['UpdateEmailSuccess'] = ResolversParentTypes['UpdateEmailSuccess']> = {
|
||||
email?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
||||
verificationEmailSent?: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
||||
export type UpdateFilterErrorResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['UpdateFilterError'] = ResolversParentTypes['UpdateFilterError']> = {
|
||||
errorCodes?: Resolver<Array<ResolversTypes['UpdateFilterErrorCode']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
|
|
@ -6209,6 +6213,7 @@ export type UploadImportFileSuccessResolvers<ContextType = ResolverContext, Pare
|
|||
};
|
||||
|
||||
export type UserResolvers<ContextType = ResolverContext, ParentType extends ResolversParentTypes['User'] = ResolversParentTypes['User']> = {
|
||||
email?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
followersCount?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
|
||||
friendsCount?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
|
||||
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
|
||||
|
|
@ -6221,6 +6226,7 @@ export type UserResolvers<ContextType = ResolverContext, ParentType extends Reso
|
|||
sharedArticlesCount?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
|
||||
sharedHighlightsCount?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
|
||||
sharedNotesCount?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
|
||||
source?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
||||
viewerIsFollowing?: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>;
|
||||
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
||||
};
|
||||
|
|
@ -6334,9 +6340,6 @@ export type Resolvers<ContextType = ResolverContext> = {
|
|||
BulkActionError?: BulkActionErrorResolvers<ContextType>;
|
||||
BulkActionResult?: BulkActionResultResolvers<ContextType>;
|
||||
BulkActionSuccess?: BulkActionSuccessResolvers<ContextType>;
|
||||
ConvertToEmailError?: ConvertToEmailErrorResolvers<ContextType>;
|
||||
ConvertToEmailResult?: ConvertToEmailResultResolvers<ContextType>;
|
||||
ConvertToEmailSuccess?: ConvertToEmailSuccessResolvers<ContextType>;
|
||||
CreateArticleError?: CreateArticleErrorResolvers<ContextType>;
|
||||
CreateArticleResult?: CreateArticleResultResolvers<ContextType>;
|
||||
CreateArticleSavingRequestError?: CreateArticleSavingRequestErrorResolvers<ContextType>;
|
||||
|
|
@ -6583,6 +6586,9 @@ export type Resolvers<ContextType = ResolverContext> = {
|
|||
UnsubscribeError?: UnsubscribeErrorResolvers<ContextType>;
|
||||
UnsubscribeResult?: UnsubscribeResultResolvers<ContextType>;
|
||||
UnsubscribeSuccess?: UnsubscribeSuccessResolvers<ContextType>;
|
||||
UpdateEmailError?: UpdateEmailErrorResolvers<ContextType>;
|
||||
UpdateEmailResult?: UpdateEmailResultResolvers<ContextType>;
|
||||
UpdateEmailSuccess?: UpdateEmailSuccessResolvers<ContextType>;
|
||||
UpdateFilterError?: UpdateFilterErrorResolvers<ContextType>;
|
||||
UpdateFilterResult?: UpdateFilterResultResolvers<ContextType>;
|
||||
UpdateFilterSuccess?: UpdateFilterSuccessResolvers<ContextType>;
|
||||
|
|
|
|||
|
|
@ -211,26 +211,6 @@ enum ContentReader {
|
|||
WEB
|
||||
}
|
||||
|
||||
type ConvertToEmailError {
|
||||
errorCodes: [ConvertToEmailErrorCode!]!
|
||||
}
|
||||
|
||||
enum ConvertToEmailErrorCode {
|
||||
BAD_REQUEST
|
||||
EMAIL_ALREADY_EXISTS
|
||||
UNAUTHORIZED
|
||||
}
|
||||
|
||||
input ConvertToEmailInput {
|
||||
email: String!
|
||||
}
|
||||
|
||||
union ConvertToEmailResult = ConvertToEmailError | ConvertToEmailSuccess
|
||||
|
||||
type ConvertToEmailSuccess {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type CreateArticleError {
|
||||
errorCodes: [CreateArticleErrorCode!]!
|
||||
}
|
||||
|
|
@ -1123,7 +1103,6 @@ type MoveLabelSuccess {
|
|||
type Mutation {
|
||||
addPopularRead(name: String!): AddPopularReadResult!
|
||||
bulkAction(action: BulkActionType!, async: Boolean, expectedCount: Int, labelIds: [ID!], query: String!): BulkActionResult!
|
||||
convertToEmail(input: ConvertToEmailInput!): ConvertToEmailResult!
|
||||
createArticle(input: CreateArticleInput!): CreateArticleResult!
|
||||
createArticleSavingRequest(input: CreateArticleSavingRequestInput!): CreateArticleSavingRequestResult!
|
||||
createGroup(input: CreateGroupInput!): CreateGroupResult!
|
||||
|
|
@ -1180,6 +1159,7 @@ type Mutation {
|
|||
setWebhook(input: SetWebhookInput!): SetWebhookResult!
|
||||
subscribe(input: SubscribeInput!): SubscribeResult!
|
||||
unsubscribe(name: String!, subscriptionId: ID): UnsubscribeResult!
|
||||
updateEmail(input: UpdateEmailInput!): UpdateEmailResult!
|
||||
updateFilter(input: UpdateFilterInput!): UpdateFilterResult!
|
||||
updateHighlight(input: UpdateHighlightInput!): UpdateHighlightResult!
|
||||
updateHighlightReply(input: UpdateHighlightReplyInput!): UpdateHighlightReplyResult!
|
||||
|
|
@ -2198,6 +2178,27 @@ type UnsubscribeSuccess {
|
|||
subscription: Subscription!
|
||||
}
|
||||
|
||||
type UpdateEmailError {
|
||||
errorCodes: [UpdateEmailErrorCode!]!
|
||||
}
|
||||
|
||||
enum UpdateEmailErrorCode {
|
||||
BAD_REQUEST
|
||||
EMAIL_ALREADY_EXISTS
|
||||
UNAUTHORIZED
|
||||
}
|
||||
|
||||
input UpdateEmailInput {
|
||||
email: String!
|
||||
}
|
||||
|
||||
union UpdateEmailResult = UpdateEmailError | UpdateEmailSuccess
|
||||
|
||||
type UpdateEmailSuccess {
|
||||
email: String!
|
||||
verificationEmailSent: Boolean
|
||||
}
|
||||
|
||||
type UpdateFilterError {
|
||||
errorCodes: [UpdateFilterErrorCode!]!
|
||||
}
|
||||
|
|
@ -2535,6 +2536,7 @@ enum UploadImportFileType {
|
|||
}
|
||||
|
||||
type User {
|
||||
email: String
|
||||
followersCount: Int
|
||||
friendsCount: Int
|
||||
id: ID!
|
||||
|
|
@ -2547,6 +2549,7 @@ type User {
|
|||
sharedArticlesCount: Int
|
||||
sharedHighlightsCount: Int
|
||||
sharedNotesCount: Int
|
||||
source: String
|
||||
viewerIsFollowing: Boolean
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ import { createReactionResolver, deleteReactionResolver } from './reaction'
|
|||
import { markEmailAsItemResolver, recentEmailsResolver } from './recent_emails'
|
||||
import { recentSearchesResolver } from './recent_searches'
|
||||
import { Claims, WithDataSourcesContext } from './types'
|
||||
import { convertToEmailResolver } from './user'
|
||||
import { updateEmailResolver } from './user'
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
type ResultResolveType = {
|
||||
|
|
@ -213,7 +213,7 @@ export const functionResolvers = {
|
|||
setFavoriteArticle: setFavoriteArticleResolver,
|
||||
updateSubscription: updateSubscriptionResolver,
|
||||
updateFilter: updateFilterResolver,
|
||||
convertToEmail: convertToEmailResolver,
|
||||
updateEmail: updateEmailResolver,
|
||||
},
|
||||
Query: {
|
||||
me: getMeUserResolver,
|
||||
|
|
@ -680,5 +680,5 @@ export const functionResolvers = {
|
|||
...resultResolveTypeResolver('ImportFromIntegration'),
|
||||
...resultResolveTypeResolver('SetFavoriteArticle'),
|
||||
...resultResolveTypeResolver('UpdateSubscription'),
|
||||
...resultResolveTypeResolver('ConvertToEmail'),
|
||||
...resultResolveTypeResolver('UpdateEmail'),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
import * as jwt from 'jsonwebtoken'
|
||||
import { Not } from 'typeorm'
|
||||
import { RegistrationType } from '../../datalayer/user/model'
|
||||
import { deletePagesByParam } from '../../elastic/pages'
|
||||
import { User as UserEntity } from '../../entity/user'
|
||||
import { getRepository, setClaims } from '../../entity/utils'
|
||||
import { env } from '../../env'
|
||||
import {
|
||||
ConvertToEmailError,
|
||||
ConvertToEmailErrorCode,
|
||||
ConvertToEmailSuccess,
|
||||
DeleteAccountError,
|
||||
DeleteAccountErrorCode,
|
||||
DeleteAccountSuccess,
|
||||
|
|
@ -16,16 +13,19 @@ import {
|
|||
LoginResult,
|
||||
LogOutErrorCode,
|
||||
LogOutResult,
|
||||
MutationConvertToEmailArgs,
|
||||
MutationDeleteAccountArgs,
|
||||
MutationGoogleLoginArgs,
|
||||
MutationGoogleSignupArgs,
|
||||
MutationUpdateEmailArgs,
|
||||
MutationUpdateUserArgs,
|
||||
MutationUpdateUserProfileArgs,
|
||||
QueryUserArgs,
|
||||
QueryValidateUsernameArgs,
|
||||
ResolverFn,
|
||||
SignupErrorCode,
|
||||
UpdateEmailError,
|
||||
UpdateEmailErrorCode,
|
||||
UpdateEmailSuccess,
|
||||
UpdateUserError,
|
||||
UpdateUserErrorCode,
|
||||
UpdateUserProfileError,
|
||||
|
|
@ -344,34 +344,49 @@ export const deleteAccountResolver = authorized<
|
|||
return { userID }
|
||||
})
|
||||
|
||||
export const convertToEmailResolver = authorized<
|
||||
ConvertToEmailSuccess,
|
||||
ConvertToEmailError,
|
||||
MutationConvertToEmailArgs
|
||||
export const updateEmailResolver = authorized<
|
||||
UpdateEmailSuccess,
|
||||
UpdateEmailError,
|
||||
MutationUpdateEmailArgs
|
||||
>(async (_, { input: { email } }, { uid, log }) => {
|
||||
try {
|
||||
const user = await getRepository(UserEntity).findOneBy({
|
||||
id: uid,
|
||||
source: Not('EMAIL'),
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
return {
|
||||
errorCodes: [ConvertToEmailErrorCode.Unauthorized],
|
||||
errorCodes: [UpdateEmailErrorCode.Unauthorized],
|
||||
}
|
||||
}
|
||||
|
||||
if (user.source === RegistrationType.Email) {
|
||||
await AppDataSource.transaction(async (entityManager) => {
|
||||
await setClaims(entityManager, user.id)
|
||||
return entityManager.getRepository(UserEntity).update(user.id, {
|
||||
email,
|
||||
})
|
||||
})
|
||||
|
||||
return { email }
|
||||
}
|
||||
|
||||
const result = await sendVerificationEmail({
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
email,
|
||||
})
|
||||
if (!result) {
|
||||
return {
|
||||
errorCodes: [UpdateEmailErrorCode.BadRequest],
|
||||
}
|
||||
}
|
||||
|
||||
return { success: result }
|
||||
return { email, verificationEmailSent: true }
|
||||
} catch (error) {
|
||||
log.error('Error converting user to email', error)
|
||||
log.error('Error updating email', error)
|
||||
return {
|
||||
errorCodes: [ConvertToEmailErrorCode.BadRequest],
|
||||
errorCodes: [UpdateEmailErrorCode.BadRequest],
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -672,7 +672,6 @@ export function authRouter() {
|
|||
|
||||
const user = await getRepository(User).findOneBy({
|
||||
id: claims.uid,
|
||||
source: 'EMAIL',
|
||||
})
|
||||
if (!user) {
|
||||
return res.redirect(
|
||||
|
|
@ -686,13 +685,17 @@ export function authRouter() {
|
|||
)
|
||||
}
|
||||
|
||||
// check if email needs to be updated
|
||||
const updateEmail = claims.email && claims.email !== user.email
|
||||
|
||||
const hashedPassword = await hashPassword(password)
|
||||
const updated = await AppDataSource.transaction(
|
||||
async (entityManager) => {
|
||||
await setClaims(entityManager, user.id)
|
||||
return entityManager.getRepository(User).update(user.id, {
|
||||
password: hashedPassword,
|
||||
email: claims.email,
|
||||
email: updateEmail ? claims.email : undefined,
|
||||
source: updateEmail ? RegistrationType.Email : undefined,
|
||||
})
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -84,6 +84,8 @@ const schema = gql`
|
|||
sharedNotesCount: Int
|
||||
friendsCount: Int
|
||||
followersCount: Int
|
||||
email: String
|
||||
source: String
|
||||
}
|
||||
|
||||
type Profile {
|
||||
|
|
@ -2567,23 +2569,24 @@ const schema = gql`
|
|||
NOT_FOUND
|
||||
}
|
||||
|
||||
union ConvertToEmailResult = ConvertToEmailSuccess | ConvertToEmailError
|
||||
union UpdateEmailResult = UpdateEmailSuccess | UpdateEmailError
|
||||
|
||||
type ConvertToEmailSuccess {
|
||||
success: Boolean!
|
||||
type UpdateEmailSuccess {
|
||||
email: String!
|
||||
verificationEmailSent: Boolean
|
||||
}
|
||||
|
||||
type ConvertToEmailError {
|
||||
errorCodes: [ConvertToEmailErrorCode!]!
|
||||
type UpdateEmailError {
|
||||
errorCodes: [UpdateEmailErrorCode!]!
|
||||
}
|
||||
|
||||
enum ConvertToEmailErrorCode {
|
||||
enum UpdateEmailErrorCode {
|
||||
UNAUTHORIZED
|
||||
BAD_REQUEST
|
||||
EMAIL_ALREADY_EXISTS
|
||||
}
|
||||
|
||||
input ConvertToEmailInput {
|
||||
input UpdateEmailInput {
|
||||
email: String!
|
||||
}
|
||||
|
||||
|
|
@ -2595,7 +2598,7 @@ const schema = gql`
|
|||
deleteAccount(userID: ID!): DeleteAccountResult!
|
||||
updateUser(input: UpdateUserInput!): UpdateUserResult!
|
||||
updateUserProfile(input: UpdateUserProfileInput!): UpdateUserProfileResult!
|
||||
convertToEmail(input: ConvertToEmailInput!): ConvertToEmailResult!
|
||||
updateEmail(input: UpdateEmailInput!): UpdateEmailResult!
|
||||
createArticle(input: CreateArticleInput!): CreateArticleResult!
|
||||
createHighlight(input: CreateHighlightInput!): CreateHighlightResult!
|
||||
mergeHighlight(input: MergeHighlightInput!): MergeHighlightResult!
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export const sendVerificationEmail = async (user: {
|
|||
}): Promise<boolean> => {
|
||||
// generate verification link
|
||||
const token = generateVerificationToken({ id: user.id, email: user.email })
|
||||
const link = `${env.client.url}/auth/convert-to-email/${token}`
|
||||
const link = `${env.client.url}/auth/reset-password/${token}`
|
||||
// send email
|
||||
const dynamicTemplateData = {
|
||||
name: user.name,
|
||||
|
|
|
|||
45
packages/web/lib/networking/mutations/updateEmailMutation.ts
Normal file
45
packages/web/lib/networking/mutations/updateEmailMutation.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { gql } from 'graphql-request'
|
||||
import { gqlFetcher } from '../networkHelpers'
|
||||
|
||||
export interface UpdateEmailInput {
|
||||
email: string
|
||||
}
|
||||
|
||||
export interface UpdateEmailSuccess {
|
||||
email: string
|
||||
verificationEmailSent: boolean
|
||||
}
|
||||
|
||||
interface Response {
|
||||
updateEmail: UpdateEmailSuccess
|
||||
}
|
||||
|
||||
export async function updateEmailMutation(
|
||||
input: UpdateEmailInput
|
||||
): Promise<UpdateEmailSuccess | undefined> {
|
||||
const mutation = gql`
|
||||
mutation UpdateEmail($input: UpdateEmailInput!) {
|
||||
updateEmail(input: $input) {
|
||||
... on UpdateEmailSuccess {
|
||||
email
|
||||
verificationEmailSent
|
||||
}
|
||||
... on UpdateEmailError {
|
||||
errorCodes
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
try {
|
||||
const data = await gqlFetcher(mutation, {
|
||||
input,
|
||||
})
|
||||
const output = data as Response
|
||||
return {
|
||||
email: output.updateEmail.email,
|
||||
verificationEmailSent: output.updateEmail.verificationEmailSent,
|
||||
}
|
||||
} catch (err) {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,8 @@ export type UserBasicData = {
|
|||
name: string
|
||||
isFullUser?: boolean
|
||||
profile: UserProfile
|
||||
email: string
|
||||
source: string
|
||||
}
|
||||
|
||||
export type UserProfile = {
|
||||
|
|
@ -39,6 +41,8 @@ export function useGetViewerQuery(): ViewerQueryResponse {
|
|||
pictureUrl
|
||||
bio
|
||||
}
|
||||
email
|
||||
source
|
||||
}
|
||||
}
|
||||
`
|
||||
|
|
|
|||
|
|
@ -1,23 +1,22 @@
|
|||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { showErrorToast, showSuccessToast } from '../../lib/toastHelpers'
|
||||
import { applyStoredTheme } from '../../lib/themeUpdater'
|
||||
|
||||
import { StyledText } from '../../components/elements/StyledText'
|
||||
import { useGetViewerQuery } from '../../lib/networking/queries/useGetViewerQuery'
|
||||
import { SettingsLayout } from '../../components/templates/SettingsLayout'
|
||||
import { Toaster } from 'react-hot-toast'
|
||||
import { Button } from '../../components/elements/Button'
|
||||
import {
|
||||
Box,
|
||||
SpanBox,
|
||||
VStack,
|
||||
} from '../../components/elements/LayoutPrimitives'
|
||||
import { Button } from '../../components/elements/Button'
|
||||
import { useValidateUsernameQuery } from '../../lib/networking/queries/useValidateUsernameQuery'
|
||||
import { StyledText } from '../../components/elements/StyledText'
|
||||
import { SettingsLayout } from '../../components/templates/SettingsLayout'
|
||||
import { styled } from '../../components/tokens/stitches.config'
|
||||
import { updateEmailMutation } from '../../lib/networking/mutations/updateEmailMutation'
|
||||
import { updateUserMutation } from '../../lib/networking/mutations/updateUserMutation'
|
||||
import { updateUserProfileMutation } from '../../lib/networking/mutations/updateUserProfileMutation'
|
||||
import { styled, theme } from '../../components/tokens/stitches.config'
|
||||
import { ProgressBar } from '../../components/elements/ProgressBar'
|
||||
import { useGetLibraryItemsQuery } from '../../lib/networking/queries/useGetLibraryItemsQuery'
|
||||
import { useGetViewerQuery } from '../../lib/networking/queries/useGetViewerQuery'
|
||||
import { useValidateUsernameQuery } from '../../lib/networking/queries/useValidateUsernameQuery'
|
||||
import { applyStoredTheme } from '../../lib/themeUpdater'
|
||||
import { showErrorToast, showSuccessToast } from '../../lib/toastHelpers'
|
||||
|
||||
const StyledLabel = styled('label', {
|
||||
fontWeight: 600,
|
||||
|
|
@ -49,6 +48,9 @@ export default function Account(): JSX.Element {
|
|||
const [username, setUsername] = useState('')
|
||||
const [nameUpdating, setNameUpdating] = useState(false)
|
||||
const [usernameUpdating, setUsernameUpdating] = useState(false)
|
||||
const [email, setEmail] = useState('')
|
||||
const [emailUpdating, setEmailUpdating] = useState(false)
|
||||
const [source, setSource] = useState('')
|
||||
|
||||
const [debouncedUsername, setDebouncedUsername] = useState('')
|
||||
const { usernameErrorMessage, isLoading: isUsernameValidationLoading } =
|
||||
|
|
@ -96,6 +98,18 @@ export default function Account(): JSX.Element {
|
|||
}
|
||||
}, [viewerData?.me?.name])
|
||||
|
||||
useEffect(() => {
|
||||
if (viewerData?.me?.email) {
|
||||
setEmail(viewerData?.me?.email)
|
||||
}
|
||||
}, [viewerData?.me?.email])
|
||||
|
||||
useEffect(() => {
|
||||
if (viewerData?.me?.source) {
|
||||
setSource(viewerData?.me?.source)
|
||||
}
|
||||
}, [viewerData?.me?.source])
|
||||
|
||||
const handleUsernameChange = useCallback(
|
||||
(event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
setUsername(event.target.value)
|
||||
|
|
@ -154,6 +168,24 @@ export default function Account(): JSX.Element {
|
|||
viewerData?.me,
|
||||
])
|
||||
|
||||
const updateEmail = useCallback(() => {
|
||||
setEmailUpdating(true)
|
||||
;(async () => {
|
||||
const response = await updateEmailMutation({ email })
|
||||
if (response) {
|
||||
setEmail(response.email)
|
||||
if (response.verificationEmailSent) {
|
||||
showSuccessToast('Verification email sent')
|
||||
} else {
|
||||
showSuccessToast('Email updated')
|
||||
}
|
||||
} else {
|
||||
showErrorToast('Error updating email')
|
||||
}
|
||||
setEmailUpdating(false)
|
||||
})()
|
||||
}, [email])
|
||||
|
||||
applyStoredTheme(false)
|
||||
|
||||
return (
|
||||
|
|
@ -281,6 +313,50 @@ export default function Account(): JSX.Element {
|
|||
</form>
|
||||
</VStack>
|
||||
|
||||
<VStack
|
||||
css={{
|
||||
padding: '24px',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
bg: '$grayBg',
|
||||
gap: '5px',
|
||||
borderRadius: '5px',
|
||||
}}
|
||||
>
|
||||
<form
|
||||
onSubmit={(event) => {
|
||||
updateEmail()
|
||||
event.preventDefault()
|
||||
}}
|
||||
>
|
||||
<StyledLabel>Email</StyledLabel>
|
||||
<FormInput
|
||||
type={'text'}
|
||||
placeholder={'Email'}
|
||||
value={email}
|
||||
disabled={emailUpdating}
|
||||
onChange={(event) => {
|
||||
setEmail(event.target.value)
|
||||
event.preventDefault()
|
||||
}}
|
||||
/>
|
||||
<StyledText style="footnote" css={{ mt: '10px', mb: '20px' }}>
|
||||
Your email is used for account recovery and notifications.
|
||||
</StyledText>
|
||||
{source == 'EMAIL' ? (
|
||||
<Button style="ctaDarkYellow">Update Email</Button>
|
||||
) : (
|
||||
<VStack>
|
||||
<StyledText style="footnote" css={{ mt: '10px', mb: '20px' }}>
|
||||
You are currently logged in with a social account. To
|
||||
convert to an email login, please click the button below.
|
||||
</StyledText>
|
||||
<Button style="ctaDarkYellow">Convert to email login</Button>
|
||||
</VStack>
|
||||
)}
|
||||
</form>
|
||||
</VStack>
|
||||
|
||||
{/* <VStack
|
||||
css={{
|
||||
padding: '24px',
|
||||
|
|
|
|||
Loading…
Reference in a new issue