mirror of
https://github.com/owncast/owncast.git
synced 2024-10-10 19:16:02 +00:00
Mange outbound notification settings (#458)
* ActivityPub admin pages for configuration * Fix dev build * Add support for requiring follow approval. Closes https://github.com/owncast/owncast/issues/1208 * Point at admin version of followers endpoint * Add setting for toggling displaying fediverse engagement in admin. https://github.com/owncast/owncast/issues/1404 * Add instance URL textfield to federation config and disable federation if it is empty * If instance URL is not https disable federation * Tweak federation toggle text. Make go live message optional * Add federation info modal. Closes https://github.com/owncast/owncast/issues/1544 * Add support for blocked federated domains. For https://github.com/owncast/owncast/issues/1209 * Simplify fediverse post input * Add placeholder Fediverse icon * Tweak federation logo in admin menu. Closes https://github.com/owncast/owncast/issues/1603 * Add global button for composing a fediverse post. Closes https://github.com/owncast/owncast/issues/1610 * Federation -> Social * Add page for listing federated actions. Closes https://github.com/owncast/owncast/issues/1573 * Auto-close social post modal after success * Make user modal action buttons look nicer * Center and reduce width and center count column. Closes https://github.com/owncast/owncast/issues/1580 * Update the followers table to be clearer * Fix exception thrown when passing undefined * Disable federation settings if feature is disabled * Update enable social modal. For https://github.com/owncast/owncast/issues/1594 * Fix type props * Quiet, linter * Move compose button to the left * Add tooltip for compose button * Add NSFW toggle to federation config. Closes https://github.com/owncast/owncast/issues/1628 * Add support for blocking/removing followers. For https://github.com/owncast/owncast/issues/1630 * Allow editing the server url field even when federation is disabled * Continue to update the copy around the social features * Use relative path to action images. Fixes https://github.com/owncast/owncast/issues/1646 * Link IRIs and make action verbse present tense * Update caniuse * Notifications configuration UI * Remove twilio add email notifications * WIP email support * Add support for managing twitter notifications * Mark fields as passwords * Continued WIP * Post merge cleanup * Cleanup * Remove email config. Handle disabled notifications state * Remove email related components * Remove email related settings properties
This commit is contained in:
parent
68fd329cc9
commit
8ad2240247
129
web/components/config/notification/browser.tsx
Normal file
129
web/components/config/notification/browser.tsx
Normal file
@ -0,0 +1,129 @@
|
||||
import { Button, Typography } from 'antd';
|
||||
import React, { useState, useContext, useEffect } from 'react';
|
||||
import { ServerStatusContext } from '../../../utils/server-status-context';
|
||||
import TextField, { TEXTFIELD_TYPE_TEXTAREA } from '../form-textfield';
|
||||
import {
|
||||
postConfigUpdateToAPI,
|
||||
RESET_TIMEOUT,
|
||||
BROWSER_PUSH_CONFIG_FIELDS,
|
||||
} from '../../../utils/config-constants';
|
||||
import ToggleSwitch from '../form-toggleswitch';
|
||||
import {
|
||||
createInputStatus,
|
||||
StatusState,
|
||||
STATUS_ERROR,
|
||||
STATUS_SUCCESS,
|
||||
} from '../../../utils/input-statuses';
|
||||
import { UpdateArgs } from '../../../types/config-section';
|
||||
import FormStatusIndicator from '../form-status-indicator';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
export default function ConfigNotify() {
|
||||
const serverStatusData = useContext(ServerStatusContext);
|
||||
const { serverConfig, setFieldInConfigState } = serverStatusData || {};
|
||||
const { notifications } = serverConfig || {};
|
||||
const { browser } = notifications || {};
|
||||
|
||||
const { enabled, goLiveMessage } = browser || {};
|
||||
|
||||
const [formDataValues, setFormDataValues] = useState<any>({});
|
||||
const [submitStatus, setSubmitStatus] = useState<StatusState>(null);
|
||||
|
||||
const [enableSaveButton, setEnableSaveButton] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
setFormDataValues({
|
||||
enabled,
|
||||
goLiveMessage,
|
||||
});
|
||||
}, [notifications, browser]);
|
||||
|
||||
const canSave = (): boolean => true;
|
||||
|
||||
// update individual values in state
|
||||
const handleFieldChange = ({ fieldName, value }: UpdateArgs) => {
|
||||
console.log(fieldName, value);
|
||||
setFormDataValues({
|
||||
...formDataValues,
|
||||
[fieldName]: value,
|
||||
});
|
||||
|
||||
setEnableSaveButton(canSave());
|
||||
};
|
||||
|
||||
// toggle switch.
|
||||
const handleSwitchChange = (switchEnabled: boolean) => {
|
||||
// setShouldDisplayForm(storageEnabled);
|
||||
handleFieldChange({ fieldName: 'enabled', value: switchEnabled });
|
||||
};
|
||||
|
||||
let resetTimer = null;
|
||||
const resetStates = () => {
|
||||
setSubmitStatus(null);
|
||||
resetTimer = null;
|
||||
clearTimeout(resetTimer);
|
||||
};
|
||||
|
||||
const save = async () => {
|
||||
const postValue = formDataValues;
|
||||
|
||||
await postConfigUpdateToAPI({
|
||||
apiPath: '/notifications/browser',
|
||||
data: { value: postValue },
|
||||
onSuccess: () => {
|
||||
setFieldInConfigState({
|
||||
fieldName: 'browser',
|
||||
value: postValue,
|
||||
path: 'notifications',
|
||||
});
|
||||
setSubmitStatus(createInputStatus(STATUS_SUCCESS, 'Updated.'));
|
||||
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
||||
},
|
||||
onError: (message: string) => {
|
||||
setSubmitStatus(createInputStatus(STATUS_ERROR, message));
|
||||
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>Browser Alerts</Title>
|
||||
<p className="description reduced-margins">
|
||||
Viewers can opt into being notified when you go live with their browser.
|
||||
</p>
|
||||
<p className="description reduced-margins">Not all browsers support this.</p>
|
||||
<ToggleSwitch
|
||||
apiPath=""
|
||||
fieldName="enabled"
|
||||
label="Enable browser notifications"
|
||||
onChange={handleSwitchChange}
|
||||
checked={formDataValues.enabled}
|
||||
/>
|
||||
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
|
||||
<TextField
|
||||
{...BROWSER_PUSH_CONFIG_FIELDS.goLiveMessage}
|
||||
required
|
||||
type={TEXTFIELD_TYPE_TEXTAREA}
|
||||
value={formDataValues.goLiveMessage}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
type="primary"
|
||||
style={{
|
||||
display: enableSaveButton ? 'inline-block' : 'none',
|
||||
position: 'relative',
|
||||
marginLeft: 'auto',
|
||||
right: '0',
|
||||
marginTop: '20px',
|
||||
}}
|
||||
onClick={save}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<FormStatusIndicator status={submitStatus} />
|
||||
</>
|
||||
);
|
||||
}
|
153
web/components/config/notification/discord.tsx
Normal file
153
web/components/config/notification/discord.tsx
Normal file
@ -0,0 +1,153 @@
|
||||
import { Button, Typography } from 'antd';
|
||||
import React, { useState, useContext, useEffect } from 'react';
|
||||
import { ServerStatusContext } from '../../../utils/server-status-context';
|
||||
import TextField from '../form-textfield';
|
||||
import FormStatusIndicator from '../form-status-indicator';
|
||||
import {
|
||||
postConfigUpdateToAPI,
|
||||
RESET_TIMEOUT,
|
||||
DISCORD_CONFIG_FIELDS,
|
||||
} from '../../../utils/config-constants';
|
||||
import ToggleSwitch from '../form-toggleswitch';
|
||||
import {
|
||||
createInputStatus,
|
||||
StatusState,
|
||||
STATUS_ERROR,
|
||||
STATUS_SUCCESS,
|
||||
} from '../../../utils/input-statuses';
|
||||
import { UpdateArgs } from '../../../types/config-section';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
export default function ConfigNotify() {
|
||||
const serverStatusData = useContext(ServerStatusContext);
|
||||
const { serverConfig, setFieldInConfigState } = serverStatusData || {};
|
||||
const { notifications } = serverConfig || {};
|
||||
const { discord } = notifications || {};
|
||||
|
||||
const { enabled, webhook, goLiveMessage } = discord || {};
|
||||
|
||||
const [formDataValues, setFormDataValues] = useState<any>({});
|
||||
const [submitStatus, setSubmitStatus] = useState<StatusState>(null);
|
||||
|
||||
const [enableSaveButton, setEnableSaveButton] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
setFormDataValues({
|
||||
enabled,
|
||||
webhook,
|
||||
goLiveMessage,
|
||||
});
|
||||
}, [notifications, discord]);
|
||||
|
||||
const canSave = (): boolean => {
|
||||
if (webhook === '' || goLiveMessage === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// update individual values in state
|
||||
const handleFieldChange = ({ fieldName, value }: UpdateArgs) => {
|
||||
setFormDataValues({
|
||||
...formDataValues,
|
||||
[fieldName]: value,
|
||||
});
|
||||
|
||||
setEnableSaveButton(canSave());
|
||||
};
|
||||
|
||||
let resetTimer = null;
|
||||
const resetStates = () => {
|
||||
setSubmitStatus(null);
|
||||
resetTimer = null;
|
||||
clearTimeout(resetTimer);
|
||||
};
|
||||
|
||||
const save = async () => {
|
||||
const postValue = formDataValues;
|
||||
|
||||
await postConfigUpdateToAPI({
|
||||
apiPath: '/notifications/discord',
|
||||
data: { value: postValue },
|
||||
onSuccess: () => {
|
||||
setFieldInConfigState({
|
||||
fieldName: 'discord',
|
||||
value: postValue,
|
||||
path: 'notifications',
|
||||
});
|
||||
setSubmitStatus(createInputStatus(STATUS_SUCCESS, 'Updated.'));
|
||||
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
||||
},
|
||||
onError: (message: string) => {
|
||||
setSubmitStatus(createInputStatus(STATUS_ERROR, message));
|
||||
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// toggle switch.
|
||||
const handleSwitchChange = (switchEnabled: boolean) => {
|
||||
// setShouldDisplayForm(storageEnabled);
|
||||
handleFieldChange({ fieldName: 'enabled', value: switchEnabled });
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>Discord</Title>
|
||||
<p className="description reduced-margins">
|
||||
Let your Discord channel know each time you go live.
|
||||
</p>
|
||||
<p className="description reduced-margins">
|
||||
<a
|
||||
href="https://support.discord.com/hc/en-us/articles/228383668"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Create a webhook
|
||||
</a>{' '}
|
||||
under <i>Edit Channel / Integrations</i> on your Discord channel and provide it below.
|
||||
</p>
|
||||
|
||||
<ToggleSwitch
|
||||
apiPath=""
|
||||
fieldName="discordEnabled"
|
||||
label="Enable Discord"
|
||||
checked={formDataValues.enabled}
|
||||
onChange={handleSwitchChange}
|
||||
/>
|
||||
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
|
||||
<TextField
|
||||
{...DISCORD_CONFIG_FIELDS.webhookUrl}
|
||||
required
|
||||
value={formDataValues.webhook}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
|
||||
<TextField
|
||||
{...DISCORD_CONFIG_FIELDS.goLiveMessage}
|
||||
required
|
||||
value={formDataValues.goLiveMessage}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={save}
|
||||
style={{
|
||||
display: enableSaveButton ? 'inline-block' : 'none',
|
||||
position: 'relative',
|
||||
marginLeft: 'auto',
|
||||
right: '0',
|
||||
marginTop: '20px',
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<FormStatusIndicator status={submitStatus} />
|
||||
</>
|
||||
);
|
||||
}
|
51
web/components/config/notification/federation.tsx
Normal file
51
web/components/config/notification/federation.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import { Button, Typography } from 'antd';
|
||||
import React, { useState, useContext, useEffect } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { ServerStatusContext } from '../../../utils/server-status-context';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
export default function ConfigNotify() {
|
||||
const serverStatusData = useContext(ServerStatusContext);
|
||||
const { serverConfig } = serverStatusData || {};
|
||||
const { federation } = serverConfig || {};
|
||||
|
||||
const { enabled } = federation || {};
|
||||
const [formDataValues, setFormDataValues] = useState<any>({});
|
||||
|
||||
useEffect(() => {
|
||||
setFormDataValues({
|
||||
enabled,
|
||||
});
|
||||
}, [enabled]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>Fediverse Social</Title>
|
||||
<p className="description">
|
||||
Enabling the Fediverse social features will not just alert people to when you go live, but
|
||||
also enable other functionality.
|
||||
</p>
|
||||
<p>
|
||||
Fediverse social features:{' '}
|
||||
<span style={{ color: federation.enabled ? 'green' : 'red' }}>
|
||||
{formDataValues.enabled ? 'Enabled' : 'Disabled'}
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<Link passHref href="/config-federation">
|
||||
<Button
|
||||
type="primary"
|
||||
style={{
|
||||
position: 'relative',
|
||||
marginLeft: 'auto',
|
||||
right: '0',
|
||||
marginTop: '20px',
|
||||
}}
|
||||
>
|
||||
Configure
|
||||
</Button>
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
}
|
198
web/components/config/notification/twitter.tsx
Normal file
198
web/components/config/notification/twitter.tsx
Normal file
@ -0,0 +1,198 @@
|
||||
import { Button, Typography } from 'antd';
|
||||
import React, { useState, useContext, useEffect } from 'react';
|
||||
import { ServerStatusContext } from '../../../utils/server-status-context';
|
||||
import TextField, { TEXTFIELD_TYPE_PASSWORD } from '../form-textfield';
|
||||
import FormStatusIndicator from '../form-status-indicator';
|
||||
import {
|
||||
postConfigUpdateToAPI,
|
||||
RESET_TIMEOUT,
|
||||
TWITTER_CONFIG_FIELDS,
|
||||
} from '../../../utils/config-constants';
|
||||
import ToggleSwitch from '../form-toggleswitch';
|
||||
import {
|
||||
createInputStatus,
|
||||
StatusState,
|
||||
STATUS_ERROR,
|
||||
STATUS_SUCCESS,
|
||||
} from '../../../utils/input-statuses';
|
||||
import { UpdateArgs } from '../../../types/config-section';
|
||||
import { TEXTFIELD_TYPE_TEXT } from '../form-textfield-with-submit';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
export default function ConfigNotify() {
|
||||
const serverStatusData = useContext(ServerStatusContext);
|
||||
const { serverConfig, setFieldInConfigState } = serverStatusData || {};
|
||||
const { notifications } = serverConfig || {};
|
||||
const { twitter } = notifications || {};
|
||||
|
||||
const { enabled, apiKey, apiSecret, accessToken, accessTokenSecret, bearerToken, goLiveMessage } =
|
||||
twitter || {};
|
||||
|
||||
const [formDataValues, setFormDataValues] = useState<any>({});
|
||||
const [submitStatus, setSubmitStatus] = useState<StatusState>(null);
|
||||
|
||||
const [enableSaveButton, setEnableSaveButton] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
setFormDataValues({
|
||||
enabled,
|
||||
apiKey,
|
||||
apiSecret,
|
||||
accessToken,
|
||||
accessTokenSecret,
|
||||
bearerToken,
|
||||
goLiveMessage,
|
||||
});
|
||||
}, [twitter]);
|
||||
|
||||
const canSave = (): boolean => true;
|
||||
|
||||
// update individual values in state
|
||||
const handleFieldChange = ({ fieldName, value }: UpdateArgs) => {
|
||||
setFormDataValues({
|
||||
...formDataValues,
|
||||
[fieldName]: value,
|
||||
});
|
||||
|
||||
setEnableSaveButton(canSave());
|
||||
};
|
||||
|
||||
// toggle switch.
|
||||
const handleSwitchChange = (switchEnabled: boolean) => {
|
||||
handleFieldChange({ fieldName: 'enabled', value: switchEnabled });
|
||||
};
|
||||
|
||||
let resetTimer = null;
|
||||
const resetStates = () => {
|
||||
setSubmitStatus(null);
|
||||
resetTimer = null;
|
||||
clearTimeout(resetTimer);
|
||||
};
|
||||
|
||||
const save = async () => {
|
||||
const postValue = formDataValues;
|
||||
|
||||
await postConfigUpdateToAPI({
|
||||
apiPath: '/notifications/twitter',
|
||||
data: { value: postValue },
|
||||
onSuccess: () => {
|
||||
setFieldInConfigState({
|
||||
fieldName: 'twitter',
|
||||
value: postValue,
|
||||
path: 'notifications',
|
||||
});
|
||||
setSubmitStatus(createInputStatus(STATUS_SUCCESS, 'Updated.'));
|
||||
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
||||
},
|
||||
onError: (message: string) => {
|
||||
setSubmitStatus(createInputStatus(STATUS_ERROR, message));
|
||||
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>Twitter</Title>
|
||||
<p className="description reduced-margins">
|
||||
Let your Twitter followers know each time you go live.
|
||||
</p>
|
||||
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
|
||||
<p className="description reduced-margins">
|
||||
<a
|
||||
href="https://developer.twitter.com/en/portal/dashboard"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Read how to configure your Twitter account
|
||||
</a>{' '}
|
||||
to support posting from Owncast.
|
||||
</p>
|
||||
<p className="description reduced-margins">
|
||||
<a
|
||||
href="https://developer.twitter.com/en/portal/dashboard"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
And then get your Twitter developer credentials
|
||||
</a>{' '}
|
||||
to fill in below.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ToggleSwitch
|
||||
apiPath=""
|
||||
fieldName="enabled"
|
||||
label="Enable Twitter"
|
||||
onChange={handleSwitchChange}
|
||||
checked={formDataValues.enabled}
|
||||
/>
|
||||
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
|
||||
<TextField
|
||||
{...TWITTER_CONFIG_FIELDS.apiKey}
|
||||
required
|
||||
value={formDataValues.apiKey}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
|
||||
<TextField
|
||||
{...TWITTER_CONFIG_FIELDS.apiSecret}
|
||||
type={TEXTFIELD_TYPE_PASSWORD}
|
||||
required
|
||||
value={formDataValues.apiSecret}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
|
||||
<TextField
|
||||
{...TWITTER_CONFIG_FIELDS.accessToken}
|
||||
required
|
||||
value={formDataValues.accessToken}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
|
||||
<TextField
|
||||
{...TWITTER_CONFIG_FIELDS.accessTokenSecret}
|
||||
type={TEXTFIELD_TYPE_PASSWORD}
|
||||
required
|
||||
value={formDataValues.accessTokenSecret}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
|
||||
<TextField
|
||||
{...TWITTER_CONFIG_FIELDS.bearerToken}
|
||||
required
|
||||
value={formDataValues.bearerToken}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: formDataValues.enabled ? 'block' : 'none' }}>
|
||||
<TextField
|
||||
{...TWITTER_CONFIG_FIELDS.goLiveMessage}
|
||||
type={TEXTFIELD_TYPE_TEXT}
|
||||
required
|
||||
value={formDataValues.goLiveMessage}
|
||||
onChange={handleFieldChange}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={save}
|
||||
style={{
|
||||
display: enableSaveButton ? 'inline-block' : 'none',
|
||||
position: 'relative',
|
||||
marginLeft: 'auto',
|
||||
right: '0',
|
||||
marginTop: '20px',
|
||||
}}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<FormStatusIndicator status={submitStatus} />
|
||||
</>
|
||||
);
|
||||
}
|
@ -196,6 +196,9 @@ export default function MainLayout(props) {
|
||||
<Menu.Item key="config-federation">
|
||||
<Link href="/config-federation">Social</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="config-notify">
|
||||
<Link href="/config-notify">Notifications</Link>
|
||||
</Menu.Item>
|
||||
|
||||
<Menu.Item key="config-storage">
|
||||
<Link href="/config-storage">S3 Storage</Link>
|
||||
|
135
web/pages/config-notify.tsx
Normal file
135
web/pages/config-notify.tsx
Normal file
@ -0,0 +1,135 @@
|
||||
import { Alert, Button, Col, Row, Typography } from 'antd';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
|
||||
import Discord from '../components/config/notification/discord';
|
||||
import Browser from '../components/config/notification/browser';
|
||||
import Twitter from '../components/config/notification/twitter';
|
||||
import Federation from '../components/config/notification/federation';
|
||||
import TextFieldWithSubmit, {
|
||||
TEXTFIELD_TYPE_URL,
|
||||
} from '../components/config/form-textfield-with-submit';
|
||||
import { TEXTFIELD_PROPS_FEDERATION_INSTANCE_URL } from '../utils/config-constants';
|
||||
import { ServerStatusContext } from '../utils/server-status-context';
|
||||
import { UpdateArgs } from '../types/config-section';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
export default function ConfigNotify() {
|
||||
const [formDataValues, setFormDataValues] = useState(null);
|
||||
const serverStatusData = useContext(ServerStatusContext);
|
||||
const { serverConfig } = serverStatusData || {};
|
||||
const { yp } = serverConfig;
|
||||
const { instanceUrl } = yp;
|
||||
|
||||
useEffect(() => {
|
||||
setFormDataValues({
|
||||
instanceUrl,
|
||||
});
|
||||
}, [yp]);
|
||||
|
||||
const handleSubmitInstanceUrl = () => {
|
||||
setFormDataValues({
|
||||
...formDataValues,
|
||||
enabled: false,
|
||||
});
|
||||
};
|
||||
|
||||
const handleFieldChange = ({ fieldName, value }: UpdateArgs) => {
|
||||
setFormDataValues({
|
||||
...formDataValues,
|
||||
[fieldName]: value,
|
||||
});
|
||||
};
|
||||
|
||||
const enabled = instanceUrl !== '';
|
||||
console.log(enabled);
|
||||
const configurationWarning = !enabled && (
|
||||
<>
|
||||
<Alert
|
||||
message="You must set your server URL before you can enable this feature."
|
||||
type="warning"
|
||||
showIcon
|
||||
/>
|
||||
<br />
|
||||
<TextFieldWithSubmit
|
||||
fieldName="instanceUrl"
|
||||
{...TEXTFIELD_PROPS_FEDERATION_INSTANCE_URL}
|
||||
value={formDataValues?.instanceUrl || ''}
|
||||
initialValue={yp.instanceUrl}
|
||||
type={TEXTFIELD_TYPE_URL}
|
||||
onChange={handleFieldChange}
|
||||
onSubmit={handleSubmitInstanceUrl}
|
||||
required
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>Notifications</Title>
|
||||
<p className="description">
|
||||
Let your viewers know when you go live by supporting some of the following notification
|
||||
channels.
|
||||
</p>
|
||||
|
||||
{configurationWarning}
|
||||
|
||||
<Row>
|
||||
<Col
|
||||
span={10}
|
||||
className={`form-module ${enabled ? '' : 'disabled'}`}
|
||||
style={{ margin: '5px', display: 'flex', flexDirection: 'column' }}
|
||||
>
|
||||
<Browser />
|
||||
</Col>
|
||||
<Col
|
||||
span={10}
|
||||
className={`form-module ${enabled ? '' : 'disabled'}`}
|
||||
style={{ margin: '5px', display: 'flex', flexDirection: 'column' }}
|
||||
>
|
||||
<Twitter />
|
||||
</Col>
|
||||
|
||||
<Col
|
||||
span={10}
|
||||
className={`form-module ${enabled ? '' : 'disabled'}`}
|
||||
style={{ margin: '5px', display: 'flex', flexDirection: 'column' }}
|
||||
>
|
||||
<Discord />
|
||||
</Col>
|
||||
|
||||
<Col
|
||||
span={10}
|
||||
className={`form-module ${enabled ? '' : 'disabled'}`}
|
||||
style={{ margin: '5px', display: 'flex', flexDirection: 'column' }}
|
||||
>
|
||||
<Federation />
|
||||
</Col>
|
||||
|
||||
<Col
|
||||
span={10}
|
||||
className={`form-module ${enabled ? '' : 'disabled'}`}
|
||||
style={{ margin: '5px', display: 'flex', flexDirection: 'column' }}
|
||||
>
|
||||
<Title>Custom</Title>
|
||||
<p className="description">Build your own notifications by using custom webhooks.</p>
|
||||
|
||||
<Link passHref href="/webhooks">
|
||||
<Button
|
||||
type="primary"
|
||||
style={{
|
||||
position: 'relative',
|
||||
marginLeft: 'auto',
|
||||
right: '0',
|
||||
marginTop: '20px',
|
||||
}}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
</Link>
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
}
|
@ -36,6 +36,12 @@ p.description,
|
||||
margin: 1em 0;
|
||||
color: var(--white-75);
|
||||
}
|
||||
|
||||
.description.reduced-margins {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
pre {
|
||||
display: block;
|
||||
padding: 1rem;
|
||||
@ -86,6 +92,11 @@ strong {
|
||||
}
|
||||
}
|
||||
|
||||
.form-module.disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
@ -98,6 +98,33 @@ export interface Federation {
|
||||
blockedDomains: string[];
|
||||
}
|
||||
|
||||
export interface BrowserNotification {
|
||||
enabled: boolean;
|
||||
goLiveMessage: string;
|
||||
}
|
||||
|
||||
export interface DiscordNotification {
|
||||
enabled: boolean;
|
||||
webhook: string;
|
||||
goLiveMessage: string;
|
||||
}
|
||||
|
||||
export interface TwitterNotification {
|
||||
enabled: boolean;
|
||||
apiKey: string;
|
||||
apiSecret: string;
|
||||
accessToken: string;
|
||||
accessTokenSecret: string;
|
||||
bearerToken: string;
|
||||
goLiveMessage: string;
|
||||
}
|
||||
|
||||
export interface NotificationsConfig {
|
||||
browser: BrowserNotification;
|
||||
discord: DiscordNotification;
|
||||
twitter: TwitterNotification;
|
||||
}
|
||||
|
||||
export interface ConfigDetails {
|
||||
externalActions: ExternalAction[];
|
||||
ffmpegPath: string;
|
||||
@ -114,7 +141,8 @@ export interface ConfigDetails {
|
||||
forbiddenUsernames: string[];
|
||||
suggestedUsernames: string[];
|
||||
chatDisabled: boolean;
|
||||
federation: Federation;
|
||||
notifications: NotificationsConfig;
|
||||
chatJoinMessagesEnabled: boolean;
|
||||
chatEstablishedUserMode: boolean;
|
||||
federation: Federation;
|
||||
}
|
||||
|
@ -489,3 +489,78 @@ export const S3_TEXT_FIELDS_INFO = {
|
||||
tip: "If your S3 provider doesn't support virtual-hosted-style URLs set this to ON (i.e. Oracle Cloud Object Storage)",
|
||||
},
|
||||
};
|
||||
|
||||
export const DISCORD_CONFIG_FIELDS = {
|
||||
webhookUrl: {
|
||||
fieldName: 'webhook',
|
||||
label: 'Webhook URL',
|
||||
maxLength: 255,
|
||||
placeholder: 'https://discord.com/api/webhooks/837/jf38-6iNEv',
|
||||
tip: 'The webhook assigned to your channel.',
|
||||
type: TEXTFIELD_TYPE_URL,
|
||||
pattern: DEFAULT_TEXTFIELD_URL_PATTERN,
|
||||
useTrim: true,
|
||||
},
|
||||
goLiveMessage: {
|
||||
fieldName: 'goLiveMessage',
|
||||
label: 'Go Live Text',
|
||||
maxLength: 300,
|
||||
tip: 'The text to send when you go live.',
|
||||
placeholder: `I've gone live! Come watch!`,
|
||||
},
|
||||
};
|
||||
|
||||
export const BROWSER_PUSH_CONFIG_FIELDS = {
|
||||
goLiveMessage: {
|
||||
fieldName: 'goLiveMessage',
|
||||
label: 'Go Live Text',
|
||||
maxLength: 200,
|
||||
tip: 'The text to send when you go live.',
|
||||
placeholder: `I've gone live! Come watch!`,
|
||||
},
|
||||
};
|
||||
|
||||
export const TWITTER_CONFIG_FIELDS = {
|
||||
apiKey: {
|
||||
fieldName: 'apiKey',
|
||||
label: 'API Key',
|
||||
maxLength: 200,
|
||||
tip: '',
|
||||
placeholder: `gaUQhRC2lqfrEFfElBXJgOctU`,
|
||||
},
|
||||
apiSecret: {
|
||||
fieldName: 'apiSecret',
|
||||
label: 'API Secret',
|
||||
maxLength: 200,
|
||||
tip: '',
|
||||
placeholder: `IIz4jFZMWbUKdFOEGUprFjRwIslG56d1SPQlolJYjXwJ2y2qKS`,
|
||||
},
|
||||
accessToken: {
|
||||
fieldName: 'accessToken',
|
||||
label: 'Access Token',
|
||||
maxLength: 200,
|
||||
tip: '',
|
||||
placeholder: `952540400-EEiwe9fkuSvWjnNC82YFa9kgpqbyAP3J7FjE2dkka`,
|
||||
},
|
||||
accessTokenSecret: {
|
||||
fieldName: 'accessTokenSecret',
|
||||
label: 'Access Token Secret',
|
||||
maxLength: 200,
|
||||
tip: '',
|
||||
placeholder: `xO0AZWNGfZxpNsYPg3zNEKhAsPPGvNZFlzQArA2khI9Kg`,
|
||||
},
|
||||
bearerToken: {
|
||||
fieldName: 'bearerToken',
|
||||
label: 'Bearer Token',
|
||||
maxLength: 200,
|
||||
tip: '',
|
||||
placeholder: `AAAAAAAAAAAAAAFqpXwEAAnnepHkjA8XD5ftx5jUadYIRtPtaq7AAAAwpXPpDWKDcdhiWr0tVDjsgW%2B4awGOM9VQ%3XPoMFuWcHsE42TK`,
|
||||
},
|
||||
goLiveMessage: {
|
||||
fieldName: 'goLiveMessage',
|
||||
label: 'Go Live Text',
|
||||
maxLength: 200,
|
||||
tip: 'The text to send when you go live.',
|
||||
placeholder: `I've gone live! Come watch!`,
|
||||
},
|
||||
};
|
||||
|
@ -54,6 +54,19 @@ export const initialServerConfigState: ConfigDetails = {
|
||||
showEngagement: true,
|
||||
blockedDomains: [],
|
||||
},
|
||||
notifications: {
|
||||
browser: { enabled: false, goLiveMessage: '' },
|
||||
discord: { enabled: false, webhook: '', goLiveMessage: '' },
|
||||
twitter: {
|
||||
enabled: false,
|
||||
goLiveMessage: '',
|
||||
apiKey: '',
|
||||
apiSecret: '',
|
||||
accessToken: '',
|
||||
accessTokenSecret: '',
|
||||
bearerToken: '',
|
||||
},
|
||||
},
|
||||
externalActions: [],
|
||||
supportedCodecs: [],
|
||||
videoCodec: '',
|
||||
|
Loading…
x
Reference in New Issue
Block a user