Upload logo (#54)
* add upload component for logo * - move upload logo functionlity to its own component - style upload logo component - display current logo from server - implement submit button on logo updater, to submit new logo to api after update - add some submit status indicator * update edit-logo component Logo now posts correctly to owncast api endpoint. This update includes file type validation and removes the submit button, since the ant.d Upload component already handles the post logic. * remove submit-button style for logo upload Co-authored-by: gingervitis <omqmail@gmail.com>
This commit is contained in:
parent
ab0cf2ad9d
commit
3031f8144e
@ -13,15 +13,14 @@ import {
|
||||
TEXTFIELD_PROPS_SERVER_NAME,
|
||||
TEXTFIELD_PROPS_SERVER_SUMMARY,
|
||||
TEXTFIELD_PROPS_SERVER_WELCOME_MESSAGE,
|
||||
TEXTFIELD_PROPS_LOGO,
|
||||
API_YP_SWITCH,
|
||||
FIELD_PROPS_YP,
|
||||
FIELD_PROPS_NSFW,
|
||||
} from '../../utils/config-constants';
|
||||
import { NEXT_PUBLIC_API_HOST } from '../../utils/apis';
|
||||
|
||||
import { UpdateArgs } from '../../types/config-section';
|
||||
import ToggleSwitch from './form-toggleswitch';
|
||||
import EditLogo from './edit-logo';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
@ -106,16 +105,11 @@ export default function EditInstanceDetails() {
|
||||
initialValue={instanceDetails.welcomeMessage}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
<TextFieldWithSubmit
|
||||
fieldName="logo"
|
||||
{...TEXTFIELD_PROPS_LOGO}
|
||||
value={formDataValues.logo}
|
||||
initialValue={instanceDetails.logo}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
{instanceDetails.logo && <img src="/logo" alt="uploaded logo" className="logo-preview" />}
|
||||
<br />
|
||||
|
||||
{/* Logo section */}
|
||||
<EditLogo />
|
||||
|
||||
<br />
|
||||
<p className="description">
|
||||
Increase your audience by appearing in the{' '}
|
||||
<a href="https://directory.owncast.online" target="_blank" rel="noreferrer">
|
||||
|
122
web/components/config/edit-logo.tsx
Normal file
122
web/components/config/edit-logo.tsx
Normal file
@ -0,0 +1,122 @@
|
||||
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';
|
||||
|
||||
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 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);
|
||||
},
|
||||
onError: (msg: string) => {
|
||||
setSubmitStatus(createInputStatus(STATUS_ERROR, `There was an error: ${msg}`));
|
||||
setLoading(false);
|
||||
},
|
||||
});
|
||||
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
||||
}
|
||||
};
|
||||
|
||||
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={logoUrl || currentLogo || '/logo'} 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>
|
||||
);
|
||||
}
|
@ -38,13 +38,6 @@
|
||||
}
|
||||
.instance-details-container {
|
||||
width: 100%;
|
||||
|
||||
.logo-preview {
|
||||
display: inline-block;
|
||||
margin: -1em 0 1em 11em;
|
||||
height: 120px;
|
||||
border: 1px solid var(--white-25);
|
||||
}
|
||||
}
|
||||
.social-items-container {
|
||||
background-color: var(--container-bg-color-alt);
|
||||
@ -74,3 +67,19 @@
|
||||
.other-field-container {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
|
||||
.logo-upload-container {
|
||||
.input-group {
|
||||
align-items: center;
|
||||
}
|
||||
img.logo-preview {
|
||||
min-height: 120px;
|
||||
min-width: 120px;
|
||||
max-height: 256px;
|
||||
max-width: 256px;
|
||||
margin-right: 1em;
|
||||
display: inline-block;
|
||||
border: 1px solid var(--white-25);
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ export const TEXTFIELD_PROPS_LOGO = {
|
||||
placeholder: '/img/mylogo.png',
|
||||
label: 'Logo',
|
||||
tip:
|
||||
'Name of your logo in the data directory. We recommend that you use a square image that is at least 256x256.',
|
||||
'Upload your logo if you have one. We recommend that you use a square image that is at least 256x256.',
|
||||
};
|
||||
export const TEXTFIELD_PROPS_STREAM_KEY = {
|
||||
apiPath: API_STREAM_KEY,
|
||||
|
Loading…
Reference in New Issue
Block a user