mirror of
https://github.com/owncast/owncast.git
synced 2024-11-23 13:24:33 +03:00
d1f3fffe2f
* refactor: move/rename BanUserButton file * refactor: move/rename Chart file * refactor: update generic component filenames to PascalCase * refactor: update config component filenames to PascalCase * refactor: update AdminLayout component filename to PascalCase * refactor: update/move VideoJS component * chore(eslint): disable bad react/require-default-props rule * refactor: normalize ActionButton component * refactor: normalize ActionButtonRow component * refactor: normalize FollowButton component * refactor: normalize NotifyButton component * refactor: normalize ChatActionMessage component * refactor: normalize ChatContainer component * refactor: normalize ChatJoinMessage component * refactor: normalize ChatModerationActionMenu component * refactor: normalize ChatModerationDetailsModal component * refactor: normalize ChatModeratorNotification component * refactor: normalize ChatSocialMessage component * refactor: normalize ChatSystemMessage component * refactor: normalize ChatTextField component * refactor: normalize ChatUserBadge component * refactor: normalize ChatUserMessage component * refactor: normalize ContentHeader component * refactor: normalize OwncastLogo component * refactor: normalize UserDropdown component * chore(eslint): modify react/function-component-definition rule * refactor: normalize CodecSelector component * refactor: update a bunch of functional components using eslint * refactor: update a bunch of functional components using eslint, pt2 * refactor: update a bunch of functional components using eslint, pt3 * refactor: replace all component->component default imports with named imports * refactor: replace all component-stories->component default imports with named imports * refactor: remove default exports from most components * chore(eslint): add eslint config files for the components and pages dirs * fix: use-before-define error in ChatContainer * Fix ChatContainer import * Only process .tsx files in Next builds Co-authored-by: Gabe Kangas <gabek@real-ity.com>
257 lines
7.5 KiB
TypeScript
257 lines
7.5 KiB
TypeScript
import { Button, Collapse } from 'antd';
|
|
import classNames from 'classnames';
|
|
import React, { useContext, useState, useEffect, FC } from 'react';
|
|
import { UpdateArgs } from '../../types/config-section';
|
|
import { ServerStatusContext } from '../../utils/server-status-context';
|
|
import { AlertMessageContext } from '../../utils/alert-message-context';
|
|
|
|
import {
|
|
postConfigUpdateToAPI,
|
|
API_S3_INFO,
|
|
RESET_TIMEOUT,
|
|
S3_TEXT_FIELDS_INFO,
|
|
} from '../../utils/config-constants';
|
|
import {
|
|
createInputStatus,
|
|
StatusState,
|
|
STATUS_ERROR,
|
|
STATUS_PROCESSING,
|
|
STATUS_SUCCESS,
|
|
} from '../../utils/input-statuses';
|
|
import { TextField } from './TextField';
|
|
import { FormStatusIndicator } from './FormStatusIndicator';
|
|
import isValidUrl from '../../utils/urls';
|
|
import { ToggleSwitch } from './ToggleSwitch';
|
|
|
|
const { Panel } = Collapse;
|
|
|
|
// we could probably add more detailed checks here
|
|
// `currentValues` is what's currently in the global store and in the db
|
|
function checkSaveable(formValues: any, currentValues: any) {
|
|
const {
|
|
endpoint,
|
|
accessKey,
|
|
secret,
|
|
bucket,
|
|
region,
|
|
enabled,
|
|
servingEndpoint,
|
|
acl,
|
|
forcePathStyle,
|
|
} = formValues;
|
|
// if fields are filled out and different from what's in store, then return true
|
|
if (enabled) {
|
|
if (!!endpoint && isValidUrl(endpoint) && !!accessKey && !!secret && !!bucket && !!region) {
|
|
if (
|
|
enabled !== currentValues.enabled ||
|
|
endpoint !== currentValues.endpoint ||
|
|
accessKey !== currentValues.accessKey ||
|
|
secret !== currentValues.secret ||
|
|
region !== currentValues.region ||
|
|
(!currentValues.servingEndpoint && servingEndpoint !== '') ||
|
|
(!!currentValues.servingEndpoint && servingEndpoint !== currentValues.servingEndpoint) ||
|
|
(!currentValues.acl && acl !== '') ||
|
|
(!!currentValues.acl && acl !== currentValues.acl) ||
|
|
forcePathStyle !== currentValues.forcePathStyle
|
|
) {
|
|
return true;
|
|
}
|
|
}
|
|
} else if (enabled !== currentValues.enabled) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export const EditStorage: FC = () => {
|
|
const [formDataValues, setFormDataValues] = useState(null);
|
|
const [submitStatus, setSubmitStatus] = useState<StatusState>(null);
|
|
|
|
const [shouldDisplayForm, setShouldDisplayForm] = useState(false);
|
|
const serverStatusData = useContext(ServerStatusContext);
|
|
const { serverConfig, setFieldInConfigState } = serverStatusData || {};
|
|
|
|
const { setMessage: setAlertMessage } = useContext(AlertMessageContext);
|
|
|
|
const { s3 } = serverConfig;
|
|
const {
|
|
accessKey = '',
|
|
acl = '',
|
|
bucket = '',
|
|
enabled = false,
|
|
endpoint = '',
|
|
region = '',
|
|
secret = '',
|
|
servingEndpoint = '',
|
|
forcePathStyle = false,
|
|
} = s3;
|
|
|
|
useEffect(() => {
|
|
setFormDataValues({
|
|
accessKey,
|
|
acl,
|
|
bucket,
|
|
enabled,
|
|
endpoint,
|
|
region,
|
|
secret,
|
|
servingEndpoint,
|
|
forcePathStyle,
|
|
});
|
|
setShouldDisplayForm(enabled);
|
|
}, [s3]);
|
|
|
|
if (!formDataValues) {
|
|
return null;
|
|
}
|
|
|
|
let resetTimer = null;
|
|
const resetStates = () => {
|
|
setSubmitStatus(null);
|
|
resetTimer = null;
|
|
clearTimeout(resetTimer);
|
|
};
|
|
|
|
// update individual values in state
|
|
const handleFieldChange = ({ fieldName, value }: UpdateArgs) => {
|
|
setFormDataValues({
|
|
...formDataValues,
|
|
[fieldName]: value,
|
|
});
|
|
};
|
|
|
|
// posts the whole state
|
|
const handleSave = async () => {
|
|
setSubmitStatus(createInputStatus(STATUS_PROCESSING));
|
|
const postValue = formDataValues;
|
|
|
|
await postConfigUpdateToAPI({
|
|
apiPath: API_S3_INFO,
|
|
data: { value: postValue },
|
|
onSuccess: () => {
|
|
setFieldInConfigState({ fieldName: 's3', value: postValue, path: '' });
|
|
setSubmitStatus(createInputStatus(STATUS_SUCCESS, 'Updated.'));
|
|
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
|
setAlertMessage(
|
|
'Changing your storage configuration will take place the next time you start a new stream.',
|
|
);
|
|
},
|
|
onError: (message: string) => {
|
|
setSubmitStatus(createInputStatus(STATUS_ERROR, message));
|
|
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
|
},
|
|
});
|
|
};
|
|
|
|
// toggle switch.
|
|
const handleSwitchChange = (storageEnabled: boolean) => {
|
|
setShouldDisplayForm(storageEnabled);
|
|
handleFieldChange({ fieldName: 'enabled', value: storageEnabled });
|
|
};
|
|
|
|
const handleForcePathStyleSwitchChange = (forcePathStyleEnabled: boolean) => {
|
|
handleFieldChange({ fieldName: 'forcePathStyle', value: forcePathStyleEnabled });
|
|
};
|
|
|
|
const containerClass = classNames({
|
|
'edit-storage-container': true,
|
|
'form-module': true,
|
|
enabled: shouldDisplayForm,
|
|
});
|
|
|
|
const isSaveable = checkSaveable(formDataValues, s3);
|
|
|
|
return (
|
|
<div className={containerClass}>
|
|
<div className="enable-switch">
|
|
<ToggleSwitch
|
|
apiPath=""
|
|
fieldName="enabled"
|
|
label="Use S3 Storage Provider"
|
|
checked={formDataValues.enabled}
|
|
onChange={handleSwitchChange}
|
|
/>
|
|
{/* <Switch
|
|
checked={formDataValues.enabled}
|
|
defaultChecked={formDataValues.enabled}
|
|
onChange={handleSwitchChange}
|
|
checkedChildren="ON"
|
|
unCheckedChildren="OFF"
|
|
/>{' '}
|
|
Enabled */}
|
|
</div>
|
|
|
|
<div className="form-fields">
|
|
<div className="field-container">
|
|
<TextField
|
|
{...S3_TEXT_FIELDS_INFO.endpoint}
|
|
value={formDataValues.endpoint}
|
|
onChange={handleFieldChange}
|
|
/>
|
|
</div>
|
|
<div className="field-container">
|
|
<TextField
|
|
{...S3_TEXT_FIELDS_INFO.accessKey}
|
|
value={formDataValues.accessKey}
|
|
onChange={handleFieldChange}
|
|
/>
|
|
</div>
|
|
<div className="field-container">
|
|
<TextField
|
|
{...S3_TEXT_FIELDS_INFO.secret}
|
|
value={formDataValues.secret}
|
|
onChange={handleFieldChange}
|
|
/>
|
|
</div>
|
|
<div className="field-container">
|
|
<TextField
|
|
{...S3_TEXT_FIELDS_INFO.bucket}
|
|
value={formDataValues.bucket}
|
|
onChange={handleFieldChange}
|
|
/>
|
|
</div>
|
|
<div className="field-container">
|
|
<TextField
|
|
{...S3_TEXT_FIELDS_INFO.region}
|
|
value={formDataValues.region}
|
|
onChange={handleFieldChange}
|
|
/>
|
|
</div>
|
|
|
|
<Collapse className="advanced-section">
|
|
<Panel header="Optional Settings" key="1">
|
|
<div className="field-container">
|
|
<TextField
|
|
{...S3_TEXT_FIELDS_INFO.acl}
|
|
value={formDataValues.acl}
|
|
onChange={handleFieldChange}
|
|
/>
|
|
</div>
|
|
<div className="field-container">
|
|
<TextField
|
|
{...S3_TEXT_FIELDS_INFO.servingEndpoint}
|
|
value={formDataValues.servingEndpoint}
|
|
onChange={handleFieldChange}
|
|
/>
|
|
</div>
|
|
<div className="enable-switch">
|
|
<ToggleSwitch
|
|
{...S3_TEXT_FIELDS_INFO.forcePathStyle}
|
|
fieldName="forcePathStyle"
|
|
checked={formDataValues.forcePathStyle}
|
|
onChange={handleForcePathStyleSwitchChange}
|
|
/>
|
|
</div>
|
|
</Panel>
|
|
</Collapse>
|
|
</div>
|
|
|
|
<div className="button-container">
|
|
<Button type="primary" onClick={handleSave} disabled={!isSaveable}>
|
|
Save
|
|
</Button>
|
|
<FormStatusIndicator status={submitStatus} />
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|