Files
2024-02-15 17:01:50 +00:00

109 lines
3.4 KiB
TypeScript

import {Fragment} from 'react';
import {updateUser} from 'sentry/actionCreators/account';
import AvatarChooser from 'sentry/components/avatarChooser';
import type {FormProps} from 'sentry/components/forms/form';
import Form from 'sentry/components/forms/form';
import JsonForm from 'sentry/components/forms/jsonForm';
import LoadingError from 'sentry/components/loadingError';
import LoadingIndicator from 'sentry/components/loadingIndicator';
import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
import accountDetailsFields from 'sentry/data/forms/accountDetails';
import accountPreferencesFields from 'sentry/data/forms/accountPreferences';
import {t} from 'sentry/locale';
import type {User} from 'sentry/types';
import type {ApiQueryKey} from 'sentry/utils/queryClient';
import {setApiQueryData, useApiQuery, useQueryClient} from 'sentry/utils/queryClient';
import useOrganization from 'sentry/utils/useOrganization';
import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
// The avatar endpoint ("/users/me/avatar/") returns a User-like type without `options` and other properties that are present in User
export type ChangeAvatarUser = Omit<
User,
'canReset2fa' | 'flags' | 'identities' | 'isAuthenticated' | 'options' | 'permissions'
> &
Partial<
Pick<
User,
| 'canReset2fa'
| 'flags'
| 'identities'
| 'isAuthenticated'
| 'options'
| 'permissions'
>
>;
const USER_ENDPOINT = '/users/me/';
const USER_ENDPOINT_QUERY_KEY: ApiQueryKey = [USER_ENDPOINT];
function AccountDetails() {
const organization = useOrganization({allowNull: true});
const queryClient = useQueryClient();
const {
data: user,
isLoading,
isError,
refetch,
} = useApiQuery<User>(USER_ENDPOINT_QUERY_KEY, {staleTime: 0});
if (isLoading) {
return (
<Fragment>
<SettingsPageHeader title={t('Account Details')} />
<LoadingIndicator />
</Fragment>
);
}
if (isError) {
return <LoadingError onRetry={refetch} />;
}
const handleSubmitSuccess = (userData: User | ChangeAvatarUser) => {
// the updateUser method updates our Config Store
// No components listen to the ConfigStore, they just access it directly
updateUser(userData);
// We need to update the state, because AvatarChooser is using it,
// otherwise it will flick
setApiQueryData(queryClient, USER_ENDPOINT_QUERY_KEY, userData);
};
const formCommonProps: Partial<FormProps> = {
apiEndpoint: USER_ENDPOINT,
apiMethod: 'PUT',
allowUndo: true,
saveOnBlur: true,
onSubmitSuccess: handleSubmitSuccess,
};
return (
<Fragment>
<SentryDocumentTitle title={t('Account Details')} />
<SettingsPageHeader title={t('Account Details')} />
<Form initialData={user} {...formCommonProps}>
<JsonForm forms={accountDetailsFields} additionalFieldProps={{user}} />
</Form>
<Form initialData={user.options} {...formCommonProps}>
<JsonForm
forms={accountPreferencesFields}
additionalFieldProps={{
user,
organization,
}}
/>
</Form>
<AvatarChooser
endpoint="/users/me/avatar/"
model={user}
onSave={resp => {
handleSubmitSuccess(resp as ChangeAvatarUser);
}}
isUser
/>
</Fragment>
);
}
export default AccountDetails;