diff --git a/packages/explorer/src/views/Browse.js b/packages/explorer/src/views/Browse.js
index e45d99b..f0cb116 100644
--- a/packages/explorer/src/views/Browse.js
+++ b/packages/explorer/src/views/Browse.js
@@ -102,12 +102,20 @@ const Page = () => {
const created = Date.now() - meta.created
return (
- {humanize(created)}
- {profile.memberId}
{profile.name} ({profile.pronouns})
+
+ {profile.avatar &&
+
+
+
}
{profile.city} - {profile.company}
{profile.bio}
{profile.email} - {profile.twitter}
+ {profile.memberId}
+ {humanize(created)}
)
})}
diff --git a/packages/profile/package.json b/packages/profile/package.json
index 6c9b960..02a5d5f 100644
--- a/packages/profile/package.json
+++ b/packages/profile/package.json
@@ -17,6 +17,7 @@
"ethers": "5.6.8",
"react": "^17.0.2",
"react-dom": "^17.0.2",
+ "react-image-file-resizer": "^0.4.8",
"react-router-dom": "^6.2.2",
"react-scripts": "5.0.0",
"sass": "^1.50.0",
diff --git a/packages/profile/src/index.scss b/packages/profile/src/index.scss
index b5c61c9..a5e3ae8 100644
--- a/packages/profile/src/index.scss
+++ b/packages/profile/src/index.scss
@@ -1,3 +1,9 @@
@tailwind base;
+
+@layer base {
+ a {
+ @apply text-kernel-eggplant-light
+ }
+}
@tailwind components;
@tailwind utilities;
diff --git a/packages/profile/src/views/Profile.jsx b/packages/profile/src/views/Profile.jsx
index a8fdfec..2acffca 100644
--- a/packages/profile/src/views/Profile.jsx
+++ b/packages/profile/src/views/Profile.jsx
@@ -8,11 +8,21 @@
import { useEffect, useReducer } from 'react'
import { useNavigate } from 'react-router-dom'
+import Resizer from 'react-image-file-resizer'
import { Switch } from '@headlessui/react'
-import { useServices, Navbar, Footer, Alert } from '@kernel/common'
+import { useServices, Navbar, Footer, Alert, getUrl } from '@kernel/common'
import AppConfig from 'App.config'
+const AVATAR_CONFIG = {
+ maxHeight: 500,
+ maxWidth: 500,
+ format: 'PNG',
+ quality: 100, // maximum: no compression
+ rotation: 0,
+ outputType: 'base64'
+}
+
const FORM_INPUT = ['email', 'name', 'pronouns', 'twitter', 'city', 'company', 'bio']
// initializes state in the form:
@@ -21,6 +31,7 @@ const INITIAL_FORM_KEYS = ['wallet'].concat(FORM_INPUT)
const INITIAL_FORM_FIELDS_STATE = INITIAL_FORM_KEYS
.reduce((acc, key) => Object.assign(acc, { [key]: '' }), {})
INITIAL_FORM_FIELDS_STATE.consent = true
+INITIAL_FORM_FIELDS_STATE.avatar = null
const INITIAL_FORM_SUBMISSION_STATE = {
formStatus: 'clean',
@@ -56,6 +67,13 @@ const reducer = (state, action) => {
const change = (dispatch, type, e) => {
try {
const target = e.target
+
+ if (type === 'avatar') {
+ onAvatarChange(dispatch, target)
+ target.value = null // reset so onChange will fire even for same image
+ return
+ }
+
const payload = target.type === 'checkbox' ? target.checked : target.value
dispatch({ type, payload })
} catch (error) {
@@ -63,6 +81,32 @@ const change = (dispatch, type, e) => {
}
}
+const onAvatarChange = (dispatch, target) => {
+ if (target.files.length === 0) {
+ return
+ }
+
+ const { maxHeight, maxWidth, format, quality, rotation, outputType } = AVATAR_CONFIG
+
+ Resizer.imageFileResizer(
+ target.files[0],
+ maxWidth,
+ maxHeight,
+ format,
+ quality,
+ rotation,
+ (uri) => {
+ console.log(uri)
+ dispatch({ type: 'avatar', payload: uri })
+ },
+ outputType
+ )
+}
+
+const removeAvatar = (dispatch) => {
+ dispatch({ type: 'avatar', payload: null })
+}
+
const value = (state, type) => state[type]
const save = async (state, dispatch, e) => {
@@ -127,7 +171,12 @@ const ProfileAlert = ({ formStatus, errorMessage }) => {
case 'submitting':
return Saving your changes...
case 'success':
- return Your changes have been saved!
+ return (
+
+ Your changes have been saved!
+ Start exploring.
+
+ )
case 'error':
return Something went wrong. {errorMessage}
default:
@@ -207,6 +256,64 @@ const Profile = () => {
)
})}
+
+
+
+
+ Upload a JPEG or PNG. Recommended size is 500 x 500.
+
+
+
+ {state.avatar
+ ?
+ :
add an image}
+
+
+
+
+
+
+