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:
@@ -0,0 +1,48 @@
|
||||
.pushPreview {
|
||||
border-style: dashed;
|
||||
border-width: 2px;
|
||||
width: 30vw;
|
||||
|
||||
.inner {
|
||||
margin: 10px;
|
||||
padding: 15px;
|
||||
background-color: white;
|
||||
box-shadow: 2px 6px 7px 0px #87898d;
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.permissionLine {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.buttonRow {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
|
||||
.disabled {
|
||||
cursor: not-allowed;
|
||||
outline-width: 1;
|
||||
outline-color: '#e2e8f0';
|
||||
outline-style: 'solid';
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.allow {
|
||||
background-color: var(--theme-primary-color);
|
||||
}
|
||||
|
||||
button {
|
||||
margin-left: 10px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
border-radius: 3px;
|
||||
border-style: solid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import BrowserNotifyModal from './BrowserNotifyModal';
|
||||
import BrowserNotifyModalMock from '../../../stories/assets/mocks/notify-modal.png';
|
||||
|
||||
const Example = () => (
|
||||
<div>
|
||||
<BrowserNotifyModal />
|
||||
</div>
|
||||
);
|
||||
|
||||
export default {
|
||||
title: 'owncast/Modals/Browser Notifications',
|
||||
component: BrowserNotifyModal,
|
||||
parameters: {
|
||||
design: {
|
||||
type: 'image',
|
||||
url: BrowserNotifyModalMock,
|
||||
scale: 0.5,
|
||||
},
|
||||
docs: {
|
||||
description: {
|
||||
component: `The notify modal allows an end user to get notified when the stream goes live via [Browser Push Notifications](https://developers.google.com/web/ilt/pwa/introduction-to-push-notifications) It must:
|
||||
|
||||
- Verify the browser supports notifications.
|
||||
- Handle errors that come back from the server.
|
||||
- Have an enabled and disabled state with accurate information about each.
|
||||
`,
|
||||
},
|
||||
},
|
||||
},
|
||||
} as ComponentMeta<typeof BrowserNotifyModal>;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const Template: ComponentStory<typeof BrowserNotifyModal> = args => (
|
||||
<RecoilRoot>
|
||||
<Example />
|
||||
</RecoilRoot>
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export const Basic = Template.bind({});
|
||||
123
web/components/modals/BrowserNotifyModal/BrowserNotifyModal.tsx
Normal file
123
web/components/modals/BrowserNotifyModal/BrowserNotifyModal.tsx
Normal file
@@ -0,0 +1,123 @@
|
||||
import { Row, Col, Spin, Typography, Button } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { accessTokenAtom, clientConfigStateAtom } from '../../stores/ClientConfigStore';
|
||||
import {
|
||||
registerWebPushNotifications,
|
||||
saveNotificationRegistration,
|
||||
} from '../../../services/notifications-service';
|
||||
import s from './BrowserNotifyModal.module.scss';
|
||||
import isPushNotificationSupported from '../../../utils/browserPushNotifications';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
function NotificationsNotSupported() {
|
||||
return <div>Browser notifications are not supported in your browser.</div>;
|
||||
}
|
||||
|
||||
function NotificationsEnabled() {
|
||||
return <div>Notifications enabled</div>;
|
||||
}
|
||||
|
||||
interface PermissionPopupPreviewProps {
|
||||
start: () => void;
|
||||
}
|
||||
function PermissionPopupPreview(props: PermissionPopupPreviewProps) {
|
||||
const { start } = props;
|
||||
|
||||
return (
|
||||
<div id="browser-push-preview-box" className={s.pushPreview}>
|
||||
<div className={s.inner}>
|
||||
<div className={s.title}>{window.location.toString()} wants to</div>
|
||||
<div className={s.permissionLine}>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M14 12.3333V13H2V12.3333L3.33333 11V7C3.33333 4.93333 4.68667 3.11333 6.66667 2.52667C6.66667 2.46 6.66667 2.4 6.66667 2.33333C6.66667 1.97971 6.80714 1.64057 7.05719 1.39052C7.30724 1.14048 7.64638 1 8 1C8.35362 1 8.69276 1.14048 8.94281 1.39052C9.19286 1.64057 9.33333 1.97971 9.33333 2.33333C9.33333 2.4 9.33333 2.46 9.33333 2.52667C11.3133 3.11333 12.6667 4.93333 12.6667 7V11L14 12.3333ZM9.33333 13.6667C9.33333 14.0203 9.19286 14.3594 8.94281 14.6095C8.69276 14.8595 8.35362 15 8 15C7.64638 15 7.30724 14.8595 7.05719 14.6095C6.80714 14.3594 6.66667 14.0203 6.66667 13.6667"
|
||||
fill="#676670"
|
||||
/>
|
||||
</svg>
|
||||
Show notifications
|
||||
</div>
|
||||
<div className={s.buttonRow}>
|
||||
<Button
|
||||
type="primary"
|
||||
className={s.allow}
|
||||
onClick={() => {
|
||||
start();
|
||||
}}
|
||||
>
|
||||
Allow
|
||||
</Button>
|
||||
<button type="button" className={s.disabled}>
|
||||
Block
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function BrowserNotifyModal() {
|
||||
const [error, setError] = useState<string>(null);
|
||||
const accessToken = useRecoilValue(accessTokenAtom);
|
||||
const config = useRecoilValue(clientConfigStateAtom);
|
||||
const [browserPushPermissionsPending, setBrowserPushPermissionsPending] =
|
||||
useState<boolean>(false);
|
||||
const notificationsPermitted =
|
||||
isPushNotificationSupported() && Notification.permission !== 'default';
|
||||
|
||||
const { notifications } = config;
|
||||
const { browser } = notifications;
|
||||
const { publicKey } = browser;
|
||||
|
||||
const browserPushSupported = browser.enabled && isPushNotificationSupported();
|
||||
|
||||
if (notificationsPermitted) {
|
||||
return <NotificationsEnabled />;
|
||||
}
|
||||
|
||||
const startBrowserPushRegistration = async () => {
|
||||
// If it's already denied or granted, don't do anything.
|
||||
if (isPushNotificationSupported() && Notification.permission !== 'default') {
|
||||
return;
|
||||
}
|
||||
|
||||
setBrowserPushPermissionsPending(true);
|
||||
try {
|
||||
const subscription = await registerWebPushNotifications(publicKey);
|
||||
saveNotificationRegistration('BROWSER_PUSH_NOTIFICATION', subscription, accessToken);
|
||||
setError(null);
|
||||
} catch (e) {
|
||||
setError(
|
||||
`Error registering for live notifications: ${e.message}. Make sure you're not inside a private browser environment or have previously disabled notifications for this stream.`,
|
||||
);
|
||||
}
|
||||
setBrowserPushPermissionsPending(false);
|
||||
};
|
||||
|
||||
if (!browserPushSupported) {
|
||||
return <NotificationsNotSupported />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Spin spinning={browserPushPermissionsPending}>
|
||||
<Row align="top">
|
||||
<Title>Browser Notifications</Title>
|
||||
Get notified right in the browser each time this stream goes live. Blah blah blah more
|
||||
description text goes here.
|
||||
</Row>
|
||||
<Row>{error}</Row>
|
||||
<Row align="top">
|
||||
<Col span={12}>
|
||||
<PermissionPopupPreview start={() => startBrowserPushRegistration()} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Spin>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user