owncast/web/components/config/edit-logo.tsx
Jannik be5243f5f8 Update Next to 11.0.1 (including lint & import fixes) (#248)
* Bump next from 10.2.3 to 11.0.1

Bumps [next](https://github.com/vercel/next.js) from 10.2.3 to 11.0.1.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v10.2.3...v11.0.1)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* 🚨 apply automatic linting

* 🎨 remove unused imports

* 🔇 allow console.* to give more debugging options

* 🎨 move stuff around to reduce linter messages

* 🚨 use destructuring so lint won't complain

* 📌 link Chartkick and Chart.js

Commit uses the linking code which was previously imported with
`import "chartkick/chart.js" [1]. Next did not like the import path,
but this does works now. ¯\_(ツ)_/¯

[1]: https://github.com/ankane/chartkick.js/blob/master/chart.js/chart.esm.js

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-07-09 11:42:01 -07:00

127 lines
4.1 KiB
TypeScript

import { Button, Upload } from 'antd';
import { RcFile } from 'antd/lib/upload/interface';
import { LoadingOutlined, UploadOutlined } from '@ant-design/icons';
import React, { useState, useContext } from 'react';
import FormStatusIndicator from './form-status-indicator';
import { ServerStatusContext } from '../../utils/server-status-context';
import {
postConfigUpdateToAPI,
RESET_TIMEOUT,
TEXTFIELD_PROPS_LOGO,
} from '../../utils/config-constants';
import {
createInputStatus,
StatusState,
STATUS_ERROR,
STATUS_PROCESSING,
STATUS_SUCCESS,
} from '../../utils/input-statuses';
import { NEXT_PUBLIC_API_HOST } from '../../utils/apis';
const ACCEPTED_FILE_TYPES = ['image/svg+xml', 'image/png', 'image/jpeg', 'image/gif'];
function getBase64(img: File | Blob, callback: (imageUrl: string | ArrayBuffer) => void) {
const reader = new FileReader();
reader.addEventListener('load', () => callback(reader.result));
reader.readAsDataURL(img);
}
export default function EditLogo() {
const [logoUrl, setlogoUrl] = useState(null);
const [loading, setLoading] = useState(false);
const [logoCachedbuster, setLogoCacheBuster] = useState(0);
const serverStatusData = useContext(ServerStatusContext);
const { setFieldInConfigState, serverConfig } = serverStatusData || {};
const currentLogo = serverConfig?.instanceDetails?.logo;
const [submitStatus, setSubmitStatus] = useState<StatusState>(null);
let resetTimer = null;
const { apiPath, tip } = TEXTFIELD_PROPS_LOGO;
// Clear out any validation states and messaging
const resetStates = () => {
setSubmitStatus(null);
clearTimeout(resetTimer);
resetTimer = null;
};
// validate file type and create base64 encoded img
const beforeUpload = (file: RcFile) => {
setLoading(true);
// eslint-disable-next-line consistent-return
return new Promise<void>((res, rej) => {
if (!ACCEPTED_FILE_TYPES.includes(file.type)) {
const msg = `File type is not supported: ${file.type}`;
setSubmitStatus(createInputStatus(STATUS_ERROR, `There was an error: ${msg}`));
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
setLoading(false);
return rej();
}
getBase64(file, (url: string) => {
setlogoUrl(url);
return res();
});
});
};
// Post new logo to api
const handleLogoUpdate = async () => {
if (logoUrl !== currentLogo) {
setSubmitStatus(createInputStatus(STATUS_PROCESSING));
await postConfigUpdateToAPI({
apiPath,
data: { value: logoUrl },
onSuccess: () => {
setFieldInConfigState({ fieldName: 'logo', value: logoUrl, path: '' });
setSubmitStatus(createInputStatus(STATUS_SUCCESS));
setLoading(false);
setLogoCacheBuster(Math.floor(Math.random() * 100)); // Force logo to re-load
},
onError: (msg: string) => {
setSubmitStatus(createInputStatus(STATUS_ERROR, `There was an error: ${msg}`));
setLoading(false);
},
});
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
}
};
const logoDisplayUrl = `${NEXT_PUBLIC_API_HOST}logo?random=${logoCachedbuster}`;
return (
<div className="formfield-container logo-upload-container">
<div className="label-side">
<span className="formfield-label">Logo</span>
</div>
<div className="input-side">
<div className="input-group">
<img src={logoDisplayUrl} alt="avatar" className="logo-preview" />
<Upload
name="logo"
listType="picture"
className="avatar-uploader"
showUploadList={false}
accept={ACCEPTED_FILE_TYPES.join(',')}
beforeUpload={beforeUpload}
customRequest={handleLogoUpdate}
disabled={loading}
>
{loading ? (
<LoadingOutlined style={{ color: 'white' }} />
) : (
<Button icon={<UploadOutlined />} />
)}
</Upload>
</div>
<FormStatusIndicator status={submitStatus} />
<p className="field-tip">{tip}</p>
</div>
</div>
);
}