Skip to content

Commit f10a950

Browse files
committed
add deleteProfile and setProfile image
1 parent 4f48bf1 commit f10a950

File tree

29 files changed

+389
-13
lines changed

29 files changed

+389
-13
lines changed

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"immer": "^7.0.9",
88
"node-sass": "^4.14.1",
99
"react": "^16.13.1",
10+
"react-avatar-editor": "11.0.7",
1011
"react-dom": "^16.13.1",
1112
"react-redux": "^7.2.1",
1213
"react-router-dom": "^5.2.0",
@@ -39,6 +40,7 @@
3940
]
4041
},
4142
"devDependencies": {
43+
"@types/react-avatar-editor": "^10.3.5",
4244
"@testing-library/jest-dom": "^4.2.4",
4345
"@testing-library/react": "^9.3.2",
4446
"@testing-library/user-event": "^7.1.2",
@@ -59,6 +61,6 @@
5961
"eslint-plugin-react-hooks": "^4.1.2",
6062
"prettier": "2.1.1",
6163
"redux-devtools": "^3.7.0",
62-
"typescript": "~3.7.2"
64+
"typescript": "3.9.7"
6365
}
6466
}

src/assets/no-avatar.jpg

33.1 KB
Loading

src/components/NavBar/NavBar.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ import okImage from '../../assets/ok.png';
55
import { firebase_app } from '../../api/firebase';
66
import { useDispatch } from 'redux-react-hook';
77
import { signOut } from '../../store/auth/signOut';
8+
import { User } from 'firebase';
89

