EditProfile.jsx 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. import React, { useEffect, useState } from "react";
  2. import { withAuthenticationRequired } from "@auth0/auth0-react";
  3. import { Box, Button, Card, CardContent, CardHeader, Collapse, IconButton, InputBase, Table, TableBody, TableCell, TableContainer, TableRow, Typography } from "@material-ui/core";
  4. import { ExpandLess, ExpandMore, InfoTwoTone } from "@material-ui/icons";
  5. import ImageUpload from "../Images/ImageUpload";
  6. import { updateUser, updateUserMetadata } from "./settings.util";
  7. import { makeStyles } from "@material-ui/styles";
  8. import { useTranslation } from "react-i18next";
  9. import Navbar from "../Navbar";
  10. import BackButton from "../Buttons/BackButton";
  11. import { muiTableBorder } from "../util";
  12. import { Navigate, useLocation, useNavigate } from "react-router-dom";
  13. import SavingButton from "../Buttons/SavingButton";
  14. const useStyles = makeStyles(theme => ({
  15. userProfile: {
  16. padding: '1rem 0',
  17. overflow: 'auto',
  18. },
  19. table: {
  20. borderTop: muiTableBorder(theme),
  21. margin: '1rem 0',
  22. },
  23. tableCell: {
  24. padding: '10px',
  25. backgroundColor: theme.palette.background.default,
  26. '&:first-child': {
  27. width: '25%',
  28. },
  29. '&:last-child': {
  30. fontStyle: "italic",
  31. }
  32. },
  33. label: {
  34. color: theme.palette.secondary.light,
  35. fontSize: '1rem',
  36. },
  37. deleteImage: {
  38. fontSize: '1rem',
  39. fontStyle: 'italic',
  40. textAlign: 'center',
  41. marginTop: '0.5rem',
  42. color: theme.palette.error.main,
  43. textTransform: 'none',
  44. width: 'max-content',
  45. margin: '0.2rem auto',
  46. display: 'block',
  47. padding: '0rem 0.5rem',
  48. },
  49. submitButton: {
  50. minWidth: '10rem',
  51. maxWidth: '60%',
  52. margin: '0 auto 1.5rem',
  53. display: "block",
  54. },
  55. }));
  56. const IS_SECONDARY = true;
  57. /** Dialog page to edit user data, looks like Profile but is editable */
  58. const EditProfile = () => {
  59. const classes = useStyles();
  60. const { t } = useTranslation();
  61. let { state } = useLocation();
  62. const navigate = useNavigate();
  63. if (!state || !state.userData) {
  64. return <Navigate replace to="/settings" />;
  65. }
  66. const colorA = IS_SECONDARY ? "primary" : "secondary";
  67. // const colorB = IS_SECONDARY ? "secondary" : "primary";
  68. let {
  69. userData,
  70. userData: {
  71. user_metadata: metadata,
  72. user_id: userId,
  73. name: userName,
  74. email: userEmail,
  75. }
  76. } = state;
  77. const [profileImage, setProfileImage] = useState(metadata.picture || userData.picture);
  78. const [showReset, setShowReset] = useState(!!metadata.picture);
  79. const [username, setUsername] = useState(metadata.username);
  80. const [name, setName] = useState(userName);
  81. const [email, setEmail] = useState(userEmail);
  82. const [usingOAuth, setUsingOAuth] = useState(false);
  83. const [infoCollapsed, setInfoCollapsed] = useState(false);
  84. const [foreignAccountProvider, setForeignAccountProvider] = useState('');
  85. const [isSaving, setIsSaving] = useState(false);
  86. useEffect(() => {
  87. setUsingOAuth(userId.includes("oauth"));
  88. if (userId.includes("google")) {
  89. setForeignAccountProvider('Google');
  90. }
  91. }, [userId]);
  92. const editAndClose = (event) => {
  93. event.preventDefault();
  94. setIsSaving(true);
  95. // saving is only waiting for metadata for now because in Emilia there is only google login, not email/password
  96. updateUserMetadata(userId, { username: username }, onSave);
  97. if (!usingOAuth) {
  98. const newUserData = {
  99. name, email, nickname: username
  100. };
  101. updateUser(userId, newUserData, null);
  102. }
  103. }
  104. const onSave = () => {
  105. setIsSaving(false);
  106. goToSettings();
  107. };
  108. const updateProfileImage = (image) => {
  109. const imageSrc = image.url;
  110. console.log('set uploaded source', imageSrc);
  111. setProfileImage(imageSrc);
  112. updateProfileImageInMetadata(imageSrc);
  113. }
  114. const updateProfileImageInMetadata = (imageSrc) => {
  115. updateUserMetadata(userId, {
  116. picture: imageSrc,
  117. }, (newUserData) => {
  118. setProfileImage(newUserData.user_metadata.picture || newUserData.picture);
  119. setShowReset(!!newUserData.user_metadata.picture);
  120. });
  121. }
  122. const deleteProfileImage = () => {
  123. updateProfileImageInMetadata(null);
  124. }
  125. const goToSettings = () => {
  126. // send state to make Profile reload new data
  127. navigate('../', { state: { newUsername: username } });
  128. }
  129. return (
  130. <>
  131. <Navbar pageTitle={t('Edit Profile')} leftSideComponent={<BackButton onClick={goToSettings} />} secondary={IS_SECONDARY} />
  132. <Box className={classes.userProfile}>
  133. <ImageUpload uploadedImages={[profileImage]}
  134. imageName={t('profile picture of {{name}}', { name })}
  135. category="userProfile"
  136. categoryId={userId}
  137. onChangeUploadedImages={updateProfileImage}
  138. useSingleUploadOverlay />
  139. {showReset && <Button disableRipple className={classes.deleteImage} onClick={deleteProfileImage}>{t('Reset Image')}</Button>}
  140. {usingOAuth && <Card style={{ marginTop: '1rem' }}>
  141. <CardHeader avatar={<InfoTwoTone />}
  142. title={t('Logged in via {{provider}}', { provider: foreignAccountProvider })}
  143. action={<IconButton aria-label="collapse info" onClick={() => {setInfoCollapsed(!infoCollapsed)}}>
  144. {infoCollapsed ? <ExpandMore /> : <ExpandLess />}
  145. </IconButton>} />
  146. <Collapse in={!infoCollapsed} timeout="auto" unmountOnExit>
  147. <CardContent style={{ paddingTop: 0 }}>
  148. <Typography variant="body2" color="textSecondary" component="p">
  149. {t(`Since you are logged in via your {{provider}} account, you cannot change your data here. You can change the data directly in your {{provider}} account and it will be adopted on the next login.`, { provider: foreignAccountProvider })}
  150. </Typography>
  151. <Typography variant="body2" color="textSecondary" component="p">
  152. {t('You can, however, set a custom profile picture and username.')}
  153. </Typography>
  154. </CardContent>
  155. </Collapse>
  156. </Card>}
  157. <form name="edit-user-form" onSubmit={editAndClose}>
  158. <TableContainer className={classes.table}>
  159. <Table aria-label="edit profile data" size="small">
  160. <TableBody>
  161. <TableRow>
  162. <TableCell className={classes.tableCell}><Typography className={classes.label}>{t('Name')}</Typography></TableCell>
  163. <TableCell className={classes.tableCell}>
  164. <InputBase value={name} name="name" onChange={e => setName(e.target.value)} label={t('Name')} disabled={usingOAuth} />
  165. </TableCell>
  166. </TableRow>
  167. <TableRow>
  168. <TableCell className={classes.tableCell}><Typography className={classes.label}>{t('Username')}</Typography></TableCell>
  169. <TableCell className={classes.tableCell}>
  170. <InputBase value={username} name="username" onChange={e => setUsername(e.target.value)} label={t('Username')} />
  171. </TableCell>
  172. </TableRow>
  173. <TableRow>
  174. <TableCell className={classes.tableCell}><Typography className={classes.label}>{t('Email address')}</Typography></TableCell>
  175. <TableCell className={classes.tableCell}>
  176. <InputBase prop value={email} name="email" type="email" onChange={e => setEmail(e.target.value)} label={t('example@company.com')} disabled={usingOAuth} />
  177. </TableCell>
  178. </TableRow>
  179. </TableBody>
  180. </Table>
  181. </TableContainer>
  182. <SavingButton isSaving={isSaving} color={colorA} type="submit" variant="contained" size="large" className={classes.submitButton}>
  183. {t('Save Changes')}
  184. </SavingButton>
  185. </form>
  186. </Box>
  187. </>
  188. );
  189. };
  190. export default withAuthenticationRequired(EditProfile);