198 lines
6.8 KiB
TypeScript
198 lines
6.8 KiB
TypeScript
import {
|
|
Form,
|
|
FormGroup,
|
|
ActionGroup,
|
|
FileUpload,
|
|
Avatar,
|
|
Button,
|
|
HelperText,
|
|
HelperTextItem
|
|
} from "@patternfly/react-core";
|
|
import { useState, CSSProperties } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { useEnvironment } from "../root/KeycloakContext";
|
|
import { useAlerts, HelpItem } from "ui-shared";
|
|
|
|
interface AvatarFormProps {
|
|
accountUrl: string;
|
|
}
|
|
|
|
interface AvatarFormState {
|
|
errors: any;
|
|
imageBlob: any;
|
|
filename: string;
|
|
avatarUrl: string;
|
|
avatarSrc: string;
|
|
noAvatarSrc: string;
|
|
}
|
|
|
|
export const AvatarForm = ({accountUrl} : AvatarFormProps) => {
|
|
const { t } = useTranslation();
|
|
const context = useEnvironment();
|
|
const urlparts = accountUrl.indexOf('?') > 0 ? accountUrl.split('?') : accountUrl;
|
|
const currentAvatarUrl = Array.isArray(urlparts) ? urlparts[0] + "-avatar?" + urlparts[1] : urlparts + "-avatar";
|
|
const noavatar = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjAuMiwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAzNiAzNiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMzYgMzY7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRjBGMEYwO30KCS5zdDF7ZmlsbC1ydWxlOmV2ZW5vZGQ7Y2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbDojRDJEMkQyO30KCS5zdDJ7ZmlsbDojQjhCQkJFO30KCS5zdDN7ZmlsbDojRDJEMkQyO30KPC9zdHlsZT4KPHJlY3QgY2xhc3M9InN0MCIgd2lkdGg9IjM2IiBoZWlnaHQ9IjM2Ii8+CjxwYXRoIGNsYXNzPSJzdDEiIGQ9Ik0xNy43LDIwLjFjLTMuNSwwLTYuNC0yLjktNi40LTYuNHMyLjktNi40LDYuNC02LjRzNi40LDIuOSw2LjQsNi40UzIxLjMsMjAuMSwxNy43LDIwLjF6Ii8+CjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik0xMy4zLDM2bDAtNi43Yy0yLDAuNC0yLjksMS40LTMuMSwzLjVMMTAuMSwzNkgxMy4zeiIvPgo8cGF0aCBjbGFzcz0ic3QzIiBkPSJNMTAuMSwzNmwwLjEtMy4yYzAuMi0yLjEsMS4xLTMuMSwzLjEtMy41bDAsNi43aDkuNGwwLTYuN2MyLDAuNCwyLjksMS40LDMuMSwzLjVsMC4xLDMuMmg0LjcKCWMtMC40LTMuOS0xLjMtOS0yLjktMTFjLTEuMS0xLjQtMi4zLTIuMi0zLjUtMi42cy0xLjgtMC42LTYuMy0wLjZzLTYuMSwwLjctNi4xLDAuN2MtMS4yLDAuNC0yLjQsMS4yLTMuNCwyLjYKCUM2LjcsMjcsNS44LDMyLjIsNS40LDM2SDEwLjF6Ii8+CjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik0yNS45LDM2bC0wLjEtMy4yYy0wLjItMi4xLTEuMS0zLjEtMy4xLTMuNWwwLDYuN0gyNS45eiIvPgo8L3N2Zz4=";
|
|
const initialState = {
|
|
errors: {avatar: ""},
|
|
imageBlob: null,
|
|
filename: "",
|
|
avatarUrl: currentAvatarUrl,
|
|
avatarSrc: currentAvatarUrl,
|
|
noAvatarSrc: noavatar
|
|
};
|
|
const [state, setState] = useState<AvatarFormState>(initialState);
|
|
const { addAlert, addError } = useAlerts();
|
|
|
|
const reader = new FileReader();
|
|
let currentFilename = "";
|
|
reader.onloadend = (event: any) => {
|
|
var imgData = String(event.target!.result)
|
|
imageScale(imgData, (blob: Blob) => {
|
|
setState({
|
|
errors: {avatar: ""},
|
|
imageBlob: blob,
|
|
filename: currentFilename,
|
|
avatarUrl: currentAvatarUrl,
|
|
avatarSrc: URL.createObjectURL(blob),
|
|
noAvatarSrc: noavatar
|
|
})
|
|
})
|
|
};
|
|
|
|
const handleFileInputChange = (_: any, file: File) => {
|
|
if (file != null && file.name != "") {
|
|
if (file.type.startsWith("image")) {
|
|
currentFilename = file.name;
|
|
reader.readAsDataURL(file);
|
|
} else {
|
|
console.error("Wrong file type: " + file.type);
|
|
}
|
|
}
|
|
};
|
|
|
|
const handleClear = (_: any) => {
|
|
setState(initialState);
|
|
};
|
|
|
|
const imageScale = (imgData: string, callback: any) => {
|
|
var img = new Image()
|
|
img.src = imgData
|
|
img.onload = (event: Event) => {
|
|
var canvas = document.createElement("canvas")
|
|
var ctx = canvas.getContext("2d")
|
|
ctx!.drawImage(img, 0, 0)
|
|
|
|
var MAX_WIDTH = 250
|
|
var MAX_HEIGHT = 250
|
|
var width = img.width
|
|
var height = img.height
|
|
|
|
if (width > height) {
|
|
if (width > MAX_WIDTH) {
|
|
height *= MAX_WIDTH / width
|
|
width = MAX_WIDTH
|
|
}
|
|
} else {
|
|
if (height > MAX_HEIGHT) {
|
|
width *= MAX_HEIGHT / height
|
|
height = MAX_HEIGHT
|
|
}
|
|
}
|
|
canvas.width = width
|
|
canvas.height = height
|
|
ctx = canvas.getContext("2d")
|
|
ctx!.drawImage(img, 0, 0, width, height)
|
|
canvas.toBlob(callback)
|
|
}
|
|
}
|
|
|
|
const handleSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
|
|
event.preventDefault()
|
|
const form = event.target as HTMLFormElement
|
|
var formData = new FormData(form)
|
|
formData.append("image", state.imageBlob)
|
|
var xhr = new XMLHttpRequest();
|
|
xhr.onreadystatechange = () => {
|
|
if (xhr.readyState == 4) {
|
|
if (200 <= xhr.status && xhr.status <= 204) {
|
|
addAlert(t("avatarUpdatedMessage"));
|
|
// force reload avatar
|
|
setState(initialState);
|
|
} else {
|
|
addError(xhr.response);
|
|
}
|
|
}
|
|
}
|
|
xhr.open(form.method, form.action, true);
|
|
xhr.send(formData);
|
|
}
|
|
|
|
const handleError = (event: any) => {
|
|
setState({
|
|
errors: {avatar: t('error-noAvatarFound')},
|
|
imageBlob: null,
|
|
filename: "",
|
|
avatarUrl: "",
|
|
avatarSrc: "",
|
|
noAvatarSrc: noavatar
|
|
})
|
|
};
|
|
|
|
const { filename, avatarUrl, avatarSrc, noAvatarSrc } = state;
|
|
const avatarStyle = {
|
|
objectFit: 'cover',
|
|
width: '150px', height: '150px',
|
|
border: '1px solid lightgray',
|
|
boxShadow: 'lightgray 6px 3px 10px 2px'
|
|
} as CSSProperties;
|
|
|
|
return (
|
|
<Form id="avatarForm" method="post"
|
|
action={avatarUrl} encType="multipart/form-data"
|
|
onSubmit={handleSubmit}
|
|
>
|
|
<FormGroup label={t('avatarLabel')} fieldId="avatar-current-or-preview">
|
|
<HelperText>
|
|
{state.errors.avatar !== ""
|
|
&& <HelperTextItem variant="error" hasIcon>{state.errors.avatar}</HelperTextItem>
|
|
}
|
|
</HelperText>
|
|
{ avatarSrc !== ""
|
|
? <Avatar src={avatarSrc} style={avatarStyle} alt="Avatar image preview" onError={handleError}/>
|
|
: <Avatar src={noAvatarSrc} style={avatarStyle} alt="No avatar found" />
|
|
}
|
|
</FormGroup>
|
|
<FormGroup
|
|
fieldId="avatar-upload"
|
|
label={t('uploadLabel')}
|
|
labelIcon={
|
|
<HelpItem
|
|
helpText={t("avatarInfo")}
|
|
fieldLabelId="uploadLabel"
|
|
/>
|
|
}
|
|
>
|
|
<FileUpload
|
|
id="simple-file"
|
|
filename={filename}
|
|
filenamePlaceholder={t('dragdropInfo')}
|
|
browseButtonText={t('browseButton')}
|
|
onFileInputChange={handleFileInputChange}
|
|
clearButtonText={t('clearButton')}
|
|
onClearClick={handleClear}
|
|
/>
|
|
</FormGroup>
|
|
<ActionGroup>
|
|
<Button
|
|
id="save-btn" type="submit"
|
|
variant="primary"
|
|
isDisabled={filename === ""}
|
|
>
|
|
{t('doSave')}
|
|
</Button>
|
|
</ActionGroup>
|
|
</Form>
|
|
)
|
|
};
|
|
|
|
export default AvatarForm; |