owncast/web/components/ui/OfflineEmbed/OfflineEmbed.tsx
Gabe Kangas 5ce78fbad4
New offline embed (#3599)
* WIP

* feat(web): add new offline embed view. First step of #2917

* feat(web): support remote fediverse follow flow from embed

* feat(chore): add back offline video embed browser test
2024-02-25 12:52:32 -08:00

148 lines
4.3 KiB
TypeScript

import { FC, useEffect, useState } from 'react';
import classNames from 'classnames';
import Head from 'next/head';
import { Button, Input, Space, Spin, Alert, Typography } from 'antd';
import styles from './OfflineEmbed.module.scss';
import { isValidFediverseAccount } from '../../../utils/validators';
const { Title } = Typography;
const ENDPOINT = '/api/remotefollow';
export type OfflineEmbedProps = {
streamName: string;
subtitle?: string;
image: string;
supportsFollows: boolean;
};
enum EmbedMode {
CannotFollow = 1,
CanFollow,
FollowPrompt,
InProgress,
}
export const OfflineEmbed: FC<OfflineEmbedProps> = ({
streamName,
subtitle,
image,
supportsFollows,
}) => {
const [currentMode, setCurrentMode] = useState(EmbedMode.CanFollow);
const [remoteAccount, setRemoteAccount] = useState(null);
const [valid, setValid] = useState(false);
const [loading, setLoading] = useState(false);
const [errorMessage, setErrorMessage] = useState(null);
useEffect(() => {
if (!supportsFollows) {
setCurrentMode(EmbedMode.CannotFollow);
}
}, [supportsFollows]);
const followButtonPressed = async () => {
setCurrentMode(EmbedMode.FollowPrompt);
};
const remoteFollowButtonPressed = async () => {
setLoading(true);
setCurrentMode(EmbedMode.CannotFollow);
try {
const sanitizedAccount = remoteAccount.replace(/^@+/, '');
const request = { account: sanitizedAccount };
const rawResponse = await fetch(ENDPOINT, {
method: 'POST',
body: JSON.stringify(request),
});
const result = await rawResponse.json();
if (result.redirectUrl) {
window.open(result.redirectUrl, '_blank');
}
if (!result.success) {
setErrorMessage(result.message);
setLoading(false);
return;
}
if (!result.redirectUrl) {
setErrorMessage('Unable to follow.');
setLoading(false);
return;
}
} catch (e) {
setErrorMessage(e.message);
}
setLoading(false);
};
const handleAccountChange = a => {
setRemoteAccount(a);
if (isValidFediverseAccount(a)) {
setValid(true);
} else {
setValid(false);
}
};
return (
<div>
<Head>
<title>{streamName}</title>
</Head>
<div className={classNames(styles.offlineContainer)}>
<Spin spinning={loading}>
<div className={styles.content}>
<div className={styles.heading}>This stream is not currently live.</div>
<div className={styles.message}>{subtitle}</div>
<div className={styles.pageLogo} style={{ backgroundImage: `url(${image})` }} />
<div className={styles.pageName}>{streamName}</div>
{errorMessage && (
<Alert message="Follow Error" description={errorMessage} type="error" showIcon />
)}
{currentMode === EmbedMode.CanFollow && (
<Button className={styles.submitButton} type="primary" onClick={followButtonPressed}>
Follow Server
</Button>
)}
{currentMode === EmbedMode.InProgress && (
<Title level={4} className={styles.heading}>
Follow the instructions on your Fediverse server to complete the follow.
</Title>
)}
{currentMode === EmbedMode.FollowPrompt && (
<div>
<Input
value={remoteAccount}
size="large"
onChange={e => handleAccountChange(e.target.value)}
placeholder="Your fediverse account @account@server"
defaultValue={remoteAccount}
/>
<div className={styles.footer}>
You&apos;ll be redirected to your Fediverse server and asked to confirm the
action.
</div>
<Space className={styles.buttons}>
<Button
className={styles.submitButton}
disabled={!valid}
type="primary"
onClick={remoteFollowButtonPressed}
>
Submit and Follow
</Button>
</Space>
</div>
)}
</div>
</Spin>
</div>
</div>
);
};