update profile layout

This commit is contained in:
Roland Osborne 2022-03-24 09:37:41 -07:00
parent c91a714e3c
commit 318ea81e7e
8 changed files with 81 additions and 21 deletions

View File

@ -8,8 +8,8 @@ function checkResponse(response) {
} }
} }
export function getProfileImageUrl(token) { export function getProfileImageUrl(token, revision) {
return '/profile/image?agent=' + token return '/profile/image?agent=' + token + "&revision=" + revision
} }
async function fetchWithTimeout(url, options) { async function fetchWithTimeout(url, options) {
@ -61,6 +61,12 @@ export async function setProfileData(token, name, location, description) {
return await profile.json() return await profile.json()
} }
export async function setProfileImage(token, image) {
let profile = await fetchWithTimeout('/profile/image?agent=' + token, { method: 'PUT', body: JSON.stringify(image), timeout: FETCH_TIMEOUT });
checkResponse(profile)
return await profile.json()
}
export async function getGroups(token, revision) { export async function getGroups(token, revision) {
let param = "?agent=" + token let param = "?agent=" + token
if (revision != null) { if (revision != null) {

View File

@ -1,5 +1,5 @@
import { useEffect, useState, useRef } from 'react'; import { useEffect, useState, useRef } from 'react';
import { setProfileData, getProfileImageUrl, getProfile, getGroups, getAvailable, getUsername, setLogin, createAccount } from './fetchUtil'; import { setProfileImage, setProfileData, getProfileImageUrl, getProfile, getGroups, getAvailable, getUsername, setLogin, createAccount } from './fetchUtil';
async function updateProfile(token, updateData) { async function updateProfile(token, updateData) {
let profile = await getProfile(token); let profile = await getProfile(token);
@ -71,10 +71,13 @@ export function useAppContext() {
appLogout(updateState, clearWebsocket); appLogout(updateState, clearWebsocket);
resetData(); resetData();
}, },
setProfile: async (name, location, description) => { setProfileData: async (name, location, description) => {
await setProfileData(state.token, name, location, description); await setProfileData(state.token, name, location, description);
}, },
profileImageUrl: () => getProfileImageUrl(state.token) setProfileImage: async (image) => {
await setProfileImage(state.token, image);
},
profileImageUrl: () => getProfileImageUrl(state.token, state.Data?.profile?.revision)
} }
const adminActions = { const adminActions = {

View File

@ -45,11 +45,17 @@ export function Profile(props) {
} }
const onProfileSave = async () => { const onProfileSave = async () => {
if (await actions.setModalProfile()) { if (await actions.setProfileData()) {
setInfoVisible(false); setInfoVisible(false);
} }
} }
const onImageSave = async () => {
if (await actions.setProfileImage()) {
setLogoVisible(false);
}
}
const onSelectImage = () => { const onSelectImage = () => {
imageFile.current.click(); imageFile.current.click();
}; };
@ -64,12 +70,12 @@ export function Profile(props) {
const Footer = ( const Footer = (
<ModalFooter> <ModalFooter>
<input type='file' id='file' ref={imageFile} onChange={e => selected(e)} style={{display: 'none'}}/> <input type='file' id='file' accept="image/*" ref={imageFile} onChange={e => selected(e)} style={{display: 'none'}}/>
<div class="select"> <div class="select">
<Button key="select" class="select" onClick={() => onSelectImage()}>Select Image</Button> <Button key="select" class="select" onClick={() => onSelectImage()}>Select Image</Button>
</div> </div>
<Button key="back" onClick={() => setLogoVisible(false)}>Cancel</Button> <Button key="back" onClick={() => setLogoVisible(false)}>Cancel</Button>
<Button key="save" type="primary" onClick={() => setLogoVisible(false)}>Save</Button> <Button key="save" type="primary" onClick={() => onImageSave()}>Save</Button>
</ModalFooter> </ModalFooter>
); );
@ -88,7 +94,7 @@ export function Profile(props) {
</div> </div>
</div> </div>
<div class="block" onClick={() => setInfoVisible(true)}> <div class="block" onClick={() => setInfoVisible(true)}>
<span class="label">detail:</span> <span class="label">details:</span>
<EditIcon class="detailedit" /> <EditIcon class="detailedit" />
</div> </div>
<div class="details"> <div class="details">
@ -99,7 +105,7 @@ export function Profile(props) {
</div> </div>
<div class="contact"></div> <div class="contact"></div>
</div> </div>
<Modal title="Profile Info" centered visible={infoVisible} okText="Save" <Modal title="Profile Details" centered visible={infoVisible} okText="Save"
onOk={() => onProfileSave()} onCancel={() => setInfoVisible(false)}> onOk={() => onProfileSave()} onCancel={() => setInfoVisible(false)}>
<ProfileInfo state={state} actions={actions} /> <ProfileInfo state={state} actions={actions} />
</Modal> </Modal>

View File

@ -84,6 +84,10 @@ export const ProfileWrapper = styled.div`
position: absolute; position: absolute;
padding-right: 8px; padding-right: 8px;
cursor: pointer; cursor: pointer;
background: #f6f5ed;
padding-left: 8px;
border-radius: 4px;
border: 1px solid #dddddd;
} }
.detailedit { .detailedit {
@ -98,7 +102,7 @@ export const ProfileWrapper = styled.div`
.label { .label {
padding-right: 8px; padding-right: 8px;
font-size: 1.2em; font-size: 1em;
font-weight: bold; font-weight: bold;
color: #888888; color: #888888;
} }

View File

@ -1,15 +1,15 @@
import React, { useState, useCallback } from 'react'; import React, { useState, useCallback } from 'react';
import Cropper from 'react-easy-crop' import Cropper from 'react-easy-crop'
import { UserOutlined } from '@ant-design/icons'; import { UserOutlined } from '@ant-design/icons';
import { ProfileImageWrapper, ProfileDefaultImage } from './ProfileImage.styled'; import { ProfileSpin, ProfileImageWrapper, ProfileDefaultImage } from './ProfileImage.styled';
export function ProfileImage({ state, actions }) { export function ProfileImage({ state, actions }) {
const [crop, setCrop] = useState({ x: 0, y: 0 }) const [crop, setCrop] = useState({ x: 0, y: 0 })
const [zoom, setZoom] = useState(1) const [zoom, setZoom] = useState(1)
const onCropComplete = useCallback((croppedArea, croppedAreaPixels) => { const onCropComplete = useCallback((area, crop) => {
console.log("crop complete"); actions.setModalCrop(crop.width, crop.height, crop.x, crop.y)
}); });
const Logo = () => { const Logo = () => {
@ -19,15 +19,12 @@ export function ProfileImage({ state, actions }) {
return <></> return <></>
} }
const onSelect = () => {
console.log("ON SELECT");
}
return ( return (
<ProfileImageWrapper> <ProfileImageWrapper>
<Cropper onClick={() => onSelect()} image={state.modalImage} crop={crop} zoom={zoom} aspect={1} <Cropper image={state.modalImage} crop={crop} zoom={zoom} aspect={1}
onCropChange={setCrop} onCropComplete={onCropComplete} onZoomChange={setZoom} /> onCropChange={setCrop} onCropComplete={onCropComplete} onZoomChange={setZoom} />
<Logo /> <Logo />
<ProfileSpin size="large" spinning={state.modalBusy} />
</ProfileImageWrapper> </ProfileImageWrapper>
) )
} }

View File

@ -1,3 +1,4 @@
import { Spin } from 'antd';
import styled from 'styled-components'; import styled from 'styled-components';
export const ProfileImageWrapper = styled.div` export const ProfileImageWrapper = styled.div`
@ -23,3 +24,8 @@ export const ProfileDefaultImage = styled.div`
cursor: pointer; cursor: pointer;
`; `;
export const ProfileSpin = styled(Spin)`
position: absolute;
x-index: 10;
`;

View File

@ -15,6 +15,7 @@ export function useProfile() {
modalLocation: '', modalLocation: '',
modalDescription: '', modalDescription: '',
modalImage: null, modalImage: null,
crop: { w :0, h: 0, x: 0, y: 0 }
}); });
const navigate = useNavigate(); const navigate = useNavigate();
@ -40,12 +41,15 @@ export function useProfile() {
setModalImage: (value) => { setModalImage: (value) => {
updateState({ modalImage: value }); updateState({ modalImage: value });
}, },
setModalProfile: async () => { setModalCrop: (w, h, x, y) => {
updateState({ crop: { w: w, h: h, x: x, y: y } });
},
setProfileData: async () => {
let set = false let set = false
if(!state.modalBusy) { if(!state.modalBusy) {
updateState({ modalBusy: true }); updateState({ modalBusy: true });
try { try {
await app.actions.setProfile(state.modalName, state.modalLocation, state.modalDescription); await app.actions.setProfileData(state.modalName, state.modalLocation, state.modalDescription);
set = true set = true
} }
catch (err) { catch (err) {
@ -55,6 +59,39 @@ export function useProfile() {
} }
return set return set
}, },
setProfileImage: async () => {
let set = false
if(!state.modalBusy) {
updateState({ modalBusy: true });
try {
const processImg = () => {
return new Promise((resolve, reject) => {
let img = new Image();
img.onload = () => {
var canvas = document.createElement("canvas");
var context = canvas.getContext('2d');
canvas.width = state.crop.w;
canvas.height = state.crop.h;
context.drawImage(img, state.crop.x, state.crop.y, state.crop.w, state.crop.h,
0, 0, state.crop.w, state.crop.h);
resolve(canvas.toDataURL());
}
img.onerror = reject;
img.src = state.modalImage;
});
};
let dataUrl = await processImg();
let data = dataUrl.split(",")[1];
await app.actions.setProfileImage(data);
set = true
}
catch (err) {
window.alert(err);
}
updateState({ modalBusy: false });
}
return set;
},
}; };
useEffect(() => { useEffect(() => {

View File

@ -10,6 +10,7 @@ export function Identity() {
const { state, actions } = useIdentity() const { state, actions } = useIdentity()
const Logo = () => { const Logo = () => {
console.log(state);
if (state.imageUrl != null) { if (state.imageUrl != null) {
if (state.imageUrl === '') { if (state.imageUrl === '') {
return <UserOutlined /> return <UserOutlined />