diff --git a/web/components/action-buttons/ActionButton/ActionButton.tsx b/web/components/action-buttons/ActionButton/ActionButton.tsx
index 658cdfaf2..d9d1bc17a 100644
--- a/web/components/action-buttons/ActionButton/ActionButton.tsx
+++ b/web/components/action-buttons/ActionButton/ActionButton.tsx
@@ -1,7 +1,7 @@
import { Button } from 'antd';
import { FC, useState } from 'react';
-import { Modal } from '../../ui/Modal/Modal';
-import { ExternalAction } from '../../../interfaces/external-action';
+import { Modal } from '~/components/ui/Modal/Modal';
+import { ExternalAction } from '~/interfaces/external-action';
import styles from './ActionButton.module.scss';
export type ActionButtonProps = {
diff --git a/web/components/action-buttons/FollowButton.tsx b/web/components/action-buttons/FollowButton.tsx
index 0b8de983b..62e2ff881 100644
--- a/web/components/action-buttons/FollowButton.tsx
+++ b/web/components/action-buttons/FollowButton.tsx
@@ -1,12 +1,12 @@
-import { Button, ButtonProps } from 'antd';
-import { HeartFilled } from '@ant-design/icons';
import { FC, useState } from 'react';
import { useRecoilValue } from 'recoil';
-import { Modal } from '../ui/Modal/Modal';
-import { FollowModal } from '../modals/FollowModal/FollowModal';
+import { Button, ButtonProps } from 'antd';
+import { HeartFilled } from '@ant-design/icons';
+import { Modal } from '~/components/ui/Modal/Modal';
+import { FollowModal } from '~/components/modals/FollowModal/FollowModal';
+import { clientConfigStateAtom } from '~/components/stores/ClientConfigStore';
+import { ClientConfig } from '~/interfaces/client-config.model';
import styles from './ActionButton/ActionButton.module.scss';
-import { clientConfigStateAtom } from '../stores/ClientConfigStore';
-import { ClientConfig } from '../../interfaces/client-config.model';
export type FollowButtonProps = ButtonProps;
diff --git a/web/components/action-buttons/NotifyButton.tsx b/web/components/action-buttons/NotifyButton.tsx
index e292cba29..d6eb515f3 100644
--- a/web/components/action-buttons/NotifyButton.tsx
+++ b/web/components/action-buttons/NotifyButton.tsx
@@ -1,6 +1,6 @@
+import { FC } from 'react';
import { Button } from 'antd';
import { BellFilled } from '@ant-design/icons';
-import { FC } from 'react';
import styles from './ActionButton/ActionButton.module.scss';
export type NotifyButtonProps = {
diff --git a/web/components/chat/ChatContainer/ChatContainer.stories.tsx b/web/components/chat/ChatContainer/ChatContainer.stories.tsx
index 220f6d754..479fc7d15 100644
--- a/web/components/chat/ChatContainer/ChatContainer.stories.tsx
+++ b/web/components/chat/ChatContainer/ChatContainer.stories.tsx
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { RecoilRoot } from 'recoil';
import { ChatContainer } from './ChatContainer';
-import { ChatMessage } from '../../../interfaces/chat-message.model';
+import { ChatMessage } from '~/interfaces/chat-message.model';
export default {
title: 'owncast/Chat/Chat messages container',
diff --git a/web/components/chat/ChatContainer/ChatContainer.tsx b/web/components/chat/ChatContainer/ChatContainer.tsx
index 2a7c045fb..8c29da7cd 100644
--- a/web/components/chat/ChatContainer/ChatContainer.tsx
+++ b/web/components/chat/ChatContainer/ChatContainer.tsx
@@ -5,16 +5,15 @@ import {
ConnectedClientInfoEvent,
MessageType,
NameChangeEvent,
-} from '../../../interfaces/socket-events';
-import styles from './ChatContainer.module.scss';
-import { ChatMessage } from '../../../interfaces/chat-message.model';
-import { ChatUserMessage } from '../ChatUserMessage/ChatUserMessage';
-import { ChatTextField } from '../ChatTextField/ChatTextField';
-import { ChatModeratorNotification } from '../ChatModeratorNotification/ChatModeratorNotification';
-// import ChatActionMessage from '../ChatAction/ChatActionMessage';
-import { ChatSystemMessage } from '../ChatSystemMessage/ChatSystemMessage';
-import { ChatJoinMessage } from '../ChatJoinMessage/ChatJoinMessage';
+} from '~/interfaces/socket-events';
+import { ChatMessage } from '~/interfaces/chat-message.model';
+import { ChatUserMessage } from '~/components/chat/ChatUserMessage/ChatUserMessage';
+import { ChatTextField } from '~/components/chat/ChatTextField/ChatTextField';
+import { ChatModeratorNotification } from '~/components/chat/ChatModeratorNotification/ChatModeratorNotification';
+import { ChatSystemMessage } from '~/components/chat/ChatSystemMessage/ChatSystemMessage';
+import { ChatJoinMessage } from '~/components/chat/ChatJoinMessage/ChatJoinMessage';
import { ScrollToBotBtn } from './ScrollToBotBtn';
+import styles from './ChatContainer.module.scss';
export type ChatContainerProps = {
messages: ChatMessage[];
diff --git a/web/components/chat/ChatContainer/ScrollToBotBtn.tsx b/web/components/chat/ChatContainer/ScrollToBotBtn.tsx
index ea72b7d5a..7fa6cb0a4 100644
--- a/web/components/chat/ChatContainer/ScrollToBotBtn.tsx
+++ b/web/components/chat/ChatContainer/ScrollToBotBtn.tsx
@@ -1,7 +1,7 @@
import { VerticalAlignBottomOutlined } from '@ant-design/icons';
import { Button } from 'antd';
import { FC, MutableRefObject } from 'react';
-import { ChatMessage } from '../../../interfaces/chat-message.model';
+import { ChatMessage } from '~/interfaces/chat-message.model';
import styles from './ChatContainer.module.scss';
type Props = {
diff --git a/web/components/chat/ChatJoinMessage/ChatJoinMessage.stories.tsx b/web/components/chat/ChatJoinMessage/ChatJoinMessage.stories.tsx
index 474931fb0..1a8579bcf 100644
--- a/web/components/chat/ChatJoinMessage/ChatJoinMessage.stories.tsx
+++ b/web/components/chat/ChatJoinMessage/ChatJoinMessage.stories.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { ChatJoinMessage } from './ChatJoinMessage';
-import Mock from '../../../stories/assets/mocks/chatmessage-action.png';
+import Mock from '~/stories/assets/mocks/chatmessage-action.png';
export default {
title: 'owncast/Chat/Messages/Chat Join',
diff --git a/web/components/chat/ChatJoinMessage/ChatJoinMessage.tsx b/web/components/chat/ChatJoinMessage/ChatJoinMessage.tsx
index 572a866cc..f3573322e 100644
--- a/web/components/chat/ChatJoinMessage/ChatJoinMessage.tsx
+++ b/web/components/chat/ChatJoinMessage/ChatJoinMessage.tsx
@@ -1,6 +1,6 @@
import { FC } from 'react';
import { TeamOutlined } from '@ant-design/icons';
-import { ChatUserBadge } from '../ChatUserBadge/ChatUserBadge';
+import { ChatUserBadge } from '~/components/chat/ChatUserBadge/ChatUserBadge';
import styles from './ChatJoinMessage.module.scss';
export type ChatJoinMessageProps = {
diff --git a/web/components/chat/ChatModerationActionMenu/ChatModerationActionMenu.tsx b/web/components/chat/ChatModerationActionMenu/ChatModerationActionMenu.tsx
index 7b68a54af..67cc99259 100644
--- a/web/components/chat/ChatModerationActionMenu/ChatModerationActionMenu.tsx
+++ b/web/components/chat/ChatModerationActionMenu/ChatModerationActionMenu.tsx
@@ -7,9 +7,9 @@ import {
import { Dropdown, Menu, MenuProps, Space, message, Modal as AntModal } from 'antd';
import { FC, useState } from 'react';
import { Modal } from '../../ui/Modal/Modal';
-import { ChatModerationDetailsModal } from '../ChatModerationDetailsModal/ChatModerationDetailsModal';
+import { ChatModerationDetailsModal } from '~/components/chat/ChatModerationDetailsModal/ChatModerationDetailsModal';
import styles from './ChatModerationActionMenu.module.scss';
-import ChatModeration from '../../../services/moderation-service';
+import ChatModeration from '~/services/moderation-service';
const { confirm } = AntModal;
diff --git a/web/components/chat/ChatModerationDetailsModal/ChatModerationDetailsModal.tsx b/web/components/chat/ChatModerationDetailsModal/ChatModerationDetailsModal.tsx
index 9a9d90055..60559b3cd 100644
--- a/web/components/chat/ChatModerationDetailsModal/ChatModerationDetailsModal.tsx
+++ b/web/components/chat/ChatModerationDetailsModal/ChatModerationDetailsModal.tsx
@@ -3,9 +3,9 @@ import { FC, useEffect, useState } from 'react';
import format from 'date-fns/format';
import { DeleteOutlined } from '@ant-design/icons';
import { ColumnsType } from 'antd/lib/table';
-import ChatModeration from '../../../services/moderation-service';
+import ChatModeration from '~/services/moderation-service';
import styles from './ChatModerationDetailsModal.module.scss';
-import { formatUAstring } from '../../../utils/format';
+import { formatUAstring } from '~/utils/format';
const { Panel } = Collapse;
diff --git a/web/components/chat/ChatModeratorNotification/ChatModeratorNotification.tsx b/web/components/chat/ChatModeratorNotification/ChatModeratorNotification.tsx
index 7d83e2978..8c69ef414 100644
--- a/web/components/chat/ChatModeratorNotification/ChatModeratorNotification.tsx
+++ b/web/components/chat/ChatModeratorNotification/ChatModeratorNotification.tsx
@@ -1,5 +1,5 @@
import styles from './ChatModeratorNotification.module.scss';
-import Icon from '../../../assets/images/moderator.svg';
+import Icon from '~/assets/images/moderator.svg';
export const ChatModeratorNotification = () => (
diff --git a/web/components/chat/ChatSocialMessage/ChatSocialMessage.tsx b/web/components/chat/ChatSocialMessage/ChatSocialMessage.tsx
index fd2d022c0..5a4094c92 100644
--- a/web/components/chat/ChatSocialMessage/ChatSocialMessage.tsx
+++ b/web/components/chat/ChatSocialMessage/ChatSocialMessage.tsx
@@ -1,7 +1,7 @@
import { Avatar, Col, Row } from 'antd';
import dynamic from 'next/dynamic';
import React, { FC } from 'react';
-import { ChatSocialMessage as ChatMessage } from '../../../interfaces/chat-social-message.model';
+import { ChatSocialMessage as ChatMessage } from '~/interfaces/chat-social-message.model';
import styles from './ChatSocialMessage.module.scss';
const FollowIcon = dynamic(() => import('./follow.svg'));
@@ -15,7 +15,7 @@ export interface ChatSocialMessageProps {
export const ChatSocialMessage: FC
= ({ message }) => {
const { body, title, image, link, type } = message;
- let Icon;
+ let Icon: React.ComponentType;
switch (type.toString()) {
case 'follow':
diff --git a/web/components/chat/ChatSystemMessage/ChatSystemMessage.stories.tsx b/web/components/chat/ChatSystemMessage/ChatSystemMessage.stories.tsx
index b0c207ace..0763b6d82 100644
--- a/web/components/chat/ChatSystemMessage/ChatSystemMessage.stories.tsx
+++ b/web/components/chat/ChatSystemMessage/ChatSystemMessage.stories.tsx
@@ -1,8 +1,8 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { ChatSystemMessage } from './ChatSystemMessage';
-import Mock from '../../../stories/assets/mocks/chatmessage-system.png';
-import { ChatMessage } from '../../../interfaces/chat-message.model';
+import Mock from '~/stories/assets/mocks/chatmessage-system.png';
+import { ChatMessage } from '~/interfaces/chat-message.model';
export default {
title: 'owncast/Chat/Messages/System',
diff --git a/web/components/chat/ChatSystemMessage/ChatSystemMessage.tsx b/web/components/chat/ChatSystemMessage/ChatSystemMessage.tsx
index e8eb1d61a..a211680d4 100644
--- a/web/components/chat/ChatSystemMessage/ChatSystemMessage.tsx
+++ b/web/components/chat/ChatSystemMessage/ChatSystemMessage.tsx
@@ -1,7 +1,7 @@
/* eslint-disable react/no-danger */
import { Highlight } from 'react-highlighter-ts';
import { FC } from 'react';
-import { ChatMessage } from '../../../interfaces/chat-message.model';
+import { ChatMessage } from '~/interfaces/chat-message.model';
import styles from './ChatSystemMessage.module.scss';
export type ChatSystemMessageProps = {
diff --git a/web/components/chat/ChatTextField/ChatTextField.stories.tsx b/web/components/chat/ChatTextField/ChatTextField.stories.tsx
index ea3da2df4..364bb7377 100644
--- a/web/components/chat/ChatTextField/ChatTextField.stories.tsx
+++ b/web/components/chat/ChatTextField/ChatTextField.stories.tsx
@@ -2,7 +2,7 @@ import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { RecoilRoot } from 'recoil';
import { ChatTextField } from './ChatTextField';
-import Mockup from '../../../stories/assets/mocks/chatinput-mock.png';
+import Mockup from '~/stories/assets/mocks/chatinput-mock.png';
const mockResponse = JSON.parse(
`[{"name":"Reaper-gg.png","url":"https://ui-avatars.com/api/?background=0D8ABC&color=fff&name=OC"},{"name":"Reaper-hi.png","url":"https://ui-avatars.com/api/?background=0D8ABC&color=fff&name=XX"},{"name":"Reaper-hype.png","url":"https://ui-avatars.com/api/?background=0D8ABC&color=fff&name=TX"},{"name":"Reaper-lol.png","url":"https://ui-avatars.com/api/?background=0D8ABC&color=fff&name=CA"},{"name":"Reaper-love.png","url":"https://ui-avatars.com/api/?background=0D8ABC&color=fff&name=OK"}]`,
diff --git a/web/components/chat/ChatTextField/ChatTextField.tsx b/web/components/chat/ChatTextField/ChatTextField.tsx
index 596d620da..cda5ae0f1 100644
--- a/web/components/chat/ChatTextField/ChatTextField.tsx
+++ b/web/components/chat/ChatTextField/ChatTextField.tsx
@@ -1,13 +1,13 @@
-import { SendOutlined, SmileOutlined } from '@ant-design/icons';
-import { Popover } from 'antd';
import React, { FC, useMemo, useState } from 'react';
+import dynamic from 'next/dynamic';
import { useRecoilValue } from 'recoil';
import { Transforms, createEditor, BaseEditor, Text, Descendant, Editor, Node, Path } from 'slate';
import { Slate, Editable, withReact, ReactEditor, useSelected, useFocused } from 'slate-react';
-import dynamic from 'next/dynamic';
-import WebsocketService from '../../../services/websocket-service';
-import { websocketServiceAtom } from '../../stores/ClientConfigStore';
-import { MessageType } from '../../../interfaces/socket-events';
+import { MessageType } from '~/interfaces/socket-events';
+import { Popover } from 'antd';
+import { SendOutlined, SmileOutlined } from '@ant-design/icons';
+import WebsocketService from '~/services/websocket-service';
+import { websocketServiceAtom } from '~/components/stores/ClientConfigStore';
import styles from './ChatTextField.module.scss';
// Lazy loaded components
diff --git a/web/components/chat/ChatUserMessage/ChatUserMessage.stories.tsx b/web/components/chat/ChatUserMessage/ChatUserMessage.stories.tsx
index 91ad0091c..e077c7b9a 100644
--- a/web/components/chat/ChatUserMessage/ChatUserMessage.stories.tsx
+++ b/web/components/chat/ChatUserMessage/ChatUserMessage.stories.tsx
@@ -2,8 +2,8 @@ import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { RecoilRoot } from 'recoil';
import { ChatUserMessage } from './ChatUserMessage';
-import { ChatMessage } from '../../../interfaces/chat-message.model';
-import Mock from '../../../stories/assets/mocks/chatmessage-user.png';
+import { ChatMessage } from '~/interfaces/chat-message.model';
+import Mock from '~/stories/assets/mocks/chatmessage-user.png';
export default {
title: 'owncast/Chat/Messages/Standard user',
diff --git a/web/components/common/ContentHeader/ContentHeader.tsx b/web/components/common/ContentHeader/ContentHeader.tsx
index 9ed73a74f..29e65e702 100644
--- a/web/components/common/ContentHeader/ContentHeader.tsx
+++ b/web/components/common/ContentHeader/ContentHeader.tsx
@@ -1,9 +1,9 @@
import cn from 'classnames';
import { FC } from 'react';
import Linkify from 'react-linkify';
-import { Logo } from '../../ui/Logo/Logo';
-import { SocialLinks } from '../../ui/SocialLinks/SocialLinks';
-import { SocialLink } from '../../../interfaces/social-link.model';
+import { Logo } from '~/components/ui/Logo/Logo';
+import { SocialLinks } from '~/components/ui/SocialLinks/SocialLinks';
+import { SocialLink } from '~/interfaces/social-link.model';
import styles from './ContentHeader.module.scss';
export type ContentHeaderProps = {
diff --git a/web/components/common/UserDropdown/UserDropdown.tsx b/web/components/common/UserDropdown/UserDropdown.tsx
index 0e2f93561..7503919d1 100644
--- a/web/components/common/UserDropdown/UserDropdown.tsx
+++ b/web/components/common/UserDropdown/UserDropdown.tsx
@@ -1,3 +1,13 @@
+import { FC, useState } from 'react';
+import { useRecoilState, useRecoilValue } from 'recoil';
+import dynamic from 'next/dynamic';
+import { useHotkeys } from 'react-hotkeys-hook';
+import {
+ chatVisibleToggleAtom,
+ chatDisplayNameAtom,
+ appStateAtom,
+} from '~/components//stores/ClientConfigStore';
+import { AppStateOptions } from '~/components/stores/application-state';
import { Menu, Dropdown, Button, Space } from 'antd';
import {
CaretDownOutlined,
@@ -6,17 +16,7 @@ import {
MessageOutlined,
UserOutlined,
} from '@ant-design/icons';
-import { useRecoilState, useRecoilValue } from 'recoil';
-import { FC, useState } from 'react';
-import { useHotkeys } from 'react-hotkeys-hook';
-import dynamic from 'next/dynamic';
-import {
- chatVisibleToggleAtom,
- chatDisplayNameAtom,
- appStateAtom,
-} from '../../stores/ClientConfigStore';
import styles from './UserDropdown.module.scss';
-import { AppStateOptions } from '../../stores/application-state';
// Lazy loaded components
const Modal = dynamic(() => import('../../ui/Modal/Modal').then(mod => mod.Modal));
diff --git a/web/components/ui/Content/Content.tsx b/web/components/ui/Content/Content.tsx
index 577b72de5..5352df6b1 100644
--- a/web/components/ui/Content/Content.tsx
+++ b/web/components/ui/Content/Content.tsx
@@ -3,7 +3,7 @@ import { Layout, Tabs, Spin } from 'antd';
import { FC, useEffect, useState } from 'react';
import classNames from 'classnames';
import dynamic from 'next/dynamic';
-import { LOCAL_STORAGE_KEYS, getLocalStorage, setLocalStorage } from '../../../utils/localStorage';
+import { LOCAL_STORAGE_KEYS, getLocalStorage, setLocalStorage } from '~/utils/localStorage';
import {
clientConfigStateAtom,
@@ -16,24 +16,23 @@ import {
isOnlineSelector,
isMobileAtom,
serverStatusState,
-} from '../../stores/ClientConfigStore';
-import { ClientConfig } from '../../../interfaces/client-config.model';
-import { CustomPageContent } from '../CustomPageContent/CustomPageContent';
-import { OwncastPlayer } from '../../video/OwncastPlayer/OwncastPlayer';
+} from '~/components/stores/ClientConfigStore';
+import { ClientConfig } from '~/interfaces/client-config.model';
+import { CustomPageContent } from '~/components/ui/CustomPageContent/CustomPageContent';
+import { OwncastPlayer } from '~/components/video/OwncastPlayer/OwncastPlayer';
+import { Sidebar } from '~/components/ui/Sidebar/Sidebar';
+import { Footer } from '~/components/ui/Footer/Footer';
+import { ActionButtonRow } from '~/components/action-buttons/ActionButtonRow/ActionButtonRow';
+import { ActionButton } from '~/components/action-buttons/ActionButton/ActionButton';
+import { OfflineBanner } from '~/components/ui/OfflineBanner/OfflineBanner';
+import { AppStateOptions } from '~/components/stores/application-state';
+import { FollowButton } from '~/components/action-buttons/FollowButton';
+import { NotifyButton } from '~/components/action-buttons/NotifyButton';
+import { ContentHeader } from '~/components/common/ContentHeader/ContentHeader';
+import { ServerStatus } from '~/interfaces/server-status.model';
+import { Statusbar } from '~/components/ui/Statusbar/Statusbar';
+import { ChatMessage } from '~/interfaces/chat-message.model';
import styles from './Content.module.scss';
-import { Sidebar } from '../Sidebar/Sidebar';
-import { Footer } from '../Footer/Footer';
-
-import { ActionButtonRow } from '../../action-buttons/ActionButtonRow/ActionButtonRow';
-import { ActionButton } from '../../action-buttons/ActionButton/ActionButton';
-import { OfflineBanner } from '../OfflineBanner/OfflineBanner';
-import { AppStateOptions } from '../../stores/application-state';
-import { FollowButton } from '../../action-buttons/FollowButton';
-import { NotifyButton } from '../../action-buttons/NotifyButton';
-import { ContentHeader } from '../../common/ContentHeader/ContentHeader';
-import { ServerStatus } from '../../../interfaces/server-status.model';
-import { Statusbar } from '../Statusbar/Statusbar';
-import { ChatMessage } from '../../../interfaces/chat-message.model';
const { TabPane } = Tabs;
const { Content: AntContent } = Layout;
@@ -43,21 +42,21 @@ const { Content: AntContent } = Layout;
const Modal = dynamic(() => import('../Modal/Modal').then(mod => mod.Modal));
const BrowserNotifyModal = dynamic(() =>
- import('../../modals/BrowserNotifyModal/BrowserNotifyModal').then(mod => mod.BrowserNotifyModal),
+ import('~/components/modals/BrowserNotifyModal/BrowserNotifyModal').then(mod => mod.BrowserNotifyModal),
);
const NotifyReminderPopup = dynamic(() =>
- import('../NotifyReminderPopup/NotifyReminderPopup').then(mod => mod.NotifyReminderPopup),
+ import('~/components/ui/NotifyReminderPopup/NotifyReminderPopup').then(mod => mod.NotifyReminderPopup),
);
const FollowerCollection = dynamic(() =>
- import('../followers/FollowerCollection/FollowerCollection').then(mod => mod.FollowerCollection),
+ import('~/components/ui/followers/FollowerCollection/FollowerCollection').then(mod => mod.FollowerCollection),
);
// We only need to load the chat container here if we're in mobile or narrow
// windows, so lazy loading it makes sense.
const ChatContainer = dynamic(() =>
- import('../../chat/ChatContainer/ChatContainer').then(mod => mod.ChatContainer),
+ import('~/components/chat/ChatContainer/ChatContainer').then(mod => mod.ChatContainer),
);
const DesktopContent = ({ name, streamTitle, summary, tags, socialHandles, extraPageContent }) => (