// Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import React, { FormEventHandler, FunctionComponent, useEffect, useRef, useState, } from 'react'; import { noop } from 'lodash'; import { LocalizerType } from '../../../types/Util'; import { ModalHost } from '../../ModalHost'; import { AvatarInput, AvatarInputVariant } from '../../AvatarInput'; import { Button, ButtonVariant } from '../../Button'; import { Spinner } from '../../Spinner'; import { GroupTitleInput } from '../../GroupTitleInput'; import * as log from '../../../logging/log'; import { canvasToArrayBuffer } from '../../../util/canvasToArrayBuffer'; import { RequestState } from './util'; const TEMPORARY_AVATAR_VALUE = new ArrayBuffer(0); type PropsType = { avatarPath?: string; i18n: LocalizerType; makeRequest: ( _: Readonly<{ avatar?: undefined | ArrayBuffer; title?: undefined | string; }> ) => void; onClose: () => void; requestState: RequestState; title: string; }; export const EditConversationAttributesModal: FunctionComponent = ({ avatarPath: externalAvatarPath, i18n, makeRequest, onClose, requestState, title: externalTitle, }) => { const startingTitleRef = useRef(externalTitle); const startingAvatarPathRef = useRef(externalAvatarPath); const [avatar, setAvatar] = useState( externalAvatarPath ? TEMPORARY_AVATAR_VALUE : undefined ); const [rawTitle, setRawTitle] = useState(externalTitle); const [hasAvatarChanged, setHasAvatarChanged] = useState(false); const trimmedTitle = rawTitle.trim(); useEffect(() => { const startingAvatarPath = startingAvatarPathRef.current; if (!startingAvatarPath) { return noop; } let shouldCancel = false; (async () => { try { const buffer = await imagePathToArrayBuffer(startingAvatarPath); if (shouldCancel) { return; } setAvatar(buffer); } catch (err) { log.warn( `Failed to convert image URL to array buffer. Error message: ${ err && err.message }` ); } })(); return () => { shouldCancel = true; }; }, []); const hasChangedExternally = startingAvatarPathRef.current !== externalAvatarPath || startingTitleRef.current !== externalTitle; const hasTitleChanged = trimmedTitle !== externalTitle.trim(); const isRequestActive = requestState === RequestState.Active; const canSubmit = !isRequestActive && (hasChangedExternally || hasTitleChanged || hasAvatarChanged) && trimmedTitle.length > 0; const onSubmit: FormEventHandler = event => { event.preventDefault(); const request: { avatar?: undefined | ArrayBuffer; title?: string; } = {}; if (hasAvatarChanged) { request.avatar = avatar; } if (hasTitleChanged) { request.title = trimmedTitle; } makeRequest(request); }; return (
); }; async function imagePathToArrayBuffer(src: string): Promise { const image = new Image(); const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); if (!context) { throw new Error( 'imagePathToArrayBuffer: could not get canvas rendering context' ); } image.src = src; await image.decode(); canvas.width = image.width; canvas.height = image.height; context.drawImage(image, 0, 0); const result = await canvasToArrayBuffer(canvas); return result; }