diff --git a/web/components/config/edit-instance-details.tsx b/web/components/config/edit-instance-details.tsx index 4f7311467..70e1dbac1 100644 --- a/web/components/config/edit-instance-details.tsx +++ b/web/components/config/edit-instance-details.tsx @@ -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} /> - - {instanceDetails.logo && uploaded logo} -
+ {/* Logo section */} + + +

Increase your audience by appearing in the{' '} diff --git a/web/components/config/edit-logo.tsx b/web/components/config/edit-logo.tsx new file mode 100644 index 000000000..89ff83812 --- /dev/null +++ b/web/components/config/edit-logo.tsx @@ -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(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((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 ( +

+
+ Logo +
+ +
+
+ avatar + + {loading ? ( + + ) : ( +
+ +

{tip}

+
+
+ ); +} diff --git a/web/styles/config-public-details.scss b/web/styles/config-public-details.scss index c03d9a13b..69835a009 100644 --- a/web/styles/config-public-details.scss +++ b/web/styles/config-public-details.scss @@ -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); + } +} diff --git a/web/utils/config-constants.tsx b/web/utils/config-constants.tsx index ac5b68891..af04be30f 100644 --- a/web/utils/config-constants.tsx +++ b/web/utils/config-constants.tsx @@ -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,