Browse Source
* 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 propertiespull/1886/head
10 changed files with 797 additions and 1 deletions
@ -0,0 +1,129 @@
@@ -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} /> |
||||
</> |
||||
); |
||||
} |
||||
@ -0,0 +1,153 @@
@@ -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} /> |
||||
</> |
||||
); |
||||
} |
||||
@ -0,0 +1,51 @@
@@ -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> |
||||
</> |
||||
); |
||||
} |
||||
@ -0,0 +1,198 @@
@@ -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} /> |
||||
</> |
||||
); |
||||
} |
||||
@ -0,0 +1,135 @@
@@ -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> |
||||
</> |
||||
); |
||||
} |
||||
Loading…
Reference in new issue