diff --git a/assets/images/dragonfly2.svg b/assets/images/dragonfly2.svg
deleted file mode 100644
index ce2b4d7a..00000000
--- a/assets/images/dragonfly2.svg
+++ /dev/null
@@ -1,69 +0,0 @@
-
diff --git a/assets/images/edit.svg b/assets/images/edit.svg
new file mode 100644
index 00000000..b2c1e3e8
--- /dev/null
+++ b/assets/images/edit.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/images/uploadPhoto.svg b/assets/images/uploadPhoto.svg
new file mode 100644
index 00000000..27510b9a
--- /dev/null
+++ b/assets/images/uploadPhoto.svg
@@ -0,0 +1,27 @@
+
diff --git a/locales/en.json b/locales/en.json
index 71b05905..0ba50036 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -109,6 +109,26 @@
"buttonClose": "Close",
"buttonDonate": "Donate now"
},
+ "EditProfile": {
+ "bodyCancel": "If you click cancel all changes you made will be lost.",
+ "buttonCancel": "Cancel",
+ "buttonContinue": "Continue Editing",
+ "buttonSave": "Save changes",
+ "charactersExplanation": "Only basic characters and numbers (A-Z, 0-9) are allowed, no symbols or whitespace, 3-24 characters",
+ "confirmationMessage": "Great! Your profile changes have been saved!",
+ "errorNameTaken": "Sorry, this name is already taken!",
+ "errorInvalidName": "Ups! Invalid name, try again.",
+ "errorImageCapture": "Sorry, there was a problem with image capture.",
+ "errorSaveChanges": "Sorry, there was a problem with saving changes.",
+ "heading": "Edit profile",
+ "optionCapture": "Capture",
+ "optionTryAgain": "Try Again",
+ "optionCamera": "Camera",
+ "optionUpload": "Upload",
+ "optionFile": "Gallery",
+ "titleCancel": "Do you want to save changes to your profile?",
+ "usernameExplanation": "Your username is how your friends can search for you on the Circles App."
+ },
"Finder": {
"bodyFilterDirect": "Directly trusted",
"bodyFilterExternal": "External",
diff --git a/package-lock.json b/package-lock.json
index 4e8c27ae..43b4b1a5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,11 +1,11 @@
{
"name": "circles-myxogastria",
- "version": "2.0.0",
+ "version": "2.0.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
- "version": "2.0.0",
+ "version": "2.0.1",
"license": "AGPL-3.0",
"dependencies": {
"@circles/core": "^2.10.10",
diff --git a/package.json b/package.json
index 866ebbe4..6cb15369 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "circles-myxogastria",
- "version": "2.0.0",
+ "version": "2.0.1",
"description": "Webapp and mobile client for Circles",
"main": "src/index.js",
"private": true,
@@ -57,7 +57,7 @@
"webpack-dev-server": "^3.11.2"
},
"dependencies": {
- "@circles/core": "^2.10.10",
+ "@circles/core": "^2.12.0",
"@circles/timecircles": "^1.0.6",
"@material-ui/core": "^4.12.4",
"@material-ui/lab": "^4.0.0-alpha.61",
diff --git a/src/components/Avatar.js b/src/components/Avatar.js
index 3387a67c..eb008df8 100644
--- a/src/components/Avatar.js
+++ b/src/components/Avatar.js
@@ -21,6 +21,7 @@ const useStyles = makeStyles(() => ({
avatarContainer: {
position: 'relative',
margin: '0 auto',
+ cursor: 'pointer',
},
organizationIndicator: {
position: 'absolute',
@@ -29,11 +30,14 @@ const useStyles = makeStyles(() => ({
},
}));
-const Avatar = ({ address, size = 'small', ...props }) => {
+const Avatar = ({ address, size = 'small', url, useCache, ...props }) => {
const classes = useStyles();
const theme = useTheme();
- const { avatarUrl, username } = useUserdata(address);
+ let { avatarUrl, username } = useUserdata(address, useCache);
+
+ if (url) avatarUrl = url;
+
const { isOrganization } = useIsOrganization(address);
const sizePixelAvatar =
@@ -68,6 +72,8 @@ const Avatar = ({ address, size = 'small', ...props }) => {
Avatar.propTypes = {
address: PropTypes.string,
size: PropTypes.string,
+ url: PropTypes.string,
+ useCache: PropTypes.bool,
};
export default React.memo(Avatar);
diff --git a/src/components/AvatarHeader.js b/src/components/AvatarHeader.js
index e6bc516d..c233a1c6 100644
--- a/src/components/AvatarHeader.js
+++ b/src/components/AvatarHeader.js
@@ -42,9 +42,9 @@ const AvatarHeader = ({ hideImage, username }) => {
const displayedUsername = username ? (
`@${username}`
) : safe.currentAccount ? (
-
+
) : safe.pendingAddress ? (
-
+
) : null;
return (
@@ -56,6 +56,7 @@ const AvatarHeader = ({ hideImage, username }) => {
address={safe.currentAccount || safe.pendingAddress}
className={classes.avatarContainer}
size={'smallXl'}
+ useCache={false}
/>
)}
diff --git a/src/components/Button.js b/src/components/Button.js
index 135657c2..6fd4000b 100644
--- a/src/components/Button.js
+++ b/src/components/Button.js
@@ -53,6 +53,17 @@ const useStyles = makeStyles((theme) => ({
linear-gradient(to right, ${theme.custom.colors.purple}, ${theme.custom.colors.purpleDark}) border-box`,
border: '1px solid transparent',
},
+ buttonWithoutBorder: {
+ border: 0,
+ background: theme.custom.gradients.purple,
+ backgroundClip: 'text',
+ color: 'transparent',
+ '-webkit-background-clip': 'text',
+ '-webkit-text-fill-color': 'transparent',
+ '&:hover': {
+ backgroundColor: 'transparent',
+ },
+ },
}));
// eslint-disable-next-line react/display-name
@@ -68,6 +79,7 @@ const Button = React.forwardRef(
isWhite,
isWhiteText,
isGradientBorder,
+ isWithoutBorder,
to,
...props
},
@@ -83,6 +95,7 @@ const Button = React.forwardRef(
[classes.buttonWhite]: isWhite,
[classes.buttonWhiteText]: isWhiteText,
[classes.buttonGradientBorder]: isGradientBorder,
+ [classes.buttonWithoutBorder]: isWithoutBorder,
});
return React.createElement(
@@ -113,6 +126,7 @@ Button.propTypes = {
isPrimary: PropTypes.bool,
isWhite: PropTypes.bool,
isWhiteText: PropTypes.bool,
+ isWithoutBorder: PropTypes.bool,
onClick: PropTypes.func,
to: PropTypes.string,
};
diff --git a/src/components/DialogInfo.js b/src/components/DialogInfo.js
index eee5be2f..5b60d654 100644
--- a/src/components/DialogInfo.js
+++ b/src/components/DialogInfo.js
@@ -26,7 +26,16 @@ const useStyles = makeStyles((theme) => ({
},
}));
-const DialogInfo = ({ dialogContent, handleClose, id, isOpen, title }) => {
+const DialogInfo = ({
+ dialogContent,
+ handleClose,
+ id,
+ isOpen,
+ title,
+ fullWidth,
+ maxWidth,
+ isBtnClose = true,
+}) => {
const classes = useStyles();
return (
@@ -34,13 +43,15 @@ const DialogInfo = ({ dialogContent, handleClose, id, isOpen, title }) => {
aria-describedby={`dialog-${id}-text`}
aria-labelledby={`dialog-${id}-description`}
className={classes.dialogContainer}
+ fullWidth={fullWidth}
+ maxWidth={maxWidth}
open={isOpen}
onClose={handleClose}
>
{title}
{dialogContent}
-
+ {isBtnClose && }
);
@@ -48,10 +59,13 @@ const DialogInfo = ({ dialogContent, handleClose, id, isOpen, title }) => {
DialogInfo.propTypes = {
dialogContent: PropTypes.element,
+ fullWidth: PropTypes.bool,
handleClose: PropTypes.func.isRequired,
- id: PropTypes.string.isRequired,
+ id: PropTypes.string,
+ isBtnClose: PropTypes.bool,
isOpen: PropTypes.bool.isRequired,
- title: PropTypes.string.isRequired,
+ maxWidth: PropTypes.string,
+ title: PropTypes.string,
};
export default React.memo(DialogInfo);
diff --git a/src/components/ImageCapture.js b/src/components/ImageCapture.js
new file mode 100644
index 00000000..4b6c93d8
--- /dev/null
+++ b/src/components/ImageCapture.js
@@ -0,0 +1,143 @@
+import { Box, CircularProgress } from '@material-ui/core';
+import { makeStyles } from '@material-ui/core/styles';
+import clsx from 'clsx';
+import PropTypes from 'prop-types';
+import React from 'react';
+import { useEffect, useRef, useState } from 'react';
+import { createRef, useCallback } from 'react';
+
+import Button from '~/components/Button';
+import translate from '~/services/locale';
+
+const useStyles = makeStyles((theme) => ({
+ videoContainer: {
+ marginBottom: '25px',
+ height: '198px',
+ clipPath: 'circle(99px at center)',
+ [theme.breakpoints.up('sm')]: {
+ minHeight: '263px',
+ clipPath: 'circle(130px at center)',
+ },
+ overflow: 'hidden',
+ },
+ imageCanvas: {
+ display: 'none',
+ },
+ loadingMask: {
+ background: theme.custom.colors.cornflowerBlue,
+ opacity: 0.5,
+ position: 'relative',
+ },
+ loadingIndicator: {
+ position: 'absolute',
+ top: '50%',
+ left: '50%',
+ transform: 'translate(-50%, -50%)',
+ },
+ imageCaptureContainer: {
+ position: 'relative',
+
+ [theme.breakpoints.up('sm')]: {
+ minHeight: '400px',
+ },
+ },
+ captureBtn: {
+ marginBottom: '25px',
+ },
+ uploadBtn: {
+ cursor: 'not-allowed',
+ },
+}));
+
+const ImageCapture = ({ onCapture, onError, width, userMediaConfig }) => {
+ const classes = useStyles();
+ const [isLoading, setIsLoading] = useState(true);
+
+ const playerRef = createRef();
+ const canvasRef = createRef();
+ const tracks = useRef();
+ useEffect(() => {
+ navigator.mediaDevices
+ .getUserMedia(userMediaConfig)
+ .then((stream) => {
+ if (playerRef.current) {
+ playerRef.current.srcObject = stream;
+ setIsLoading(false);
+ }
+ tracks.current = stream.getTracks();
+ })
+ .catch((error) => {
+ if (onError) onError(error);
+ });
+ /* eslint-disable react-hooks/exhaustive-deps */
+ }, [onError, userMediaConfig]);
+ /* eslint-enable react-hooks/exhaustive-deps */
+
+ useEffect(() => {
+ return () => {
+ if (tracks.current) {
+ tracks.current.forEach((track) => track.stop());
+ }
+ };
+ }, []);
+
+ const captureImage = useCallback(() => {
+ const imageWidth = playerRef.current.offsetWidth;
+ const imageHeight = playerRef.current.offsetHeight;
+ [canvasRef.current.width, canvasRef.current.height] = [
+ imageWidth,
+ imageHeight,
+ ];
+ const context = canvasRef.current.getContext('2d');
+ context.drawImage(playerRef.current, 0, 0, imageWidth, imageHeight);
+ if (onCapture) {
+ const pngData = canvasRef.current.toDataURL();
+ canvasRef.current.toBlob((blob) => {
+ onCapture({
+ blob,
+ png: pngData,
+ file: new File([blob], `${new Date().getTime()}.png`, {
+ type: 'image/png',
+ }),
+ });
+ });
+ }
+ }, [onCapture, canvasRef, playerRef]);
+
+ return (
+
+
+ {isLoading && (
+
+
+
+
+
+ )}
+
+
+
+
+
+
+ );
+};
+
+ImageCapture.propTypes = {
+ onCapture: PropTypes.func,
+ onError: PropTypes.func,
+ userMediaConfig: PropTypes.object,
+ width: PropTypes.string,
+};
+
+export default ImageCapture;
diff --git a/src/components/NavigationFloating.js b/src/components/NavigationFloating.js
index 60f3e833..39825dfd 100644
--- a/src/components/NavigationFloating.js
+++ b/src/components/NavigationFloating.js
@@ -5,7 +5,7 @@ import React from 'react';
import { Link } from 'react-router-dom';
import {
- //EDIT_PROFILE,
+ EDIT_PROFILE_PATH,
MY_PROFILE_PATH,
ORGANIZATION_MEMBERS_PATH,
} from '~/routes';
@@ -153,13 +153,13 @@ export default function NavigationFloating(props) {
)}
- {/*