123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- import { Avatar, Button, Card, Col, Row, Tooltip, Typography } from 'antd';
- import Upload, { RcFile } from 'antd/lib/upload';
- import React, { ReactElement, useEffect, useState } from 'react';
- import dynamic from 'next/dynamic';
- import FormStatusIndicator from '../../../components/admin/FormStatusIndicator';
- import { DELETE_EMOJI, fetchData, UPLOAD_EMOJI } from '../../../utils/apis';
- import { ACCEPTED_IMAGE_TYPES, getBase64 } from '../../../utils/images';
- import {
- createInputStatus,
- STATUS_ERROR,
- STATUS_PROCESSING,
- STATUS_SUCCESS,
- } from '../../../utils/input-statuses';
- import { RESET_TIMEOUT } from '../../../utils/config-constants';
- import { AdminLayout } from '../../../components/layouts/AdminLayout';
- const URL_CUSTOM_EMOJIS = `/api/emoji`;
- const { Meta } = Card;
- // Lazy loaded components
- const CloseOutlined = dynamic(() => import('@ant-design/icons/CloseOutlined'), {
- ssr: false,
- });
- type CustomEmoji = {
- name: string;
- url: string;
- };
- const { Title, Paragraph } = Typography;
- const Emoji = () => {
- const [emojis, setEmojis] = useState<CustomEmoji[]>([]);
- const [loading, setLoading] = useState(false);
- const [submitStatus, setSubmitStatus] = useState(null);
- const [uploadFile, setUploadFile] = useState<RcFile>(null);
- let resetTimer = null;
- const resetStates = () => {
- setSubmitStatus(null);
- clearTimeout(resetTimer);
- resetTimer = null;
- };
- async function getEmojis() {
- setLoading(true);
- try {
- const response = await fetchData(URL_CUSTOM_EMOJIS);
- setEmojis(response);
- } catch (error) {
- console.error('error fetching emojis', error);
- }
- setLoading(false);
- }
- useEffect(() => {
- getEmojis();
- }, []);
- async function handleDelete(fullPath: string) {
- const name = `/${fullPath.split('/').slice(3).join('/')}`;
- console.log(name);
- setLoading(true);
- setSubmitStatus(createInputStatus(STATUS_PROCESSING, 'Deleting emoji...'));
- try {
- const response = await fetchData(DELETE_EMOJI, {
- method: 'POST',
- data: { name },
- });
- if (response instanceof Error) {
- throw response;
- }
- setSubmitStatus(createInputStatus(STATUS_SUCCESS, 'Emoji deleted'));
- resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
- } catch (error) {
- setSubmitStatus(createInputStatus(STATUS_ERROR, `${error}`));
- setLoading(false);
- resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
- }
- getEmojis();
- }
- async function handleEmojiUpload() {
- setLoading(true);
- try {
- setSubmitStatus(createInputStatus(STATUS_PROCESSING, 'Converting emoji...'));
- // eslint-disable-next-line consistent-return
- const emojiData = await new Promise<CustomEmoji>((res, rej) => {
- if (!ACCEPTED_IMAGE_TYPES.includes(uploadFile.type)) {
- const msg = `File type is not supported: ${uploadFile.type}`;
- // eslint-disable-next-line no-promise-executor-return
- return rej(msg);
- }
- getBase64(uploadFile, (url: string) =>
- res({
- name: uploadFile.name,
- url,
- }),
- );
- });
- setSubmitStatus(createInputStatus(STATUS_PROCESSING, 'Uploading emoji...'));
- const response = await fetchData(UPLOAD_EMOJI, {
- method: 'POST',
- data: {
- name: emojiData.name,
- data: emojiData.url,
- },
- });
- if (response instanceof Error) {
- throw response;
- }
- setSubmitStatus(createInputStatus(STATUS_SUCCESS, 'Emoji uploaded successfully!'));
- getEmojis();
- } catch (error) {
- setSubmitStatus(createInputStatus(STATUS_ERROR, `${error}`));
- }
- resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
- setLoading(false);
- }
- return (
- <div>
- <Title>Emojis</Title>
- <Paragraph>
- Here you can upload new custom emojis for usage in the chat. When uploading a new emoji, the
- filename will be used as emoji name.
- </Paragraph>
- <br />
- <Upload
- name="emoji"
- listType="picture"
- className="emoji-uploader"
- showUploadList={false}
- accept={ACCEPTED_IMAGE_TYPES.join(',')}
- beforeUpload={setUploadFile}
- customRequest={handleEmojiUpload}
- disabled={loading}
- >
- <Button type="primary" disabled={loading}>
- Upload new emoji
- </Button>
- </Upload>
- <FormStatusIndicator status={submitStatus} />
- <br />
- <Row>
- {emojis.map(record => (
- <Col style={{ padding: '10px' }} key={record.name}>
- <Card style={{ width: 120, marginTop: 16 }} actions={[]}>
- <Meta
- description={[
- <div
- style={{
- display: 'flex',
- justifyItems: 'center',
- alignItems: 'center',
- flexDirection: 'column',
- gap: '20px',
- }}
- >
- <Tooltip title={record.name}>
- <Avatar style={{ height: 50, width: 50 }} src={record.url} />
- </Tooltip>
- <Button
- size="small"
- type="ghost"
- title="Delete emoji"
- style={{
- position: 'absolute',
- right: 0,
- top: 0,
- height: 24,
- width: 24,
- border: 'none',
- color: 'gray',
- }}
- onClick={() => handleDelete(record.url)}
- icon={<CloseOutlined />}
- />
- </div>,
- ]}
- />
- </Card>
- </Col>
- ))}
- </Row>
- <br />
- </div>
- );
- };
- Emoji.getLayout = function getLayout(page: ReactElement) {
- return <AdminLayout page={page} />;
- };
- export default Emoji;
|