@@ -11,7 +11,12 @@ import {
1111 RecoveryPhoneStatus ,
1212 useAccountState ,
1313} from '../../models/contexts/AccountStateContext' ;
14- import { Email , LinkedAccount , SecurityEvent , mapAttachedClient } from '../../models/Account' ;
14+ import {
15+ Email ,
16+ LinkedAccount ,
17+ SecurityEvent ,
18+ mapAttachedClient ,
19+ } from '../../models/Account' ;
1520import { AccountTotp , AccountBackupCodes , AccountAvatar } from '../interfaces' ;
1621import config from '../config' ;
1722import { ERRNO } from '@fxa/accounts/errors' ;
@@ -60,21 +65,40 @@ interface AccountDataResult {
6065/** Shape returned by the consolidated /account auth-server endpoint. */
6166interface AccountResponse {
6267 emails ?: Array < { email : string ; isPrimary : boolean ; verified : boolean } > ;
63- linkedAccounts ?: Array < { providerId : number ; authAt : number ; enabled : boolean } > ;
64- subscriptions ?: Array < { created ?: number ; createdAt ?: number ; productName ?: string ; product_name ?: string } > ;
68+ linkedAccounts ?: Array < {
69+ providerId : number ;
70+ authAt : number ;
71+ enabled : boolean ;
72+ } > ;
73+ subscriptions ?: Array < {
74+ created ?: number ;
75+ createdAt ?: number ;
76+ productName ?: string ;
77+ product_name ?: string ;
78+ } > ;
6579 totp ?: { exists ?: boolean ; verified ?: boolean } ;
6680 backupCodes ?: { hasBackupCodes ?: boolean ; count ?: number } ;
6781 recoveryKey ?: { exists ?: boolean ; estimatedSyncDeviceCount ?: number } ;
68- recoveryPhone ?: { exists ?: boolean ; phoneNumber ?: string ; available ?: boolean } ;
69- securityEvents ?: Array < { name : string ; createdAt : number ; verified ?: boolean } > ;
82+ recoveryPhone ?: {
83+ exists ?: boolean ;
84+ phoneNumber ?: string ;
85+ available ?: boolean ;
86+ } ;
87+ securityEvents ?: Array < {
88+ name : string ;
89+ createdAt : number ;
90+ verified ?: boolean ;
91+ } > ;
7092 metricsOptOutAt ?: number | null ;
7193 createdAt ?: number ;
7294 passwordCreatedAt ?: number ;
7395 hasPassword ?: boolean ;
7496}
7597
7698/** Transform the consolidated /account endpoint response to AccountState format. */
77- function transformAccountResponse ( response : AccountResponse ) : Partial < AccountState > {
99+ function transformAccountResponse (
100+ response : AccountResponse
101+ ) : Partial < AccountState > {
78102 const emails : Email [ ] = ( response . emails || [ ] ) . map ( ( e ) => ( {
79103 email : e . email ,
80104 isPrimary : e . isPrimary ,
@@ -152,19 +176,22 @@ async function fetchProfileData(
152176 { scope : 'profile' , ttl : PROFILE_OAUTH_TOKEN_TTL_SECONDS }
153177 ) ;
154178
155- const response = await fetch ( `${ config . servers . profile . url } /v1/profile` , {
156- headers : { Authorization : `Bearer ${ access_token } ` } ,
157- } ) ;
179+ const headers = { Authorization : `Bearer ${ access_token } ` } ;
180+ const [ profileResponse , avatarResponse ] = await Promise . all ( [
181+ fetch ( `${ config . servers . profile . url } /v1/profile` , { headers } ) ,
182+ fetch ( `${ config . servers . profile . url } /v1/avatar` , { headers } ) ,
183+ ] ) ;
158184
159- if ( ! response . ok ) {
185+ if ( ! profileResponse . ok ) {
160186 return { displayName : null , avatar : null } ;
161187 }
162188
163- const profile = await response . json ( ) ;
189+ const profile = await profileResponse . json ( ) ;
190+ const avatarData = avatarResponse . ok ? await avatarResponse . json ( ) : null ;
164191 return {
165192 displayName : profile . displayName || null ,
166193 avatar : profile . avatar
167- ? { id : profile . avatarId || 'default' , url : profile . avatar }
194+ ? { id : avatarData ?. id || null , url : profile . avatar }
168195 : null ,
169196 } ;
170197 } catch ( error ) {
@@ -192,10 +219,7 @@ export function useAccountData({
192219 authClientRef . current = authClient ;
193220
194221 const accountState = useAccountState ( ) ;
195- const {
196- setAccountData,
197- ...stateData
198- } = accountState ;
222+ const { setAccountData, ...stateData } = accountState ;
199223
200224 const [ localLoading , setLocalLoading ] = useState ( true ) ;
201225 const [ localError , setLocalError ] = useState < Error | null > ( null ) ;
@@ -222,32 +246,45 @@ export function useAccountData({
222246 ] ) ;
223247
224248 // Check for invalid token errors - user needs to sign in again
225- const rejectedResults = [ accountResult , profileResult , attachedClientsResult ]
226- . filter ( ( r ) : r is PromiseRejectedResult => r . status === 'rejected' ) ;
249+ const rejectedResults = [
250+ accountResult ,
251+ profileResult ,
252+ attachedClientsResult ,
253+ ] . filter ( ( r ) : r is PromiseRejectedResult => r . status === 'rejected' ) ;
227254 if ( rejectedResults . some ( ( r ) => isInvalidTokenError ( r . reason ) ) ) {
228255 throw new InvalidTokenError ( ) ;
229256 }
230257
231258 let accountData : Partial < AccountState > = { } ;
232259
233260 if ( accountResult . status === 'fulfilled' ) {
234- accountData = { ...accountData , ...transformAccountResponse ( accountResult . value ) } ;
261+ accountData = {
262+ ...accountData ,
263+ ...transformAccountResponse ( accountResult . value ) ,
264+ } ;
235265 } else {
236- Sentry . captureMessage ( `Failed to fetch account: ${ accountResult . reason } ` ) ;
266+ Sentry . captureMessage (
267+ `Failed to fetch account: ${ accountResult . reason } `
268+ ) ;
237269 }
238270
239271 if ( profileResult . status === 'fulfilled' ) {
240272 const { displayName, avatar } = profileResult . value ;
241273 if ( displayName !== null ) accountData . displayName = displayName ;
242274 if ( avatar !== null ) accountData . avatar = avatar ;
243275 } else {
244- Sentry . captureMessage ( `Failed to fetch profile: ${ profileResult . reason } ` ) ;
276+ Sentry . captureMessage (
277+ `Failed to fetch profile: ${ profileResult . reason } `
278+ ) ;
245279 }
246280
247281 if ( attachedClientsResult . status === 'fulfilled' ) {
248- accountData . attachedClients = attachedClientsResult . value . map ( mapAttachedClient ) ;
282+ accountData . attachedClients =
283+ attachedClientsResult . value . map ( mapAttachedClient ) ;
249284 } else {
250- Sentry . captureMessage ( `Failed to fetch attached clients: ${ attachedClientsResult . reason } ` ) ;
285+ Sentry . captureMessage (
286+ `Failed to fetch attached clients: ${ attachedClientsResult . reason } `
287+ ) ;
251288 accountData . attachedClients = [ ] ;
252289 }
253290
@@ -278,7 +315,10 @@ export function useAccountData({
278315 }
279316 case 'displayName' :
280317 case 'avatar' : {
281- const { displayName, avatar } = await fetchProfileData ( client , token ) ;
318+ const { displayName, avatar } = await fetchProfileData (
319+ client ,
320+ token
321+ ) ;
282322 if ( displayName !== null ) fieldData . displayName = displayName ;
283323 if ( avatar !== null ) fieldData . avatar = avatar ;
284324 break ;
@@ -303,7 +343,11 @@ export function useAccountData({
303343 } , [ fetchAccountData ] ) ;
304344
305345 return {
306- data : { ...stateData , isLoading : localLoading , error : localError } as AccountState ,
346+ data : {
347+ ...stateData ,
348+ isLoading : localLoading ,
349+ error : localError ,
350+ } as AccountState ,
307351 isLoading : localLoading ,
308352 error : localError ,
309353 refetch : fetchAccountData ,
0 commit comments