diff --git a/web/components/config/README.md b/web/components/config/README.md index 2117f290e..578e2193d 100644 --- a/web/components/config/README.md +++ b/web/components/config/README.md @@ -42,3 +42,5 @@ There are also a variety of other local states to manage the display of error/su +segment-slider-container +selected-value-note \ No newline at end of file diff --git a/web/components/config/edit-social-links.tsx b/web/components/config/edit-social-links.tsx index d93b24ddc..570a5ec40 100644 --- a/web/components/config/edit-social-links.tsx +++ b/web/components/config/edit-social-links.tsx @@ -221,6 +221,19 @@ export default function EditSocialLinks() { disabled: !isValidUrl(modalDataState.url), }; + const otherField = ( +
+
+
+ +
+
+ ); + return (
@@ -249,30 +262,23 @@ export default function EditSocialLinks() { confirmLoading={modalProcessing} okButtonProps={okButtonProps} > - <SocialDropdown - iconList={availableIconsList} - selectedOption={selectedOther ? OTHER_SOCIAL_HANDLE_OPTION : modalDataState.platform} - onSelected={handleDropdownSelect} - /> - {displayOther ? ( - <> - <Input - placeholder="Other" - defaultValue={modalDataState.platform} - onChange={handleOtherNameChange} - /> - <br /> - </> - ) : null} - <br /> - <TextField - fieldName="social-url" - label="URL" - placeholder={PLACEHOLDERS[modalDataState.platform] || 'Url to page'} - value={modalDataState.url} - onChange={handleUrlChange} - /> - <FormStatusIndicator status={submitStatus} /> + <div className="social-handle-modal-content"> + <SocialDropdown + iconList={availableIconsList} + selectedOption={selectedOther ? OTHER_SOCIAL_HANDLE_OPTION : modalDataState.platform} + onSelected={handleDropdownSelect} + /> + {displayOther && otherField} + <br /> + <TextField + fieldName="social-url" + label="URL" + placeholder={PLACEHOLDERS[modalDataState.platform] || 'Url to page'} + value={modalDataState.url} + onChange={handleUrlChange} + /> + <FormStatusIndicator status={submitStatus} /> + </div> </Modal> <br /> <Button diff --git a/web/components/config/social-icons-dropdown.tsx b/web/components/config/social-icons-dropdown.tsx index dec12cdf0..df1fab8dd 100644 --- a/web/components/config/social-icons-dropdown.tsx +++ b/web/components/config/social-icons-dropdown.tsx @@ -23,39 +23,41 @@ export default function SocialDropdown({ iconList, selectedOption, onSelected }: If you are looking for a platform name not on this list, please select Other and type in your own name. A logo will not be provided. </p> - <p className="description"> - If you DO have a logo, drop it in to the <code>/webroot/img/platformicons</code> directory - and update the <code>/socialHandle.go</code> list. Then restart the server and it will show - up in the list. - </p> - <Select - style={{ width: 240 }} - className="social-dropdown" - placeholder="Social platform..." - defaultValue={inititalSelected} - value={inititalSelected} - onSelect={handleSelected} - > - {iconList.map(item => { - const { platform, icon, key } = item; - return ( - <Select.Option className="social-option" key={`platform-${key}`} value={key}> - <span className="option-icon"> - <img src={`${NEXT_PUBLIC_API_HOST}${icon}`} alt="" className="option-icon" /> - </span> - <span className="option-label">{platform}</span> + <div className="formfield-container"> + <div className="label-side"> + <span className="formfield-label">Social Platform</span> + </div> + <div className="input-side"> + <Select + style={{ width: 240 }} + className="social-dropdown" + placeholder="Social platform..." + defaultValue={inititalSelected} + value={inititalSelected} + onSelect={handleSelected} + > + {iconList.map(item => { + const { platform, icon, key } = item; + return ( + <Select.Option className="social-option" key={`platform-${key}`} value={key}> + <span className="option-icon"> + <img src={`${NEXT_PUBLIC_API_HOST}${icon}`} alt="" className="option-icon" /> + </span> + <span className="option-label">{platform}</span> + </Select.Option> + ); + })} + <Select.Option + className="social-option" + key={`platform-${OTHER_SOCIAL_HANDLE_OPTION}`} + value={OTHER_SOCIAL_HANDLE_OPTION} + > + Other... </Select.Option> - ); - })} - <Select.Option - className="social-option" - key={`platform-${OTHER_SOCIAL_HANDLE_OPTION}`} - value={OTHER_SOCIAL_HANDLE_OPTION} - > - Other... - </Select.Option> - </Select> + </Select> + </div> + </div> </div> ); } diff --git a/web/components/config/video-variant-form.tsx b/web/components/config/video-variant-form.tsx index 96152288c..75c862863 100644 --- a/web/components/config/video-variant-form.tsx +++ b/web/components/config/video-variant-form.tsx @@ -156,7 +156,7 @@ export default function VideoVariantForm({ </p> <Row gutter={16}> - <Col xs={12} xl={12}> + <Col sm={24} md={12}> {/* ENCODER PRESET FIELD */} <div className="form-module cpu-usage-container"> <CPUUsageSelector @@ -177,7 +177,7 @@ export default function VideoVariantForm({ </div> </Col> - <Col xs={12} xl={12}> + <Col sm={24} md={12}> {/* VIDEO BITRATE FIELD */} <div className={`form-module bitrate-container ${ diff --git a/web/components/log-table.tsx b/web/components/log-table.tsx index 603d6af56..8223bd398 100644 --- a/web/components/log-table.tsx +++ b/web/components/log-table.tsx @@ -75,7 +75,7 @@ export default function LogTable({ logs, pageSize }: Props) { return ( <div className="logs-section"> - <Title level={2}>Logs + Logs - Chat Messages + Chat Messages

Manage the messages from viewers that show up on your stream.

Check multiple messages to change their visibility to: diff --git a/web/pages/config-video.tsx b/web/pages/config-video.tsx index aa7cf5585..f0042f469 100644 --- a/web/pages/config-video.tsx +++ b/web/pages/config-video.tsx @@ -16,13 +16,13 @@ export default function ConfigVideoSettings() { how it impacts your stream performance.

- -
+ +
- +
diff --git a/web/pages/hardware-info.tsx b/web/pages/hardware-info.tsx index e8d446342..cb5e6c3fb 100644 --- a/web/pages/hardware-info.tsx +++ b/web/pages/hardware-info.tsx @@ -1,5 +1,5 @@ import { BulbOutlined, LaptopOutlined, SaveOutlined } from '@ant-design/icons'; -import { Row } from 'antd'; +import { Row, Typography } from 'antd'; import React, { useEffect, useState } from 'react'; import { fetchData, FETCH_INTERVAL, HARDWARE_STATS } from '../utils/apis'; import Chart from '../components/chart'; @@ -67,6 +67,8 @@ export default function HardwareInfo() { return (
+ Hardware Info +
{ - const { audioPassthrough, videoPassthrough, audioBitrate, videoBitrate, framerate } = setting; + const videoQualitySettings = serverStatusData?.currentBroadcast?.outputSettings?.map(setting => { + const { audioPassthrough, videoPassthrough, audioBitrate, videoBitrate, framerate } = setting; - const audioSetting = audioPassthrough - ? `${streamDetails.audioCodec || 'Unknown'}, ${streamDetails.audioBitrate} kbps` - : `${audioBitrate || 'Unknown'} kbps`; + const audioSetting = audioPassthrough + ? `${streamDetails.audioCodec || 'Unknown'}, ${streamDetails.audioBitrate} kbps` + : `${audioBitrate || 'Unknown'} kbps`; - const videoSetting = videoPassthrough - ? `${streamDetails.videoBitrate || 'Unknown'} kbps, ${streamDetails.framerate} fps ${ - streamDetails.width - } x ${streamDetails.height}` - : `${videoBitrate || 'Unknown'} kbps, ${framerate} fps`; + const videoSetting = videoPassthrough + ? `${streamDetails.videoBitrate || 'Unknown'} kbps, ${streamDetails.framerate} fps ${ + streamDetails.width + } x ${streamDetails.height}` + : `${videoBitrate || 'Unknown'} kbps, ${framerate} fps`; - let settingTitle = 'Outbound Stream Details'; - settingTitle = - videoQualitySettings?.length > 1 ? `${settingTitle} ${index + 1}` : settingTitle; - return ( - - - - - ); - }, - ); + return ( +
+ + +
+ ); + }); // inbound const { viewerCount, sessionPeakViewerCount } = serverStatusData; @@ -118,57 +110,60 @@ export default function Home() { return (
-
- - } - /> - } /> - } - /> +
+ + +
+ } + /> + + + } /> + + + } + /> + + -
-
{videoQualitySettings}
+ +
+ + {videoQualitySettings} + + -
- - + + - - - -
- - - -
-
- + + +
); diff --git a/web/pages/offline-notice.tsx b/web/pages/offline-notice.tsx index a5bb16ab8..28b059616 100644 --- a/web/pages/offline-notice.tsx +++ b/web/pages/offline-notice.tsx @@ -1,5 +1,5 @@ import Link from 'next/link'; -import { Result, Card } from 'antd'; +import { Result, Card, Row, Col } from 'antd'; import { MessageTwoTone, QuestionCircleTwoTone, @@ -55,22 +55,23 @@ export default function Offline({ logs = [] }) { return ( <> -
-
+ +
} title="No stream is active." subTitle="You should start one." /> - -
+ + +
{data.map(item => ( - + ))} - - + + ); diff --git a/web/pages/viewer-info.tsx b/web/pages/viewer-info.tsx index b54b08f47..eaa73a8eb 100644 --- a/web/pages/viewer-info.tsx +++ b/web/pages/viewer-info.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect, useContext } from 'react'; -import { Table, Row } from 'antd'; +import { Table, Row, Typography } from 'antd'; import { formatDistanceToNow } from 'date-fns'; import { UserOutlined } from '@ant-design/icons'; import { SortOrder } from 'antd/lib/table/interface'; @@ -94,7 +94,9 @@ export default function ViewersOverTime() { ]; return ( -
+ <> + Viewer Info +
{online && ( {online &&
row.clientID} />} - + ); } diff --git a/web/styles/ant-overrides.scss b/web/styles/ant-overrides.scss index e275a075f..03e03f381 100644 --- a/web/styles/ant-overrides.scss +++ b/web/styles/ant-overrides.scss @@ -9,6 +9,9 @@ .ant-card, .ant-collapse, .ant-collapse-content, +.ant-statistic, +.ant-statistic-title, +.ant-statistic-content, .ant-table, .ant-table-thead > tr > th, .ant-table-small .ant-table-thead > tr > th, @@ -187,12 +190,10 @@ h3.ant-typography { .ant-card-meta-description { color: var(--white-75); } -.ant-card { - .ant-statistic, - .ant-statistic-title, - .ant-statistic-content { - color: var(--default-text-color); - } +.ant-card-type-inner .ant-card-head { + background-color: var(--black); + color: var(--white-88); + border-color: var(--white-25); } @@ -262,7 +263,6 @@ textarea.ant-input { &:hover, &:focus { background-color: var(--button-focused); - border-color: var(--button-focused); color: var(--white); } } @@ -273,11 +273,16 @@ textarea.ant-input { .ant-btn-primary:hover, .ant-btn-primary:focus { background-color: var(--button-focused); - border-color: var(--button-focused); + color: var(--white); } .ant-btn.ant-btn-primary:hover { border-color: var(--white); } +.ant-btn:focus, +.ant-btn-primary:focus { + border-color: var(--white); +} + .ant-btn-primary[disabled] { background-color: var(--white-25); border-color: var(--white-25); @@ -375,9 +380,29 @@ textarea.ant-input { // SELECT .ant-select-dropdown { - background-color: var(--gray); + background-color: var(--black); +} +.ant-select-single:not(.ant-select-customize-input) .ant-select-selector { + background-color: var(--black); + border-color: var(--owncast-purple-50); +} +.ant-select-arrow { + color: var(--owncast-purple); +} +.ant-select-selection-placeholder { + color: var(--owncast-purple-50); +} +.ant-select { + color: var(--white); +} +.ant-select-item { + background-color: var(--gray-dark); + color: var(--white-88); +} +.ant-select-item-option-active:not(.ant-select-item-option-disabled) { + background-color: var(--gray); + color: var(--white-75); } - // SLIDER // .ant-slider-with-marks { @@ -460,13 +485,12 @@ textarea.ant-input { - // ANT TAGS - -.ant-tag-orange { - background: #fa8c16; - color: #fff7e6; - border-color: #ffd591; +.ant-tag-red, +.ant-tag-orange, +.ant-tag-green, +.ant-tag-blue { + background-color: var(--black); } diff --git a/web/styles/config-public-details.scss b/web/styles/config-public-details.scss index f1e50acbf..c97ad7e4d 100644 --- a/web/styles/config-public-details.scss +++ b/web/styles/config-public-details.scss @@ -69,4 +69,8 @@ height: 6em !important; } } -} \ No newline at end of file +} + +.other-field-container { + margin: .5em 0; +} diff --git a/web/styles/config-socialhandles.scss b/web/styles/config-socialhandles.scss index 9e880c935..188d0d92a 100644 --- a/web/styles/config-socialhandles.scss +++ b/web/styles/config-socialhandles.scss @@ -31,7 +31,7 @@ flex-direction: row; align-items: center; justify-content: flex-start; - color: rgba(255,255,255,.85); + color: var(--white-75); .option-icon { height: 2em; diff --git a/web/styles/home.scss b/web/styles/home.scss index 68fd41559..dbb9f9643 100644 --- a/web/styles/home.scss +++ b/web/styles/home.scss @@ -1,121 +1,45 @@ .home-container { max-width: 1000px; - .statistics-list { - li { - margin-left: -.5em; - } - } - - .section { - margin: 1rem 0; - - .ant-statistic-content { - font-size: 1rem; - } - } - .online-status-section { - > .ant-card { - box-shadow: 0px 1px 10px 2px rgba(0, 22, 40, 0.1); + margin-bottom: 1em; + .online-details-card { + border-color: var(--online-color); } - - .ant-card-head { - background-color: #40b246; - border-color: #ccc; - color:#fff; - @media (prefers-color-scheme: dark) { - background-color: #2a762e; - border-bottom-color: black; - } - } - .ant-card-head-title { - font-size: .88rem; + .ant-statistic { + text-align: center; } .ant-statistic-title { - font-size: .88rem; - } - .ant-card-body { - display: flex; - flex-direction: row; - justify-content: center; - align-items: flex-start; - .ant-statistic { - width: 30%; - text-align: center; - margin: 0 1rem; - } + color: var(--white-50); } } + .ant-card-head { + color: var(--online-color); + } - .stream-details-section { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: flex-start; - width: 100%; - .details { - width: 49%; - - > .ant-card { - margin-bottom: 1rem; - } - - .ant-card-head { - background-color: #ccd; - color: black; - @media (prefers-color-scheme: dark) { - background-color: #000; - color: #ccd; - } - - } + .stream-details-item-container { + margin: 1em 0; + &:first-of-type { + margin-top: 0; } - .server-detail { - .ant-card-body { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: flex-start; - - .ant-card { - width: 45%; - text-align: center; - } - } - .ant-card-head { - background-color: #669; - color: #fff; - } + } + .ant-statistic.stream-details-item { + background-color: var(--black-50); + padding: 1em; + .ant-statistic-title { + color: var(--blue); + } + .ant-statistic-content { + font-size: 1.25em; + white-space: nowrap; } } - - @media (max-width: 800px) { - .online-status-section{ - .ant-card-body { - display: flex; - flex-direction: column; - justify-content: center; - align-items: flex-start; - .ant-statistic { - width: auto; - text-align: left; - margin: 1em; - } - } - } - - .stream-details-section { - display: flex; - flex-direction: column; - justify-content: center; - align-items: flex-start; - width: 100%; - .details { - width: 100%; - } + .outbound-details, + .inbound-details { + >.ant-card-bordered { + border-color: rgba(255,255,255,.1); } } } @@ -124,14 +48,7 @@ .offline-content { max-width: 1000px; - - display: flex; - flex-direction: row; - justify-content: center; - align-items: flex-start; - width: 100%; .logo-section { - width: 50%; .ant-result-title { font-size: 2rem; } @@ -144,36 +61,20 @@ } } .list-section { - width: 50%; + background-color: var(--container-bg-color-alt); + border-radius: var(--container-border-radius); + padding: 1em; + > .ant-card { - margin-bottom: 1rem; - .ant-card-head { - background-color: #dde; - } - .ant-card-head-title { - font-size: 1rem; - } + background-color: var(--black); + margin-bottom: 1em; .ant-card-meta-avatar { margin-top: .25rem; svg { - height: 1.25rem; - width: 1.25rem; + height: 1.5em; + width: 1.5em; } } - .ant-card-body { - font-size: .88rem; - } } } - - @media (max-width: 800px) { - flex-direction: column; - justify-content: flex-start; - align-items: flex-start; - .logo-section, - .list-section { - width: 100% - } - - } } diff --git a/web/styles/main-layout.scss b/web/styles/main-layout.scss index d36045c44..a17425a3e 100644 --- a/web/styles/main-layout.scss +++ b/web/styles/main-layout.scss @@ -84,7 +84,7 @@ } } } - .online { + &.online { .online-status-indicator { .status-icon { svg { @@ -92,6 +92,7 @@ } } .status-label { + white-space: nowrap; color: var(--online-color); } } @@ -111,8 +112,21 @@ align-items: center; margin-bottom: 0; + .ant-input-affix-wrapper { + border-color: var(--owncast-purple-50); + } + input.ant-input { + &::placeholder { + color: var(--owncast-purple); + text-align: center; + } + } + .input-side { width: 400px; + @media (max-width: 800px) { + width: auto; + } } .label-side {