diff --git a/web/components/chat/ChatContainer/ChatContainer.tsx b/web/components/chat/ChatContainer/ChatContainer.tsx index abdc589f0..8120eadae 100644 --- a/web/components/chat/ChatContainer/ChatContainer.tsx +++ b/web/components/chat/ChatContainer/ChatContainer.tsx @@ -65,9 +65,9 @@ export default function ChatContainer(props: Props) { const MessagesTable = useMemo( () => ( - <> +
)} - + ), [messages, usernameToHighlight, chatUserId, isModerator, atBottom, isMobile], ); diff --git a/web/components/chat/ChatTextField.tsx b/web/components/chat/ChatTextField.tsx deleted file mode 100644 index 5d2ef62c0..000000000 --- a/web/components/chat/ChatTextField.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { useState } from 'react'; - -interface Props {} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export default function ChatTextField(props: Props) { - const [value, setValue] = useState(''); - const [showEmojis, setShowEmojis] = useState(false); - - return ( -
- setValue(e.target.value)} - placeholder="Type a message here then hit ENTER" - /> - -
- ); -} diff --git a/web/components/chat/ChatTextField/ChatTextField.module.scss b/web/components/chat/ChatTextField/ChatTextField.module.scss index ca6439a74..43645a20f 100644 --- a/web/components/chat/ChatTextField/ChatTextField.module.scss +++ b/web/components/chat/ChatTextField/ChatTextField.module.scss @@ -7,48 +7,62 @@ overflow-x: hidden; div[role=textbox] { font-size: 0.9rem; + border-radius: .2rem; padding: .6rem; padding-right: calc(0.6rem + 44px); - border-radius: .35rem; background-color: var(--color-owncast-gray-700); + box-shadow: 0; + transition: box-shadow 50ms ease-in-out; + &:focus { + box-shadow: inset 0px 0px 0x 1px var(--color-owncast-purple-700); + outline: 1px solid var(--color-owncast-gray-500) !important; + } & > p { margin: 0px; } } + + .inputWrapper { + display: flex; + position: relative; + margin-right: .3rem; + border-radius: .2rem; + & > div { + transition: box-shadow .2s ease-in-out; + } + } + + .emojiButton { + border: none; + background: none; + cursor: pointer; + padding: 0 1rem; + position: absolute; + right: 0px; + top: 50%; + transform: translateY(-50%); + svg { + fill: var(--color-owncast-gray-300); + } + } + + .submitButtonWrapper { + display: flex; + padding: 6px 0; + justify-content: flex-end; + } } -.inputWrapper { - display: flex; - position: relative; - border-radius: var(--theme-rounded-corners); - outline: 1px solid var(--color-owncast-gray-500); - &:hover { - box-shadow: 0 0 1px 1px var(--color-owncast-gray-300); - } - & > div { - transition: box-shadow .2s ease-in-out; - &:focus { - // box-shadow: 0 0 1px 1px var(--color-owncast-gray-300); + + +.mobile { + &.root { + display: flex; + .inputWrapper { + flex: 1; + } + .submitButtonWrapper { + padding: 0px; } } } - -.emojiButton { - border: none; - background: none; - cursor: pointer; - padding: 0 1rem; - position: absolute; - right: 0px; - top: 50%; - transform: translateY(-50%); - svg { - fill: var(--color-owncast-gray-300); - } -} - -.submitButtonWrapper { - display: flex; - padding: 6px 0; - justify-content: flex-end; -} diff --git a/web/components/chat/ChatTextField/ChatTextField.tsx b/web/components/chat/ChatTextField/ChatTextField.tsx index d35ad1f8b..fa0dd6893 100644 --- a/web/components/chat/ChatTextField/ChatTextField.tsx +++ b/web/components/chat/ChatTextField/ChatTextField.tsx @@ -4,9 +4,10 @@ import React, { useState } from 'react'; import { useRecoilValue } from 'recoil'; import { Transforms, createEditor, BaseEditor, Text } from 'slate'; import { Slate, Editable, withReact, ReactEditor } from 'slate-react'; +import cn from 'classnames'; import EmojiPicker from './EmojiPicker'; import WebsocketService from '../../../services/websocket-service'; -import { websocketServiceAtom } from '../../stores/ClientConfigStore'; +import { isMobileAtom, websocketServiceAtom } from '../../stores/ClientConfigStore'; import { MessageType } from '../../../interfaces/socket-events'; import s from './ChatTextField.module.scss'; @@ -101,6 +102,7 @@ export default function ChatTextField(props: Props) { // const { value: originalValue } = props; const [showEmojis, setShowEmojis] = useState(false); const websocketService = useRecoilValue(websocketServiceAtom); + const isMobile = useRecoilValue(isMobileAtom); const [editor] = useState(() => withImages(withReact(createEditor()))); const sendMessage = () => { @@ -120,7 +122,7 @@ export default function ChatTextField(props: Props) { const handleChange = () => {}; - const handleEmojiSelect = e => { + const handleEmojiSelect = (e: any) => { ReactEditor.focus(editor); if (e.url) { @@ -134,7 +136,7 @@ export default function ChatTextField(props: Props) { } }; - const onKeyDown = e => { + const onKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { e.preventDefault(); sendMessage(); @@ -142,7 +144,11 @@ export default function ChatTextField(props: Props) { }; return ( -
+
- + {isMobile ? ( + + )}
} diff --git a/web/components/common/StreamInfo/StreamInfo.module.scss b/web/components/common/StreamInfo/StreamInfo.module.scss index 883ecdbf5..77a3be103 100644 --- a/web/components/common/StreamInfo/StreamInfo.module.scss +++ b/web/components/common/StreamInfo/StreamInfo.module.scss @@ -1,4 +1,4 @@ -.streamInfo { +.root { position: relative; display: grid; } @@ -34,3 +34,44 @@ font-weight: bold; } } + +.mobile { + &.root { + position: relative; + display: flex; + padding: 0 .3rem; + align-items: center; + justify-content: space-between; + .mobileInfo { + display: flex; + align-items: center; + .title { + font-size: 1.2rem; + font-weight: 600; + } + } + .mobileStatus { + display: flex; + font-weight: 600; + .viewerCount { + display: flex; + align-items: center; + gap: 4px; + } + .liveStatus { + display: flex; + align-items: center; + margin-left: .5rem; + font-size: .8rem; + gap: 4px; + .liveCircle { + border-radius: 50%; + background-color: red; + width: .5rem; + height: .5rem; + } + } + } + } +} + diff --git a/web/components/common/StreamInfo/StreamInfo.tsx b/web/components/common/StreamInfo/StreamInfo.tsx index 6aad0cb5e..016c8d358 100644 --- a/web/components/common/StreamInfo/StreamInfo.tsx +++ b/web/components/common/StreamInfo/StreamInfo.tsx @@ -1,16 +1,56 @@ import { useRecoilValue } from 'recoil'; +import cn from 'classnames'; +import { EyeFilled } from '@ant-design/icons'; +import { useEffect } from 'react'; import { ClientConfig } from '../../../interfaces/client-config.model'; -import { clientConfigStateAtom } from '../../stores/ClientConfigStore'; +import { + clientConfigStateAtom, + isOnlineSelector, + serverStatusState, +} from '../../stores/ClientConfigStore'; import { ServerLogo } from '../../ui'; +import StatusBar from '../../ui/Statusbar'; import CategoryIcon from '../../ui/CategoryIcon/CategoryIcon'; import SocialLinks from '../../ui/SocialLinks/SocialLinks'; import s from './StreamInfo.module.scss'; +import { ServerStatus } from '../../../interfaces/server-status.model'; -export default function StreamInfo() { +interface Props { + isMobile: boolean; +} +export default function StreamInfo({ isMobile }: Props) { const { socialHandles, name, title, tags } = useRecoilValue(clientConfigStateAtom); + const { viewerCount, lastConnectTime, lastDisconnectTime } = + useRecoilValue(serverStatusState); + const online = useRecoilValue(isOnlineSelector); - return ( -
+ useEffect(() => { + console.log({ online }); + }, [online]); + + return isMobile ? ( +
+
+ +
{name}
+
+
+
+ {online && ( + <> + {viewerCount} + + + )} +
+
+ {online &&
} + {online ? 'LIVE' : 'OFFLINE'} +
+
+
+ ) : ( +
@@ -23,6 +63,12 @@ export default function StreamInfo() {
+
); } diff --git a/web/components/ui/Content/Content.module.scss b/web/components/ui/Content/Content.module.scss index 2f96af2d2..6c649c3a3 100644 --- a/web/components/ui/Content/Content.module.scss +++ b/web/components/ui/Content/Content.module.scss @@ -1,32 +1,8 @@ .root { display: grid; grid-template-columns: 1fr auto; - &.mobile { - display: flex; - flex-direction: column; - height: calc(100vh - 64px); - overflow-y: hidden; - .topHalf { - border: 1px dashed white; - height: calc(40vh - 64px); - overflow: hidden; - } - .lowerHalf { - border: 1px dashed red; - height: 60vh; - } - } } -.mobileChat { - position: relative; - display: block; - // top: 0px; - width: 100%; - [data-virtuoso-scroller] { - height: 500px; - } -} .leftCol { display: flex; @@ -40,3 +16,26 @@ z-index: 999999; } +.mobile { + &.root { + display: flex; + flex-direction: column; + height: calc(100vh - 64px); + overflow: hidden; + .topHalf { + display: grid; + grid-template-rows: 30vh 5vh 5vh; + height: 40vh; + // overflow: hidden; + } + .lowerHalf { + height: 60vh; + } + } + .mobileChat { + position: relative; + display: block; + height: 100%; + width: 100%; + } +} diff --git a/web/components/ui/Content/Content.tsx b/web/components/ui/Content/Content.tsx index 9564a5bf0..32d2ecf79 100644 --- a/web/components/ui/Content/Content.tsx +++ b/web/components/ui/Content/Content.tsx @@ -11,7 +11,6 @@ import { chatDisplayNameAtom, chatUserIdAtom, isChatVisibleSelector, - serverStatusState, appStateAtom, isOnlineSelector, isMobileAtom, @@ -28,8 +27,6 @@ import { ChatMessage } from '../../../interfaces/chat-message.model'; import ChatTextField from '../../chat/ChatTextField/ChatTextField'; import ActionButtonRow from '../../action-buttons/ActionButtonRow'; import ActionButton from '../../action-buttons/ActionButton'; -import Statusbar from '../Statusbar/Statusbar'; -import { ServerStatus } from '../../../interfaces/server-status.model'; import { Follower } from '../../../interfaces/follower'; import NotifyReminderPopup from '../NotifyReminderPopup/NotifyReminderPopup'; import OfflineBanner from '../OfflineBanner/OfflineBanner'; @@ -38,13 +35,13 @@ import FollowButton from '../../action-buttons/FollowButton'; import NotifyButton from '../../action-buttons/NotifyButton'; import Modal from '../Modal/Modal'; import BrowserNotifyModal from '../../modals/BrowserNotify/BrowserNotifyModal'; +import StreamInfo from '../../common/StreamInfo'; const { TabPane } = Tabs; const { Content } = Layout; export default function ContentComponent() { const appState = useRecoilValue(appStateAtom); - const status = useRecoilValue(serverStatusState); const clientConfig = useRecoilValue(clientConfigStateAtom); const isChatVisible = useRecoilValue(isChatVisibleSelector); const [isMobile, setIsMobile] = useRecoilState(isMobileAtom); @@ -54,7 +51,6 @@ export default function ContentComponent() { const chatUserId = useRecoilValue(chatUserIdAtom); const { extraPageContent, version, name, summary } = clientConfig; - const { viewerCount, lastConnectTime, lastDisconnectTime } = status; const [showNotifyReminder, setShowNotifyReminder] = useState(false); const [showNotifyPopup, setShowNotifyPopup] = useState(false); @@ -129,13 +125,6 @@ export default function ContentComponent() { text="Stream is offline text goes here. Will create a new form to set it in the Admin." /> )} - -
{externalActionButtons} @@ -158,12 +147,18 @@ export default function ContentComponent() {
+
{isChatVisible && isMobile && ( - -
+ +
+
-
)} @@ -186,7 +181,7 @@ export default function ContentComponent() { -
+ {!isMobile &&
}
{isChatVisible && !isMobile && } diff --git a/web/components/ui/Header/Header.module.scss b/web/components/ui/Header/Header.module.scss index 695b46d1f..3b3770e91 100644 --- a/web/components/ui/Header/Header.module.scss +++ b/web/components/ui/Header/Header.module.scss @@ -5,15 +5,22 @@ align-items: center; justify-content: space-between; z-index: 20; - padding: 0.5rem 1rem; + padding: 0.4rem .7rem; background-color: var(--default-bg-color); .logo { display: flex; align-items: center; span { - margin-left: 1rem; - font-size: 1.7rem; + margin-left: .5rem; + font-size: 1.5rem; font-weight: 600; } } } + +@media (max-width: 768px) { + .header { + line-height: 5vh; + height: 5vh; + } +} diff --git a/web/components/ui/Logo/Logo.module.scss b/web/components/ui/Logo/Logo.module.scss index a8874162b..f68f9c7b2 100644 --- a/web/components/ui/Logo/Logo.module.scss +++ b/web/components/ui/Logo/Logo.module.scss @@ -4,8 +4,9 @@ align-items: center; justify-content: center; overflow: hidden; - width: clamp(4rem, 10vw, 120px); - height: clamp(4rem, 10vw, 120px); + margin-right: .5rem; + width: clamp(2.5vh, 9vw, 120px); + height: clamp(2.5vh, 9vw, 120px); border-radius: 50%; border-width: 3px; border-style: solid; @@ -27,3 +28,4 @@ background-position: center; overflow: hidden; } + diff --git a/web/components/ui/OfflineBanner/OfflineBanner.module.scss b/web/components/ui/OfflineBanner/OfflineBanner.module.scss index 81d21ae91..48bece685 100644 --- a/web/components/ui/OfflineBanner/OfflineBanner.module.scss +++ b/web/components/ui/OfflineBanner/OfflineBanner.module.scss @@ -7,9 +7,8 @@ width: clamp(200px, 100%, 300px); display: flex; flex-direction: column; - ; background-color: var(--theme-background-secondary); - margin: auto; + margin: 1rem auto; border-radius: var(--theme-rounded-corners); padding: 1rem; } diff --git a/web/components/ui/Statusbar/index.ts b/web/components/ui/Statusbar/index.ts new file mode 100644 index 000000000..d058c8e97 --- /dev/null +++ b/web/components/ui/Statusbar/index.ts @@ -0,0 +1 @@ +export { default } from './Statusbar'; diff --git a/web/components/ui/index.tsx b/web/components/ui/index.tsx index 651840de9..91880c9bc 100644 --- a/web/components/ui/index.tsx +++ b/web/components/ui/index.tsx @@ -4,3 +4,4 @@ export { default as Footer } from './Footer/index'; export { default as Content } from './Content/index'; export { default as ModIcon } from './ModIcon'; export { default as ServerLogo } from './Logo'; +export { default as StatusBar } from './Statusbar'; diff --git a/web/components/video/Player.module.scss b/web/components/video/Player.module.scss index 8b22c7f83..67906b1b8 100644 --- a/web/components/video/Player.module.scss +++ b/web/components/video/Player.module.scss @@ -10,3 +10,9 @@ width: 100%; height: 100%; } + +@media (max-width: 768px) { + .player { + height: 30vh !important; + } +} diff --git a/web/styles/globals.scss b/web/styles/globals.scss index 3622d924f..e74409163 100644 --- a/web/styles/globals.scss +++ b/web/styles/globals.scss @@ -9,11 +9,17 @@ body { padding: 0; margin: 0; font-family: var(--theme-font-family), var(--theme-header-font-family), sans-serif; - font-size: clamp(14px, 1.5vw, 17px); + font-size: clamp(14px, 1vw, 17px); background-color: var(--default-bg-color); color: var(--default-text-color); } +@media (max-width: 768px) { + body { + overflow: hidden; + } +} + // a { // color: inherit; // text-decoration: none;