owncast/web/pages/admin/webhooks.tsx

252 lines
6.1 KiB
TypeScript
Raw Normal View History

2021-07-20 08:02:02 +03:00
/* eslint-disable react/destructuring-assignment */
import { DeleteOutlined } from '@ant-design/icons';
import {
Button,
Checkbox,
Col,
Input,
Modal,
Row,
Space,
Table,
Tag,
Tooltip,
Typography,
} from 'antd';
import React, { useEffect, useState } from 'react';
import { CREATE_WEBHOOK, DELETE_WEBHOOK, fetchData, WEBHOOKS } from '../../utils/apis';
import isValidUrl, { DEFAULT_TEXTFIELD_URL_PATTERN } from '../../utils/urls';
2021-01-06 03:35:37 +03:00
2021-02-14 12:30:42 +03:00
const { Title, Paragraph } = Typography;
2021-01-06 03:35:37 +03:00
const availableEvents = {
CHAT: { name: 'Chat messages', description: 'When a user sends a chat message', color: 'purple' },
USER_JOINED: { name: 'User joined', description: 'When a user joins the chat', color: 'green' },
NAME_CHANGE: {
name: 'User name changed',
description: 'When a user changes their name',
color: 'blue',
},
'VISIBILITY-UPDATE': {
name: 'Message visibility changed',
description: 'When a message visibility changes, likely due to moderation',
color: 'red',
},
STREAM_STARTED: { name: 'Stream started', description: 'When a stream starts', color: 'orange' },
STREAM_STOPPED: { name: 'Stream stopped', description: 'When a stream stops', color: 'cyan' },
2021-01-06 03:35:37 +03:00
};
function convertEventStringToTag(eventString: string) {
if (!eventString || !availableEvents[eventString]) {
return null;
}
2021-01-06 03:35:37 +03:00
const event = availableEvents[eventString];
2021-01-06 03:35:37 +03:00
return (
<Tooltip key={eventString} title={event.description}>
<Tag color={event.color}>{event.name}</Tag>
</Tooltip>
);
2021-01-06 03:35:37 +03:00
}
2021-02-15 11:36:06 +03:00
interface Props {
onCancel: () => void;
onOk: any; // todo: make better type
visible: boolean;
}
reafctor: normalize component formatting (#2082) * 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>
2022-09-07 10:00:28 +03:00
const NewWebhookModal = (props: Props) => {
2021-02-15 11:36:06 +03:00
const { onOk, onCancel, visible } = props;
2021-01-06 03:35:37 +03:00
const [selectedEvents, setSelectedEvents] = useState([]);
const [webhookUrl, setWebhookUrl] = useState('');
const events = Object.keys(availableEvents).map(key => ({
value: key,
label: availableEvents[key].description,
}));
function onChange(checkedValues) {
setSelectedEvents(checkedValues);
}
function selectAll() {
setSelectedEvents(Object.keys(availableEvents));
}
function save() {
2021-02-15 11:36:06 +03:00
onOk(webhookUrl, selectedEvents);
// Reset the modal
setWebhookUrl('');
setSelectedEvents(null);
}
const okButtonProps = {
disabled: selectedEvents?.length === 0 || !isValidUrl(webhookUrl),
};
const checkboxes = events.map(singleEvent => (
<Col span={8} key={singleEvent.value}>
<Checkbox value={singleEvent.value}>{singleEvent.label}</Checkbox>
</Col>
));
return (
<Modal
title="Create New Webhook"
2021-02-15 11:36:06 +03:00
visible={visible}
onOk={save}
2021-02-15 11:36:06 +03:00
onCancel={onCancel}
okButtonProps={okButtonProps}
>
<div>
<Input
value={webhookUrl}
placeholder="https://myserver.com/webhook"
onChange={input => setWebhookUrl(input.currentTarget.value.trim())}
type="url"
pattern={DEFAULT_TEXTFIELD_URL_PATTERN}
/>
</div>
<p>Select the events that will be sent to this webhook.</p>
<Checkbox.Group style={{ width: '100%' }} value={selectedEvents} onChange={onChange}>
<Row>{checkboxes}</Row>
</Checkbox.Group>
2021-02-14 12:30:42 +03:00
<p>
<Button type="primary" onClick={selectAll}>
Select all
</Button>
</p>
</Modal>
);
reafctor: normalize component formatting (#2082) * 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>
2022-09-07 10:00:28 +03:00
};
2021-01-06 03:35:37 +03:00
reafctor: normalize component formatting (#2082) * 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>
2022-09-07 10:00:28 +03:00
const Webhooks = () => {
const [webhooks, setWebhooks] = useState([]);
const [isModalVisible, setIsModalVisible] = useState(false);
2021-02-15 11:36:06 +03:00
function handleError(error) {
console.error('error', error);
}
async function getWebhooks() {
try {
const result = await fetchData(WEBHOOKS);
setWebhooks(result);
} catch (error) {
handleError(error);
2021-01-06 03:35:37 +03:00
}
2021-02-15 11:36:06 +03:00
}
useEffect(() => {
getWebhooks();
}, []);
async function handleDelete(id) {
try {
2021-02-15 11:36:06 +03:00
await fetchData(DELETE_WEBHOOK, { method: 'POST', data: { id } });
getWebhooks();
} catch (error) {
handleError(error);
2021-01-06 03:35:37 +03:00
}
}
async function handleSave(url: string, events: string[]) {
try {
const newHook = await fetchData(CREATE_WEBHOOK, {
method: 'POST',
2021-02-15 11:36:06 +03:00
data: { url, events },
});
setWebhooks(webhooks.concat(newHook));
} catch (error) {
handleError(error);
2021-01-06 03:35:37 +03:00
}
}
const showCreateModal = () => {
setIsModalVisible(true);
};
const handleModalSaveButton = (url, events) => {
setIsModalVisible(false);
handleSave(url, events);
};
const handleModalCancelButton = () => {
setIsModalVisible(false);
};
const columns = [
{
title: '',
key: 'delete',
render: (text, record) => (
<Space size="middle">
<Button onClick={() => handleDelete(record.id)} icon={<DeleteOutlined />} />
</Space>
),
},
{
title: 'URL',
dataIndex: 'url',
key: 'url',
},
{
title: 'Events',
dataIndex: 'events',
key: 'events',
2021-07-20 08:02:02 +03:00
render: events => (
<>
{
// eslint-disable-next-line arrow-body-style
events.map(event => {
return convertEventStringToTag(event);
})
}
</>
),
},
];
return (
<div>
<Title>Webhooks</Title>
<Paragraph>
2021-02-10 11:27:02 +03:00
A webhook is a callback made to an external API in response to an event that takes place
within Owncast. This can be used to build chat bots or sending automatic notifications that
2021-02-15 11:36:06 +03:00
you&apos;ve started streaming.
</Paragraph>
<Paragraph>
Read more about how to use webhooks, with examples, at{' '}
<a
href="https://owncast.online/docs/integrations/?source=admin"
target="_blank"
rel="noopener noreferrer"
>
our documentation
</a>
.
</Paragraph>
<Table
rowKey={record => record.id}
columns={columns}
dataSource={webhooks}
pagination={false}
/>
<br />
<Button type="primary" onClick={showCreateModal}>
Create Webhook
</Button>
<NewWebhookModal
visible={isModalVisible}
onOk={handleModalSaveButton}
onCancel={handleModalCancelButton}
/>
</div>
);
reafctor: normalize component formatting (#2082) * 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>
2022-09-07 10:00:28 +03:00
};
export default Webhooks;