mirror of
https://github.com/owncast/owncast.git
synced 2025-11-03 13:01:46 +08:00
refactor(stories): co-locate stories with components (#2078)
* refactor: move ActionButton component * refactor: move BanUserButton component * refactor: move ChatActionMessage component * refactor: move ChatContainer component * refactor: move AuthModal component * refactor: move BrowserNotifyModal component * refactor: move ChatUserMessage component * refactor: move ChatJoinMessage component * refactor: move ChatTextField component * refactor: move ChatUserBadge component * refactor: move FollowerCollection and SingleFollower components * fix: bad import path * refactor: move FollowModal component * refactor: move Modal component * refactor: move ContentHeader component * refactor: move ChatSystemMessage component * refactor: move Header component * refactor: move Footer component * refactor: move StatusBar component * refactor: move OfflineBanner component * refactor: move OwncastPlayer component * refactor: move IndieAuthModal component * refactor: move SocialLinks component * refactor: move VideoPoster component * refactor: move FollowModal component * refactor: move FediAuthModal.tsx component * refactor: move UserDropdown component * refactor: move ChatSocialMessage component * refactor: move Logo component * refactor: move NotifyReminderPopup component * refactor: move NameChangeModal component * refactor: move FatalErrorStateModal component * refactor: move ChatModeratorNotification component * refactor: move ChatModerationActionMenu and ChatModerationDetailsModal components * refactor: move CustomPageContent component * refactor: move storybook Introduction file * refactor: update storybook story import path * refactor: move storybook preview styles * refactor: move storybook doc pages * refactor: move Color and ImageAsset components * fix: bad import path * fix: bad import path in story file
This commit is contained in:
44
web/components/modals/FollowModal/FollowModal.module.scss
Normal file
44
web/components/modals/FollowModal/FollowModal.module.scss
Normal file
@ -0,0 +1,44 @@
|
||||
.header {
|
||||
font-family: var(--theme-text-display-font-family);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.instructions {
|
||||
font-family: var(--theme-text-display-font-family);
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.footer {
|
||||
font-size: 0.5rem;
|
||||
}
|
||||
|
||||
.account {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 0.8rem;
|
||||
|
||||
.logo {
|
||||
border-radius: 50%;
|
||||
width: 4em;
|
||||
height: 4em;
|
||||
}
|
||||
|
||||
.username {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 10px;
|
||||
margin-top: 5px;
|
||||
.name {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
38
web/components/modals/FollowModal/FollowModal.stories.tsx
Normal file
38
web/components/modals/FollowModal/FollowModal.stories.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
import FollowModal from './FollowModal';
|
||||
import FollowModalMock from '../../../stories/assets/mocks/follow-modal.png';
|
||||
|
||||
const Example = () => (
|
||||
<div>
|
||||
<FollowModal handleClose={null} account="@fake@server.name" name="Fake Owncast Server" />
|
||||
</div>
|
||||
);
|
||||
|
||||
export default {
|
||||
title: 'owncast/Modals/Follow',
|
||||
component: FollowModal,
|
||||
parameters: {
|
||||
design: {
|
||||
type: 'image',
|
||||
url: FollowModalMock,
|
||||
scale: 0.5,
|
||||
},
|
||||
docs: {
|
||||
description: {
|
||||
component: `The Follow modal allows an end user to type in their Fediverse account information to follow this Owncast instance. It must:
|
||||
|
||||
- Validate the input to make sure it's a valid looking account.
|
||||
- Handle errors that come back from the server.
|
||||
- Perform the redirect to the remote server when the backend response is received.
|
||||
`,
|
||||
},
|
||||
},
|
||||
},
|
||||
} as ComponentMeta<typeof FollowModal>;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const Template: ComponentStory<typeof FollowModal> = args => <Example />;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export const Basic = Template.bind({});
|
||||
124
web/components/modals/FollowModal/FollowModal.tsx
Normal file
124
web/components/modals/FollowModal/FollowModal.tsx
Normal file
@ -0,0 +1,124 @@
|
||||
/* eslint-disable react/no-unescaped-entities */
|
||||
import { Input, Button, Alert, Spin, Space } from 'antd';
|
||||
import { useState } from 'react';
|
||||
import s from './FollowModal.module.scss';
|
||||
|
||||
const ENDPOINT = '/api/remotefollow';
|
||||
|
||||
interface Props {
|
||||
handleClose: () => void;
|
||||
account: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
function validateAccount(a) {
|
||||
const sanitized = a.replace(/^@+/, '');
|
||||
const regex =
|
||||
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
return regex.test(String(sanitized).toLowerCase());
|
||||
}
|
||||
|
||||
export default function FollowModal(props: Props) {
|
||||
const { handleClose, account, name } = props;
|
||||
const [remoteAccount, setRemoteAccount] = useState(null);
|
||||
const [valid, setValid] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [errorMessage, setErrorMessage] = useState(null);
|
||||
|
||||
const handleAccountChange = a => {
|
||||
setRemoteAccount(a);
|
||||
if (validateAccount(a)) {
|
||||
setValid(true);
|
||||
} else {
|
||||
setValid(false);
|
||||
}
|
||||
};
|
||||
|
||||
const joinButtonPressed = () => {
|
||||
window.open('https://owncast.online/join-fediverse', '_blank');
|
||||
};
|
||||
|
||||
const remoteFollowButtonPressed = async () => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
|
||||
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');
|
||||
handleClose();
|
||||
}
|
||||
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);
|
||||
};
|
||||
|
||||
return (
|
||||
<Space direction="vertical">
|
||||
<div className={s.header}>
|
||||
By following this stream you'll get notified on the Fediverse when it goes live. Now is a
|
||||
great time to
|
||||
<a href="https://owncast.online/join-fediverse" target="_blank" rel="noreferrer">
|
||||
learn about the Fediverse
|
||||
</a>
|
||||
if it's new to you.
|
||||
</div>
|
||||
|
||||
<Spin spinning={loading}>
|
||||
{errorMessage && (
|
||||
<Alert message="Follow Error" description={errorMessage} type="error" showIcon />
|
||||
)}
|
||||
<div className={s.account}>
|
||||
<img src="/logo" alt="logo" className={s.logo} />
|
||||
<div className={s.username}>
|
||||
<div className={s.name}>{name}</div>
|
||||
<div>{account}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className={s.instructions}>Enter your username @server to follow</div>
|
||||
<Input
|
||||
value={account}
|
||||
size="large"
|
||||
onChange={e => handleAccountChange(e.target.value)}
|
||||
placeholder="Your fediverse account @account@server"
|
||||
defaultValue={account}
|
||||
/>
|
||||
<div className={s.footer}>
|
||||
You'll be redirected to your Fediverse server and asked to confirm the action.
|
||||
</div>
|
||||
</div>
|
||||
<Space className={s.buttons}>
|
||||
<Button disabled={!valid} type="primary" onClick={remoteFollowButtonPressed}>
|
||||
Follow
|
||||
</Button>
|
||||
<Button onClick={joinButtonPressed} type="primary">
|
||||
Join the Fediverse
|
||||
</Button>
|
||||
</Space>
|
||||
</Spin>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user