From cc0cc154b9506d1961d08cb29fbc29ad815bad69 Mon Sep 17 00:00:00 2001 From: crupest Date: Mon, 24 Aug 2020 22:59:45 +0800 Subject: ... --- .../src/app/timeline/TimelinePostEdit.tsx | 468 ++++++++++----------- 1 file changed, 234 insertions(+), 234 deletions(-) (limited to 'Timeline/ClientApp/src/app/timeline/TimelinePostEdit.tsx') diff --git a/Timeline/ClientApp/src/app/timeline/TimelinePostEdit.tsx b/Timeline/ClientApp/src/app/timeline/TimelinePostEdit.tsx index d4d626ae..151df40a 100644 --- a/Timeline/ClientApp/src/app/timeline/TimelinePostEdit.tsx +++ b/Timeline/ClientApp/src/app/timeline/TimelinePostEdit.tsx @@ -1,234 +1,234 @@ -import React from 'react'; -import { Button, Spinner, Row, Col } from 'reactstrap'; -import { useTranslation } from 'react-i18next'; -import Svg from 'react-inlinesvg'; - -import textIcon from 'bootstrap-icons/icons/card-text.svg'; -import imageIcon from 'bootstrap-icons/icons/image.svg'; - -import { pushAlert } from '../common/alert-service'; -import { TimelineCreatePostRequest } from '../data/timeline'; - -import FileInput from '../common/FileInput'; -import { UiLogicError } from '../common'; - -interface TimelinePostEditImageProps { - onSelect: (blob: Blob | null) => void; -} - -const TimelinePostEditImage: React.FC = (props) => { - const { onSelect } = props; - const { t } = useTranslation(); - - const [file, setFile] = React.useState(null); - const [fileUrl, setFileUrl] = React.useState(null); - const [error, setError] = React.useState(null); - - React.useEffect(() => { - if (file != null) { - const url = URL.createObjectURL(file); - setFileUrl(url); - return () => { - URL.revokeObjectURL(url); - }; - } - }, [file]); - - const onInputChange: React.ChangeEventHandler = React.useCallback( - (e) => { - const files = e.target.files; - if (files == null || files.length === 0) { - setFile(null); - setFileUrl(null); - } else { - setFile(files[0]); - } - onSelect(null); - setError(null); - }, - [onSelect] - ); - - const onImgLoad = React.useCallback(() => { - onSelect(file); - }, [onSelect, file]); - - const onImgError = React.useCallback(() => { - setError('loadImageError'); - }, []); - - return ( - <> - - {fileUrl && error == null && ( - - )} - {error != null &&
{t(error)}
} - - ); -}; - -export type TimelinePostSendCallback = ( - content: TimelineCreatePostRequest -) => Promise; - -export interface TimelinePostEditProps { - className?: string; - onPost: TimelinePostSendCallback; - onHeightChange?: (height: number) => void; - timelineUniqueId: string; -} - -const TimelinePostEdit: React.FC = (props) => { - const { onPost } = props; - - const { t } = useTranslation(); - - const [state, setState] = React.useState<'input' | 'process'>('input'); - const [kind, setKind] = React.useState<'text' | 'image'>('text'); - const [text, setText] = React.useState(''); - const [imageBlob, setImageBlob] = React.useState(null); - - const draftLocalStorageKey = `timeline.${props.timelineUniqueId}.postDraft`; - - React.useEffect(() => { - setText(window.localStorage.getItem(draftLocalStorageKey) ?? ''); - }, [draftLocalStorageKey]); - - const canSend = kind === 'text' || (kind === 'image' && imageBlob != null); - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const containerRef = React.useRef(null!); - - const notifyHeightChange = (): void => { - if (props.onHeightChange) { - props.onHeightChange(containerRef.current.clientHeight); - } - }; - - React.useEffect(() => { - if (props.onHeightChange) { - props.onHeightChange(containerRef.current.clientHeight); - } - return () => { - if (props.onHeightChange) { - props.onHeightChange(0); - } - }; - }); - - const toggleKind = React.useCallback(() => { - setKind((oldKind) => (oldKind === 'text' ? 'image' : 'text')); - setImageBlob(null); - }, []); - - const onSend = React.useCallback(() => { - setState('process'); - - const req: TimelineCreatePostRequest = (() => { - switch (kind) { - case 'text': - return { - content: { - type: 'text', - text: text, - }, - } as TimelineCreatePostRequest; - case 'image': - if (imageBlob == null) { - throw new UiLogicError( - 'Content type is image but image blob is null.' - ); - } - return { - content: { - type: 'image', - data: imageBlob, - }, - } as TimelineCreatePostRequest; - default: - throw new UiLogicError('Unknown content type.'); - } - })(); - - onPost(req).then( - (_) => { - if (kind === 'text') { - setText(''); - window.localStorage.removeItem(draftLocalStorageKey); - } - setState('input'); - setKind('text'); - }, - (_) => { - pushAlert({ - type: 'danger', - message: t('timeline.sendPostFailed'), - }); - setState('input'); - } - ); - }, [onPost, kind, text, imageBlob, t, draftLocalStorageKey]); - - const onImageSelect = React.useCallback((blob: Blob | null) => { - setImageBlob(blob); - }, []); - - return ( -
- - - {kind === 'text' ? ( -