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:
James Young
2022-09-03 20:38:52 +02:00
committed by GitHub
parent 9f550a87d2
commit 5ebbbb8bf2
82 changed files with 120 additions and 121 deletions

View 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;
}
}
}

View 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({});

View 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>
);
}