910
export const NavBar: React.FC = () => {
1011
const dispatch = useDispatch();
11-
const [user, setUser] = useState();
12+
const [user, setUser] = useState<User>();
1213

1314
firebase_app.auth().onAuthStateChanged((user) => {
1415
if (user) {
@@ -46,7 +47,13 @@ export const NavBar: React.FC = () => {
4647
Get profile data
4748
</Link>
4849
<Link className="menu-item" to={'/update-profile-data'}>
49-
Update profile data
50+
Update profile
51+
</Link>
52+
<Link className="menu-item" to={'/delete-profile-data'}>
53+
Delete profile
54+
</Link>
55+
<Link className="menu-item" to={'/set-profile-image'}>
56+
Set image
5057
</Link>
5158
</>
5259
)}

src/index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@ import {
1414
PageNotFound,
1515
GetProfileData,
1616
UpdateProfileData,
17+
SetProfileImage,
18+
DeleteProfileData,
1719
} from './pages';
1820
import { toast, ToastContainer } from 'react-toastify';
1921

2022
import './styles/index/css-styles-reset.scss';
2123
import './styles/index/main.scss';
22-
2324
import 'react-toastify/dist/ReactToastify.css';
2425

2526
// Call it once in your app. At the root of your app is the best place
@@ -38,6 +39,8 @@ ReactDOM.render(
3839
<Route path="/get-profile-data" exact={true} component={GetProfileData} />
3940
<Route path="/get-profile-data" exact={true} component={GetProfileData} />
4041
<Route path="/update-profile-data" exact={true} component={UpdateProfileData} />
42+
<Route path="/delete-profile-data" exact={true} component={DeleteProfileData} />
43+
<Route path="/set-profile-image" exact={true} component={SetProfileImage} />
4144
<Route component={PageNotFound} />
4245
</Switch>
4346
</Router>
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import React, { useRef, useState } from 'react';
2+
import AvatarEditor from 'react-avatar-editor';
3+
4+
import noAvatarImage from '../../../assets/no-avatar.jpg';
5+
import firebaseImage from '../../../assets/firebase.png';
6+
7+
import './setProfileImage.scss';
8+
import { useDispatch, useMappedState } from 'redux-react-hook';
9+
import { action_setAvatar } from '../../../store/profile/setAvatar';
10+
import { isAvatarImageUploading } from '../../../store/profile/selectors';
11+
12+
const NoAvatar = () => {
13+
return (
14+
<div className="image-container">
15+
<img style={{ width: '200px' }} alt="profile" src={noAvatarImage} />
16+
</div>
17+
);
18+
};
19+
20+
export const SetProfileImage = () => {
21+
const dispatch = useDispatch();
22+
23+
const isLoading = useMappedState(isAvatarImageUploading);
24+
25+
const [imageRef, setImageRef] = useState<AvatarEditor | null>(null);
26+
const [avatar, setAvatar] = useState<File>();
27+
const uploadImageInputRef = useRef<HTMLInputElement>(null);
28+
29+
const setEditorRef = (editor: AvatarEditor | null) => setImageRef(editor);
30+
31+
if (isLoading) {
32+
return (
33+
<div className="set-profile-image">
34+
<img src={firebaseImage} />
35+
<span>Processing...</span>
36+
</div>
37+
);
38+
}
39+
40+
return (
41+
<div className="set-profile-image">
42+
<img src={firebaseImage} />
43+
<span>Upload your photo</span>
44+
{avatar ? (
45+
<AvatarEditor
46+
style={{
47+
margin: '20px',
48+
marginTop: '0px',
49+
width: '250px',
50+
height: '378px',
51+
}}
52+
ref={setEditorRef}
53+
image={URL.createObjectURL(avatar)}
54+
border={0}
55+
width={852}
56+
height={1280}
57+
borderRadius={0}
58+
color={[255, 255, 255, 0.6]}
59+
scale={1}
60+
/>
61+
) : (
62+
<NoAvatar />
63+
)}
64+
<button onClick={() => uploadImageInputRef.current?.click()}>Select photo</button>
65+
<input
66+
ref={uploadImageInputRef}
67+
type="file"
68+
accept=".jpg, .jpeg"
69+
onChange={(e) => {
70+
const { files } = e.target;
71+
if (files) {
72+
const file = files[0];
73+
if (file) {
74+
setAvatar(file);
75+
}
76+
}
77+
}}
78+
style={{ display: 'none' }}
79+
/>
80+
<button
81+
type="submit"
82+
disabled={false}
83+
onClick={() => {
84+
if (imageRef && avatar) {
85+
const canvasScaledImage: HTMLCanvasElement = imageRef.getImageScaledToCanvas();
86+
canvasScaledImage.toBlob(
87+
(imageBlob: Blob | null) => {
88+
dispatch(action_setAvatar(imageBlob));
89+
},
90+
'image/jpeg',
91+
0.9,
92+
);
93+
}
94+
}}
95+
>
96+
<span>Save</span>
97+
</button>
98+
</div>
99+
);
100+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { SetProfileImage } from './SetProfileImage';
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
.set-profile-image {
2+
display: flex;
3+
flex-direction: column;
4+
justify-content: center;
5+
align-items: center;
6+
width: calc(100% - 200px);
7+
8+
img {
9+
width: 50px;
10+
}
11+
12+
span {
13+
font-size: 20pt;
14+
font-weight: 500;
15+
margin: 20px;
16+
}
17+
18+
button {
19+
background-color: #bcdaff;
20+
border-radius: 15px;
21+
border: none;
22+
height: 45px;
23+
font-size: 20px;
24+
margin-top: 30px;
25+
width: 300px;
26+
cursor: pointer;
27+
28+
&:focus {
29+
outline: none;
30+
background-color: #3d95ff;
31+
}
32+
}
33+
}

src/pages/image/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { SetProfileImage } from './SetProfileImage';

src/pages/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ export { SignUp } from './auth/SignUp';
55
export { SignIn } from './auth/SignIn';
66
export { SignInWithPopUp } from './auth/SignInWithPopUp';
77
export { UpdateProfileData } from './profile/UpdateProfileData';
8+
export { DeleteProfileData } from './profile/DeleteProfileData';
9+
export { SetProfileImage } from './image/SetProfileImage';
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from 'react';
2+
import firebaseImage from '../../../assets/firebase.png';
3+
import { useDispatch } from 'redux-react-hook';
4+
import { action_deleteProfileData } from '../../../store/profile/deleteProfileData';
5+
6+
import './deleteProfileData.scss';
7+
8+
export const DeleteProfileData: React.FC = () => {
9+
const dispatch = useDispatch();
10+
11+
return (
12+
<div className="delete-profile-data">
13+
<img src={firebaseImage} />
14+
15+
<span>Do you want to delete profile data</span>
16+
<button onClick={() => dispatch(action_deleteProfileData())}>Yes</button>
17+
</div>
18+
);
19+
};

0 commit comments

Comments
 (0)