Skip to content

Commit

Permalink
Merge pull request #1587 from entando/ENG-5114_SeparateAvatarSection
Browse files Browse the repository at this point in the history
ENG-5114 Separate Avatar Section from My Profile Edit Form
  • Loading branch information
ryanjpburgos authored Jan 15, 2024
2 parents b548f46 + e3dc785 commit a426b66
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 14 deletions.
25 changes: 21 additions & 4 deletions src/state/avatar/actions.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { postAvatar, getAvatar } from 'api/avatar';
import { postAvatar, getAvatar, deleteAvatar } from 'api/avatar';
import { getBase64 } from 'state/file-browser/actions';
import { toggleLoading } from 'state/loading/actions';
import { addToast, addErrors, TOAST_SUCCESS, TOAST_ERROR } from '@entando/messages';
import { SET_AVATAR_FILE_NAME } from './types';

const setAvatarFilename = filename => ({
export const setAvatarFilename = filename => ({
type: SET_AVATAR_FILE_NAME,
payload: {
filename,
Expand All @@ -25,7 +25,7 @@ export const uploadAvatar =
const requestObject = await createFileObject(avatar);
const response = await postAvatar(requestObject);
const avatarCreated = await response.json();
dispatch(setAvatarFilename(avatarCreated.payload.filename));
dispatch(setAvatarFilename(`${avatarCreated.payload.filename}?${Date.now()}`));
dispatch(toggleLoading(loader));
dispatch(addToast({ id: 'fileBrowser.uploadFileComplete' }, TOAST_SUCCESS));
} catch (error) {
Expand All @@ -41,7 +41,7 @@ export const fetchAvatar = (loader = 'fetchAvatar') => async (dispatch) => {
dispatch(toggleLoading(loader));
const response = await getAvatar();
const avatar = await response.json();
if (avatar.payload.filename) dispatch(setAvatarFilename(avatar.payload.filename));
if (avatar.payload.filename) dispatch(setAvatarFilename(`${avatar.payload.filename}?${Date.now()}`));
dispatch(toggleLoading(loader));
} catch (error) {
dispatch(toggleLoading(loader));
Expand All @@ -50,3 +50,20 @@ export const fetchAvatar = (loader = 'fetchAvatar') => async (dispatch) => {
}
};


export const removeAvatar =
(loader = 'removeAvatar') =>
async (dispatch) => {
try {
dispatch(toggleLoading(loader));
await deleteAvatar();
dispatch(setAvatarFilename(''));
dispatch(toggleLoading(loader));
dispatch(addToast({ id: 'fileBrowser.uploadFileComplete' }, TOAST_SUCCESS));
} catch (error) {
dispatch(toggleLoading(loader));
const message = { id: 'fileBrowser.uploadFileError', values: { errmsg: error } };
dispatch(addErrors(error));
dispatch(addToast(message, TOAST_ERROR));
}
};
15 changes: 13 additions & 2 deletions src/ui/users/common/ProfileImageUploader.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,23 @@ const ProfileImageUploader = ({
}) => {
const [edit, setEdit] = useState(false);
const inputFileRef = useRef(null);
const imageRef = useRef(null);
const dispatch = useDispatch();

const imageProvider = useDynamicResourceUrl(FILE_BROWSER_PATH);

const onFileChange = ({ target: { files } }) => {
dispatch(uploadAvatar(files[0])).then(() => onChange(files[0].name));
const file = files[0];
if (file) {
dispatch(uploadAvatar(files[0])).then(() => {
const fr = new FileReader();
fr.onload = () => {
imageRef.current.src = fr.result;
onChange(file.name);
};
fr.readAsDataURL(file);
});
}
};

const handleUploadClick = () => {
Expand Down Expand Up @@ -51,7 +62,7 @@ const ProfileImageUploader = ({
className="ProfileImageUploader__file-upload"
/>
<button type="button">
<img src={userPicture} alt="user profile"className="ProfileImageUploader__picture" />
<img ref={imageRef} src={userPicture} alt="user profile"className="ProfileImageUploader__picture" />
</button>
</Dropdown.Toggle>
<Dropdown.Menu>
Expand Down
4 changes: 1 addition & 3 deletions src/ui/users/my-profile/MyProfileEditForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,12 +229,11 @@ export class MyProfileEditFormBody extends Component {
return (
<Form onSubmit={this.props.handleSubmit(this.submit)} horizontal className="MyProfileEditForm">
<FormSectionTitle titleId="user.myProfile.uploadImage" requireFields={false} />
<input type="hidden" name="profilepicture" value={avatar} />
<ProfileImageUploader
image={avatar}
onChange={onChangeProfilePicture}
gravatarEmail={userEmail}
editable={editMode}
editable
/>

<div className="MyProfileEditForm__attributes">
Expand Down Expand Up @@ -320,7 +319,6 @@ MyProfileEditFormBody.propTypes = {
id: PropTypes.string,
typeCode: PropTypes.string,
typeDescription: PropTypes.string,
profilepicture: PropTypes.string,
}),
onChangeProfilePicture: PropTypes.func.isRequired,
avatar: PropTypes.string,
Expand Down
10 changes: 7 additions & 3 deletions src/ui/users/my-profile/MyProfileEditFormContainer.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { connect } from 'react-redux';
import { reset, change } from 'redux-form';
import { reset } from 'redux-form';
import { getUsername } from '@entando/apimanager';

import { fetchMyUserProfile, updateMyUserProfile } from 'state/user-profile/actions';
import { fetchLanguages } from 'state/languages/actions';
import { fetchAvatar } from 'state/avatar/actions';
import { fetchAvatar, removeAvatar } from 'state/avatar/actions';
import { getDefaultLanguage, getActiveLanguages } from 'state/languages/selectors';
import { getSelectedProfileTypeAttributes } from 'state/profile-types/selectors';
import MyProfileEditForm from 'ui/users/my-profile/MyProfileEditForm';
Expand Down Expand Up @@ -41,7 +41,11 @@ export const mapDispatchToProps = dispatch => ({
onCancel: () => {
dispatch(reset('UserProfile'));
},
onChangeProfilePicture: picture => dispatch(change('UserProfile', 'profilepicture', picture)),
onChangeProfilePicture: (picture) => {
if (picture === '') {
dispatch(removeAvatar());
}
},
});

export default connect(
Expand Down
11 changes: 9 additions & 2 deletions test/state/avatar/actions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ describe('createFileObject', () => {
});

describe('uploadAvatar', () => {
beforeAll(() => {
global.Date.now = jest.fn(() => new Date('2019-04-07T10:20:30Z').getTime());
});
it('should upload avatar successfully', async () => {
const avatar = new File(['test data'], 'test.png', { type: 'image/png' });
const dispatch = jest.fn();
Expand All @@ -20,7 +23,7 @@ describe('uploadAvatar', () => {
await uploadAvatar(avatar)(dispatch);

expect(dispatch).toHaveBeenCalledWith({ payload: { id: 'uploadAvatar' }, type: 'loading/toggle-loading' });
expect(dispatch).toHaveBeenCalledWith({ payload: { filename: 'test.png' }, type: 'avatar/filename' });
expect(dispatch).toHaveBeenCalledWith({ payload: { filename: `test.png?${new Date('2019-04-07T10:20:30Z').getTime()}` }, type: 'avatar/filename' });
expect(dispatch).toHaveBeenCalledWith({ payload: { id: 'uploadAvatar' }, type: 'loading/toggle-loading' });
expect(dispatch).toHaveBeenCalledWith({ payload: { message: { id: 'fileBrowser.uploadFileComplete' }, type: 'success' }, type: 'toasts/add-toast' });
expect(dispatch).toHaveBeenCalledTimes(4);
Expand All @@ -45,12 +48,16 @@ describe('uploadAvatar', () => {


describe('fetchAvatar', () => {
beforeAll(() => {
global.Date.now = jest.fn(() => new Date('2019-04-07T10:20:30Z').getTime());
});

it('should retrieve the avatar successfully', async () => {
const dispatch = jest.fn();
jest.spyOn(apiHelper, 'getAvatar').mockResolvedValue({ json: () => Promise.resolve({ payload: { filename: 'test.png' } }) });
await fetchAvatar()(dispatch);
expect(dispatch).toHaveBeenCalledWith({ payload: { id: 'fetchAvatar' }, type: 'loading/toggle-loading' });
expect(dispatch).toHaveBeenCalledWith({ payload: { filename: 'test.png' }, type: 'avatar/filename' });
expect(dispatch).toHaveBeenCalledWith({ payload: { filename: `test.png?${new Date('2019-04-07T10:20:30Z').getTime()}` }, type: 'avatar/filename' });
expect(dispatch).toHaveBeenCalledWith({ payload: { id: 'fetchAvatar' }, type: 'loading/toggle-loading' });
expect(dispatch).toHaveBeenCalledTimes(3);
});
Expand Down

0 comments on commit a426b66

Please sign in to comment.