2021-08-06 00:17:05 +00:00
|
|
|
// Copyright 2021 Signal Messenger, LLC
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
2021-10-26 19:15:33 +00:00
|
|
|
import type { ChangeEventHandler } from 'react';
|
|
|
|
import React, { useEffect, useRef, useState } from 'react';
|
2021-08-06 00:17:05 +00:00
|
|
|
import { noop } from 'lodash';
|
|
|
|
|
2021-10-26 19:15:33 +00:00
|
|
|
import type { LocalizerType } from '../types/Util';
|
2021-08-06 00:17:05 +00:00
|
|
|
import { processImageFile } from '../util/processImageFile';
|
|
|
|
|
|
|
|
export type PropsType = {
|
|
|
|
className: string;
|
|
|
|
i18n: LocalizerType;
|
2021-09-24 00:49:05 +00:00
|
|
|
onChange: (avatar: Uint8Array) => unknown;
|
2021-08-06 00:17:05 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
export const AvatarUploadButton = ({
|
|
|
|
className,
|
|
|
|
i18n,
|
|
|
|
onChange,
|
|
|
|
}: PropsType): JSX.Element => {
|
|
|
|
const fileInputRef = useRef<null | HTMLInputElement>(null);
|
|
|
|
|
|
|
|
const [processingFile, setProcessingFile] = useState<File | undefined>();
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (!processingFile) {
|
|
|
|
return noop;
|
|
|
|
}
|
|
|
|
|
|
|
|
let shouldCancel = false;
|
|
|
|
|
|
|
|
(async () => {
|
2021-09-24 00:49:05 +00:00
|
|
|
let newAvatar: Uint8Array;
|
2021-08-06 00:17:05 +00:00
|
|
|
try {
|
|
|
|
newAvatar = await processImageFile(processingFile);
|
|
|
|
} catch (err) {
|
|
|
|
// Processing errors should be rare; if they do, we silently fail. In an ideal
|
|
|
|
// world, we may want to show a toast instead.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (shouldCancel) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
setProcessingFile(undefined);
|
|
|
|
onChange(newAvatar);
|
|
|
|
})();
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
shouldCancel = true;
|
|
|
|
};
|
|
|
|
}, [onChange, processingFile]);
|
|
|
|
|
|
|
|
const onInputChange: ChangeEventHandler<HTMLInputElement> = event => {
|
|
|
|
const file = event.target.files && event.target.files[0];
|
|
|
|
if (file) {
|
|
|
|
setProcessingFile(file);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<button
|
|
|
|
className={className}
|
|
|
|
onClick={() => {
|
|
|
|
const fileInput = fileInputRef.current;
|
|
|
|
if (fileInput) {
|
|
|
|
// Setting the value to empty so that onChange always fires in case
|
|
|
|
// you add multiple photos.
|
|
|
|
fileInput.value = '';
|
|
|
|
fileInput.click();
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
type="button"
|
|
|
|
>
|
|
|
|
{i18n('photo')}
|
|
|
|
</button>
|
|
|
|
<input
|
|
|
|
accept=".gif,.jpg,.jpeg,.png,.webp,image/gif,image/jpeg,image/png,image/webp"
|
|
|
|
hidden
|
|
|
|
onChange={onInputChange}
|
|
|
|
ref={fileInputRef}
|
|
|
|
type="file"
|
|
|
|
/>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
};
|