mirror of
				https://github.com/owncast/owncast.git
				synced 2025-11-04 13:27:21 +08:00 
			
		
		
		
	* 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>
		
			
				
	
	
		
			258 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			258 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>
 | 
						|
  );
 | 
						|
};
 |