accountDetails.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import {Fragment} from 'react';
  2. import {updateUser} from 'sentry/actionCreators/account';
  3. import AvatarChooser from 'sentry/components/avatarChooser';
  4. import type {FormProps} from 'sentry/components/forms/form';
  5. import Form from 'sentry/components/forms/form';
  6. import JsonForm from 'sentry/components/forms/jsonForm';
  7. import LoadingError from 'sentry/components/loadingError';
  8. import LoadingIndicator from 'sentry/components/loadingIndicator';
  9. import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
  10. import accountDetailsFields from 'sentry/data/forms/accountDetails';
  11. import accountPreferencesFields from 'sentry/data/forms/accountPreferences';
  12. import {t} from 'sentry/locale';
  13. import type {User} from 'sentry/types/user';
  14. import type {ApiQueryKey} from 'sentry/utils/queryClient';
  15. import {setApiQueryData, useApiQuery, useQueryClient} from 'sentry/utils/queryClient';
  16. import useOrganization from 'sentry/utils/useOrganization';
  17. import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
  18. // The avatar endpoint ("/users/me/avatar/") returns a User-like type without `options` and other properties that are present in User
  19. export type ChangeAvatarUser = Omit<
  20. User,
  21. 'canReset2fa' | 'flags' | 'identities' | 'isAuthenticated' | 'options' | 'permissions'
  22. > &
  23. Partial<
  24. Pick<
  25. User,
  26. | 'canReset2fa'
  27. | 'flags'
  28. | 'identities'
  29. | 'isAuthenticated'
  30. | 'options'
  31. | 'permissions'
  32. >
  33. >;
  34. const USER_ENDPOINT = '/users/me/';
  35. const USER_ENDPOINT_QUERY_KEY: ApiQueryKey = [USER_ENDPOINT];
  36. function AccountDetails() {
  37. const organization = useOrganization({allowNull: true});
  38. const queryClient = useQueryClient();
  39. const {
  40. data: user,
  41. isLoading,
  42. isError,
  43. refetch,
  44. } = useApiQuery<User>(USER_ENDPOINT_QUERY_KEY, {staleTime: 0});
  45. if (isLoading) {
  46. return (
  47. <Fragment>
  48. <SettingsPageHeader title={t('Account Details')} />
  49. <LoadingIndicator />
  50. </Fragment>
  51. );
  52. }
  53. if (isError) {
  54. return <LoadingError onRetry={refetch} />;
  55. }
  56. const handleSubmitSuccess = (userData: User | ChangeAvatarUser) => {
  57. // the updateUser method updates our Config Store
  58. // No components listen to the ConfigStore, they just access it directly
  59. updateUser(userData);
  60. // We need to update the state, because AvatarChooser is using it,
  61. // otherwise it will flick
  62. setApiQueryData(queryClient, USER_ENDPOINT_QUERY_KEY, userData);
  63. };
  64. const formCommonProps: Partial<FormProps> = {
  65. apiEndpoint: USER_ENDPOINT,
  66. apiMethod: 'PUT',
  67. allowUndo: true,
  68. saveOnBlur: true,
  69. onSubmitSuccess: handleSubmitSuccess,
  70. };
  71. return (
  72. <Fragment>
  73. <SentryDocumentTitle title={t('Account Details')} />
  74. <SettingsPageHeader title={t('Account Details')} />
  75. <Form initialData={user} {...formCommonProps}>
  76. <JsonForm forms={accountDetailsFields} additionalFieldProps={{user}} />
  77. </Form>
  78. <Form initialData={user.options} {...formCommonProps}>
  79. <JsonForm
  80. forms={accountPreferencesFields}
  81. additionalFieldProps={{
  82. user,
  83. organization,
  84. }}
  85. />
  86. </Form>
  87. <AvatarChooser
  88. endpoint="/users/me/avatar/"
  89. model={user}
  90. onSave={resp => {
  91. handleSubmitSuccess(resp as ChangeAvatarUser);
  92. }}
  93. isUser
  94. />
  95. </Fragment>
  96. );
  97. }
  98. export default AccountDetails;