mirror of
https://github.com/owncast/owncast.git
synced 2024-10-10 19:16:02 +00:00
Draft: Mark strings for translation. (#3458)
* Mark strings for translation. * Mark up strings for translation * fix(web): fix linter warnings --------- Co-authored-by: Le fractal <17422-fractal@users.noreply.framagit.org> Co-authored-by: Gabe Kangas <gabek@real-ity.com>
This commit is contained in:
parent
e2b9db1e66
commit
7013c214d6
@ -1,3 +1,4 @@
|
|||||||
|
import { useTranslation } from 'next-export-i18n';
|
||||||
import { Card, Col, Row, Typography } from 'antd';
|
import { Card, Col, Row, Typography } from 'antd';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { FC, useContext } from 'react';
|
import { FC, useContext } from 'react';
|
||||||
@ -43,6 +44,7 @@ export type OfflineProps = {
|
|||||||
export const Offline: FC<OfflineProps> = ({ logs = [], config }) => {
|
export const Offline: FC<OfflineProps> = ({ logs = [], config }) => {
|
||||||
const serverStatusData = useContext(ServerStatusContext);
|
const serverStatusData = useContext(ServerStatusContext);
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
const { serverConfig } = serverStatusData || {};
|
const { serverConfig } = serverStatusData || {};
|
||||||
const { rtmpServerPort, streamKeyOverridden } = serverConfig;
|
const { rtmpServerPort, streamKeyOverridden } = serverConfig;
|
||||||
const instanceUrl = global.window?.location.hostname || '';
|
const instanceUrl = global.window?.location.hostname || '';
|
||||||
@ -55,7 +57,7 @@ export const Offline: FC<OfflineProps> = ({ logs = [], config }) => {
|
|||||||
const data = [
|
const data = [
|
||||||
{
|
{
|
||||||
icon: <BookTwoTone twoToneColor="#6f42c1" />,
|
icon: <BookTwoTone twoToneColor="#6f42c1" />,
|
||||||
title: 'Use your broadcasting software',
|
title: t('Use your broadcasting software'),
|
||||||
content: (
|
content: (
|
||||||
<div>
|
<div>
|
||||||
<a
|
<a
|
||||||
@ -63,12 +65,13 @@ export const Offline: FC<OfflineProps> = ({ logs = [], config }) => {
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
Learn how to point your existing software to your new server and start streaming your
|
{t(
|
||||||
content.
|
'Learn how to point your existing software to your new server and start streaming your content.',
|
||||||
|
)}
|
||||||
</a>
|
</a>
|
||||||
<div className="stream-info-container">
|
<div className="stream-info-container">
|
||||||
<Text strong className="stream-info-label">
|
<Text strong className="stream-info-label">
|
||||||
Streaming URL:
|
{t('Streaming URL:')}
|
||||||
</Text>
|
</Text>
|
||||||
{rtmpURL && (
|
{rtmpURL && (
|
||||||
<Paragraph className="stream-info-box" copyable>
|
<Paragraph className="stream-info-box" copyable>
|
||||||
@ -76,14 +79,14 @@ export const Offline: FC<OfflineProps> = ({ logs = [], config }) => {
|
|||||||
</Paragraph>
|
</Paragraph>
|
||||||
)}
|
)}
|
||||||
<Text strong className="stream-info-label">
|
<Text strong className="stream-info-label">
|
||||||
Streaming Keys:
|
{t('Streaming Keys:')}
|
||||||
</Text>
|
</Text>
|
||||||
<Text strong className="stream-info-box">
|
<Text strong className="stream-info-box">
|
||||||
{!streamKeyOverridden ? (
|
{!streamKeyOverridden ? (
|
||||||
<Link href="/admin/config/server"> View </Link>
|
<Link href="/admin/config/server"> {t('View')} </Link>
|
||||||
) : (
|
) : (
|
||||||
<span style={{ paddingLeft: '10px', fontWeight: 'normal' }}>
|
<span style={{ paddingLeft: '10px', fontWeight: 'normal' }}>
|
||||||
Overridden via command line.
|
{t('Overridden via command line.')}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
@ -93,7 +96,7 @@ export const Offline: FC<OfflineProps> = ({ logs = [], config }) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <PlaySquareTwoTone twoToneColor="#f9826c" />,
|
icon: <PlaySquareTwoTone twoToneColor="#f9826c" />,
|
||||||
title: 'Embed your video onto other sites',
|
title: t('Embed your video onto other sites'),
|
||||||
content: (
|
content: (
|
||||||
<div>
|
<div>
|
||||||
<a
|
<a
|
||||||
@ -101,7 +104,7 @@ export const Offline: FC<OfflineProps> = ({ logs = [], config }) => {
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
Learn how you can add your Owncast stream to other sites you control.
|
{t('Learn how you can add your Owncast stream to other sites you control.')}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
@ -111,19 +114,19 @@ export const Offline: FC<OfflineProps> = ({ logs = [], config }) => {
|
|||||||
if (!config?.chatDisabled) {
|
if (!config?.chatDisabled) {
|
||||||
data.push({
|
data.push({
|
||||||
icon: <MessageTwoTone twoToneColor="#0366d6" />,
|
icon: <MessageTwoTone twoToneColor="#0366d6" />,
|
||||||
title: 'Chat is disabled',
|
title: t('Chat is disabled'),
|
||||||
content: <span>Chat will continue to be disabled until you begin a live stream.</span>,
|
content: <span>{t('Chat will continue to be disabled until you begin a live stream.')}</span>,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config?.yp?.enabled) {
|
if (!config?.yp?.enabled) {
|
||||||
data.push({
|
data.push({
|
||||||
icon: <ProfileTwoTone twoToneColor="#D18BFE" />,
|
icon: <ProfileTwoTone twoToneColor="#D18BFE" />,
|
||||||
title: 'Find an audience on the Owncast Directory',
|
title: t('Find an audience on the Owncast Directory'),
|
||||||
content: (
|
content: (
|
||||||
<div>
|
<div>
|
||||||
List yourself in the Owncast Directory and show off your stream. Enable it in{' '}
|
{t('List yourself in the Owncast Directory and show off your stream. Enable it in')}{' '}
|
||||||
<Link href="/admin/config/general/">settings.</Link>
|
<Link href="/admin/config/general/">{t('settings.')}</Link>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
@ -132,12 +135,13 @@ export const Offline: FC<OfflineProps> = ({ logs = [], config }) => {
|
|||||||
if (!config?.federation?.enabled) {
|
if (!config?.federation?.enabled) {
|
||||||
data.push({
|
data.push({
|
||||||
icon: <img alt="fediverse" width="20px" src="/img/fediverse-color.png" />,
|
icon: <img alt="fediverse" width="20px" src="/img/fediverse-color.png" />,
|
||||||
title: 'Add your Owncast instance to the Fediverse',
|
title: t('Add your Owncast instance to the Fediverse'),
|
||||||
content: (
|
content: (
|
||||||
<div>
|
<div>
|
||||||
<Link href="/admin/config-federation/">Enable Owncast social</Link> features to have your
|
<Link href="/admin/config-federation/">{t('Enable Owncast social features')}</Link>{' '}
|
||||||
instance join the Fediverse, allowing people to follow, share and engage with your live
|
{t(
|
||||||
stream.
|
'to have your instance join the Fediverse, allowing people to follow, share and engage with your live stream.',
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
@ -152,8 +156,8 @@ export const Offline: FC<OfflineProps> = ({ logs = [], config }) => {
|
|||||||
<OwncastLogo variant="simple" />
|
<OwncastLogo variant="simple" />
|
||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
<Title level={2}>No stream is active</Title>
|
<Title level={2}>{t('No stream is active')}</Title>
|
||||||
<p>You should start one.</p>
|
<p>{t('You should start one.')}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { ColumnsType } from 'antd/es/table';
|
|||||||
import { format } from 'date-fns';
|
import { format } from 'date-fns';
|
||||||
|
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
|
import { useTranslation } from 'next-export-i18n';
|
||||||
import { MessageType } from '../../../types/chat';
|
import { MessageType } from '../../../types/chat';
|
||||||
import {
|
import {
|
||||||
CHAT_HISTORY,
|
CHAT_HISTORY,
|
||||||
@ -61,6 +62,7 @@ export default function Chat() {
|
|||||||
const [bulkProcessing, setBulkProcessing] = useState(false);
|
const [bulkProcessing, setBulkProcessing] = useState(false);
|
||||||
const [bulkOutcome, setBulkOutcome] = useState(null);
|
const [bulkOutcome, setBulkOutcome] = useState(null);
|
||||||
const [bulkAction, setBulkAction] = useState('');
|
const [bulkAction, setBulkAction] = useState('');
|
||||||
|
const { t } = useTranslation();
|
||||||
let outcomeTimeout = null;
|
let outcomeTimeout = null;
|
||||||
let chatReloadInterval = null;
|
let chatReloadInterval = null;
|
||||||
|
|
||||||
@ -153,7 +155,7 @@ export default function Chat() {
|
|||||||
|
|
||||||
const chatColumns: ColumnsType<MessageType> = [
|
const chatColumns: ColumnsType<MessageType> = [
|
||||||
{
|
{
|
||||||
title: 'Time',
|
title: t('Time'),
|
||||||
dataIndex: 'timestamp',
|
dataIndex: 'timestamp',
|
||||||
key: 'timestamp',
|
key: 'timestamp',
|
||||||
className: 'timestamp-col',
|
className: 'timestamp-col',
|
||||||
@ -166,7 +168,7 @@ export default function Chat() {
|
|||||||
width: 90,
|
width: 90,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'User',
|
title: t('User'),
|
||||||
dataIndex: 'user',
|
dataIndex: 'user',
|
||||||
key: 'user',
|
key: 'user',
|
||||||
className: 'name-col',
|
className: 'name-col',
|
||||||
@ -182,7 +184,7 @@ export default function Chat() {
|
|||||||
width: 110,
|
width: 110,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Message',
|
title: t('Message'),
|
||||||
dataIndex: 'body',
|
dataIndex: 'body',
|
||||||
key: 'body',
|
key: 'body',
|
||||||
className: 'message-col',
|
className: 'message-col',
|
||||||
@ -201,8 +203,8 @@ export default function Chat() {
|
|||||||
key: 'hiddenAt',
|
key: 'hiddenAt',
|
||||||
className: 'toggle-col',
|
className: 'toggle-col',
|
||||||
filters: [
|
filters: [
|
||||||
{ text: 'Visible messages', value: true },
|
{ text: t('Visible messages'), value: true },
|
||||||
{ text: 'Hidden messages', value: false },
|
{ text: t('Hidden messages'), value: false },
|
||||||
],
|
],
|
||||||
onFilter: (value, record) => record.visible === value,
|
onFilter: (value, record) => record.visible === value,
|
||||||
render: (hiddenAt, record) => (
|
render: (hiddenAt, record) => (
|
||||||
@ -219,10 +221,12 @@ export default function Chat() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="chat-messages">
|
<div className="chat-messages">
|
||||||
<Title>Chat Messages</Title>
|
<Title>{t('Chat Messages')}</Title>
|
||||||
<p>Manage the messages from viewers that show up on your stream.</p>
|
<p>{t('Manage the messages from viewers that show up on your stream.')}</p>
|
||||||
<div className={bulkDivClasses}>
|
<div className={bulkDivClasses}>
|
||||||
<span className="label">Check multiple messages to change their visibility to: </span>
|
<span className="label">
|
||||||
|
{t('Check multiple messages to change their visibility to:')}{' '}
|
||||||
|
</span>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
@ -234,7 +238,7 @@ export default function Chat() {
|
|||||||
disabled={!selectedRowKeys.length || (bulkAction && bulkAction !== 'show')}
|
disabled={!selectedRowKeys.length || (bulkAction && bulkAction !== 'show')}
|
||||||
onClick={handleSubmitBulkShow}
|
onClick={handleSubmitBulkShow}
|
||||||
>
|
>
|
||||||
Show
|
{t('Show')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
@ -246,7 +250,7 @@ export default function Chat() {
|
|||||||
disabled={!selectedRowKeys.length || (bulkAction && bulkAction !== 'hide')}
|
disabled={!selectedRowKeys.length || (bulkAction && bulkAction !== 'hide')}
|
||||||
onClick={handleSubmitBulkHide}
|
onClick={handleSubmitBulkHide}
|
||||||
>
|
>
|
||||||
Hide
|
{t('Hide')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<Table
|
<Table
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React, { useState, useEffect, useContext, ReactElement } from 'react';
|
import React, { useState, useEffect, useContext, ReactElement } from 'react';
|
||||||
import { Tabs } from 'antd';
|
import { Tabs } from 'antd';
|
||||||
|
import { useTranslation } from 'next-export-i18n';
|
||||||
import { ServerStatusContext } from '../../../utils/server-status-context';
|
import { ServerStatusContext } from '../../../utils/server-status-context';
|
||||||
import {
|
import {
|
||||||
CONNECTED_CLIENTS,
|
CONNECTED_CLIENTS,
|
||||||
@ -24,6 +25,7 @@ export default function ChatUsers() {
|
|||||||
const [ipBans, setIPBans] = useState([]);
|
const [ipBans, setIPBans] = useState([]);
|
||||||
const [clients, setClients] = useState([]);
|
const [clients, setClients] = useState([]);
|
||||||
const [moderators, setModerators] = useState([]);
|
const [moderators, setModerators] = useState([]);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const getInfo = async () => {
|
const getInfo = async () => {
|
||||||
try {
|
try {
|
||||||
@ -71,34 +73,50 @@ export default function ChatUsers() {
|
|||||||
<>
|
<>
|
||||||
<ClientTable data={clients} />
|
<ClientTable data={clients} />
|
||||||
<p className="description">
|
<p className="description">
|
||||||
Visit the{' '}
|
{t('Visit the')}{' '}
|
||||||
<a
|
<a
|
||||||
href="https://owncast.online/docs/viewers/?source=admin"
|
href="https://owncast.online/docs/viewers/?source=admin"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
documentation
|
{t('documentation')}
|
||||||
</a>{' '}
|
</a>{' '}
|
||||||
to configure additional details about your viewers.
|
{t('to configure additional details about your viewers.')}
|
||||||
</p>
|
</p>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<p className="description">
|
<p className="description">
|
||||||
When a stream is active and chat is enabled, connected chat clients will be displayed here.
|
{t(
|
||||||
|
'When a stream is active and chat is enabled, connected chat clients will be displayed here.',
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
|
|
||||||
const connectedUserTabTitle = (
|
const connectedUserTabTitle = (
|
||||||
<span>Connected {online ? `(${clients.length})` : '(offline)'}</span>
|
<span>
|
||||||
|
{t('Connected')} ({online ? clients.length : t('offline')})
|
||||||
|
</span>
|
||||||
);
|
);
|
||||||
|
|
||||||
const bannedUsersTabTitle = <span>Banned Users ({disabledUsers.length})</span>;
|
const bannedUsersTabTitle = (
|
||||||
|
<span>
|
||||||
|
{t('Banned Users')} ({disabledUsers.length})
|
||||||
|
</span>
|
||||||
|
);
|
||||||
const bannedUsersTable = <UserTable data={disabledUsers} />;
|
const bannedUsersTable = <UserTable data={disabledUsers} />;
|
||||||
|
|
||||||
const bannedIPTabTitle = <span>IP Bans ({ipBans.length})</span>;
|
const bannedIPTabTitle = (
|
||||||
|
<span>
|
||||||
|
{t('IP Bans')} ({ipBans.length})
|
||||||
|
</span>
|
||||||
|
);
|
||||||
const bannedIpTable = <BannedIPsTable data={ipBans} />;
|
const bannedIpTable = <BannedIPsTable data={ipBans} />;
|
||||||
|
|
||||||
const moderatorUsersTabTitle = <span>Moderators ({moderators.length})</span>;
|
const moderatorUsersTabTitle = (
|
||||||
|
<span>
|
||||||
|
{t('Moderators')} ({moderators.length})
|
||||||
|
</span>
|
||||||
|
);
|
||||||
const moderatorTable = <UserTable data={moderators} />;
|
const moderatorTable = <UserTable data={moderators} />;
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import React, { useState, useEffect, useContext, ReactElement } from 'react';
|
|||||||
import { Row, Col, Typography, MenuProps, Dropdown, Spin, Alert } from 'antd';
|
import { Row, Col, Typography, MenuProps, Dropdown, Spin, Alert } from 'antd';
|
||||||
import { getUnixTime, sub } from 'date-fns';
|
import { getUnixTime, sub } from 'date-fns';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
|
import { useTranslation } from 'next-export-i18n';
|
||||||
import { Chart } from '../../components/admin/Chart';
|
import { Chart } from '../../components/admin/Chart';
|
||||||
import { StatisticItem } from '../../components/admin/StatisticItem';
|
import { StatisticItem } from '../../components/admin/StatisticItem';
|
||||||
import { ViewerTable } from '../../components/admin/ViewerTable';
|
import { ViewerTable } from '../../components/admin/ViewerTable';
|
||||||
@ -26,6 +27,7 @@ const FETCH_INTERVAL = 60 * 1000; // 1 min
|
|||||||
|
|
||||||
export default function ViewersOverTime() {
|
export default function ViewersOverTime() {
|
||||||
const context = useContext(ServerStatusContext);
|
const context = useContext(ServerStatusContext);
|
||||||
|
const { t } = useTranslation();
|
||||||
const { online, broadcaster, viewerCount, overallPeakViewerCount, sessionPeakViewerCount } =
|
const { online, broadcaster, viewerCount, overallPeakViewerCount, sessionPeakViewerCount } =
|
||||||
context || {};
|
context || {};
|
||||||
let streamStart;
|
let streamStart;
|
||||||
@ -34,13 +36,13 @@ export default function ViewersOverTime() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const times = [
|
const times = [
|
||||||
{ title: 'Current stream', start: streamStart },
|
{ title: t('Current stream'), start: streamStart },
|
||||||
{ title: 'Last 12 hours', start: sub(new Date(), { hours: 12 }) },
|
{ title: t('Last 12 hours'), start: sub(new Date(), { hours: 12 }) },
|
||||||
{ title: 'Last 24 hours', start: sub(new Date(), { hours: 24 }) },
|
{ title: t('Last 24 hours'), start: sub(new Date(), { hours: 24 }) },
|
||||||
{ title: 'Last 7 days', start: sub(new Date(), { days: 7 }) },
|
{ title: t('Last 7 days'), start: sub(new Date(), { days: 7 }) },
|
||||||
{ title: 'Last 30 days', start: sub(new Date(), { days: 30 }) },
|
{ title: t('Last 30 days'), start: sub(new Date(), { days: 30 }) },
|
||||||
{ title: 'Last 3 months', start: sub(new Date(), { months: 3 }) },
|
{ title: t('Last 3 months'), start: sub(new Date(), { months: 3 }) },
|
||||||
{ title: 'Last 6 months', start: sub(new Date(), { months: 6 }) },
|
{ title: t('Last 6 months'), start: sub(new Date(), { months: 6 }) },
|
||||||
];
|
];
|
||||||
|
|
||||||
const [loadingChart, setLoadingChart] = useState(true);
|
const [loadingChart, setLoadingChart] = useState(true);
|
||||||
@ -94,13 +96,13 @@ export default function ViewersOverTime() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Typography.Title>Viewer Info</Typography.Title>
|
<Typography.Title>{t('Viewer Info')}</Typography.Title>
|
||||||
<br />
|
<br />
|
||||||
<Row gutter={[16, 16]} justify="space-around">
|
<Row gutter={[16, 16]} justify="space-around">
|
||||||
{online && (
|
{online && (
|
||||||
<Col span={8} md={8}>
|
<Col span={8} md={8}>
|
||||||
<StatisticItem
|
<StatisticItem
|
||||||
title="Current viewers"
|
title={t('Current viewers')}
|
||||||
value={viewerCount.toString()}
|
value={viewerCount.toString()}
|
||||||
prefix={<UserOutlined />}
|
prefix={<UserOutlined />}
|
||||||
/>
|
/>
|
||||||
@ -108,14 +110,14 @@ export default function ViewersOverTime() {
|
|||||||
)}
|
)}
|
||||||
<Col md={online ? 8 : 12}>
|
<Col md={online ? 8 : 12}>
|
||||||
<StatisticItem
|
<StatisticItem
|
||||||
title={online ? 'Max viewers this stream' : 'Max viewers last stream'}
|
title={online ? t('Max viewers this stream') : t('Max viewers last stream')}
|
||||||
value={sessionPeakViewerCount.toString()}
|
value={sessionPeakViewerCount.toString()}
|
||||||
prefix={<UserOutlined />}
|
prefix={<UserOutlined />}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
<Col md={online ? 8 : 12}>
|
<Col md={online ? 8 : 12}>
|
||||||
<StatisticItem
|
<StatisticItem
|
||||||
title="All-time max viewers"
|
title={t('max viewers')}
|
||||||
value={overallPeakViewerCount.toString()}
|
value={overallPeakViewerCount.toString()}
|
||||||
prefix={<UserOutlined />}
|
prefix={<UserOutlined />}
|
||||||
/>
|
/>
|
||||||
@ -125,8 +127,8 @@ export default function ViewersOverTime() {
|
|||||||
<Alert
|
<Alert
|
||||||
style={{ marginTop: '10px' }}
|
style={{ marginTop: '10px' }}
|
||||||
banner
|
banner
|
||||||
message="Please wait"
|
message={t('Please wait')}
|
||||||
description="No viewer data has been collected yet."
|
description={t('No viewer data has been collected yet.')}
|
||||||
type="info"
|
type="info"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -134,7 +136,7 @@ export default function ViewersOverTime() {
|
|||||||
<Spin spinning={!viewerInfo.length || loadingChart}>
|
<Spin spinning={!viewerInfo.length || loadingChart}>
|
||||||
{viewerInfo.length > 0 && (
|
{viewerInfo.length > 0 && (
|
||||||
<Chart
|
<Chart
|
||||||
title="Viewers"
|
title={t('Viewers')}
|
||||||
data={viewerInfo}
|
data={viewerInfo}
|
||||||
color="#2087E2"
|
color="#2087E2"
|
||||||
unit="viewers"
|
unit="viewers"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user