EditCustomJavascript.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import React, { useState, useEffect, useContext, FC } from 'react';
  2. import { Typography, Button } from 'antd';
  3. import CodeMirror from '@uiw/react-codemirror';
  4. import { bbedit } from '@uiw/codemirror-theme-bbedit';
  5. import { javascript } from '@codemirror/lang-javascript';
  6. import { ServerStatusContext } from '../../utils/server-status-context';
  7. import {
  8. postConfigUpdateToAPI,
  9. RESET_TIMEOUT,
  10. API_CUSTOM_JAVASCRIPT,
  11. } from '../../utils/config-constants';
  12. import {
  13. createInputStatus,
  14. StatusState,
  15. STATUS_ERROR,
  16. STATUS_PROCESSING,
  17. STATUS_SUCCESS,
  18. } from '../../utils/input-statuses';
  19. import { FormStatusIndicator } from './FormStatusIndicator';
  20. const { Title } = Typography;
  21. // eslint-disable-next-line import/prefer-default-export
  22. export const EditCustomJavascript: FC = () => {
  23. const [content, setContent] = useState('/* Enter custom Javascript here */');
  24. const [submitStatus, setSubmitStatus] = useState<StatusState>(null);
  25. const [hasChanged, setHasChanged] = useState(false);
  26. const serverStatusData = useContext(ServerStatusContext);
  27. const { serverConfig, setFieldInConfigState } = serverStatusData || {};
  28. const { instanceDetails } = serverConfig;
  29. const { customJavascript: initialContent } = instanceDetails;
  30. let resetTimer = null;
  31. // Clear out any validation states and messaging
  32. const resetStates = () => {
  33. setSubmitStatus(null);
  34. setHasChanged(false);
  35. clearTimeout(resetTimer);
  36. resetTimer = null;
  37. };
  38. // posts all the tags at once as an array obj
  39. async function handleSave() {
  40. setSubmitStatus(createInputStatus(STATUS_PROCESSING));
  41. await postConfigUpdateToAPI({
  42. apiPath: API_CUSTOM_JAVASCRIPT,
  43. data: { value: content },
  44. onSuccess: (message: string) => {
  45. setFieldInConfigState({
  46. fieldName: 'customJavascript',
  47. value: content,
  48. path: 'instanceDetails',
  49. });
  50. setSubmitStatus(createInputStatus(STATUS_SUCCESS, message));
  51. },
  52. onError: (message: string) => {
  53. setSubmitStatus(createInputStatus(STATUS_ERROR, message));
  54. },
  55. });
  56. resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
  57. }
  58. useEffect(() => {
  59. setContent(initialContent);
  60. }, [instanceDetails]);
  61. const onValueChange = React.useCallback(value => {
  62. setContent(value);
  63. if (value !== initialContent && !hasChanged) {
  64. setHasChanged(true);
  65. } else if (value === initialContent && hasChanged) {
  66. setHasChanged(false);
  67. }
  68. }, []);
  69. return (
  70. <div className="edit-custom-css">
  71. <Title level={3} className="section-title">
  72. Customize your page with Javascript
  73. </Title>
  74. <p className="description">
  75. Insert custom Javascript into your Owncast page to add your own functionality or to add 3rd
  76. party scripts. Read more about how to use this feature in the{' '}
  77. <a href="https://owncast.online/docs/website/" rel="noopener noreferrer" target="_blank">
  78. Web page documentation.
  79. </a>
  80. .
  81. </p>
  82. <p className="description">Please use raw Javascript, no HTML or any script tags.</p>
  83. <CodeMirror
  84. value={content}
  85. placeholder="/* Enter custom Javascript here */"
  86. theme={bbedit}
  87. height="200px"
  88. extensions={[javascript()]}
  89. onChange={onValueChange}
  90. />
  91. <br />
  92. <div className="page-content-actions">
  93. {hasChanged && (
  94. <Button type="primary" onClick={handleSave}>
  95. Save
  96. </Button>
  97. )}
  98. <FormStatusIndicator status={submitStatus} />
  99. </div>
  100. </div>
  101. );
  102. };