Fix livestream countdown i18n
Ticket: 1228 ## Code changes - Pass through `getTimeAgoStr` so that the value gets localized. - Pass the simpler `number` around instead of the `moment` object for better memoization. ## Notable differences Due to how `getTimeAgoStr` is written, we now get to see the time actually counting down, vs "in a few seconds" currently in production. I think the counting-down behavior was the original intentional, since a 1s timer was used (otherwise, a 1-minute timer could be used) ... or maybe not since streams may not start on the dot.
This commit is contained in:
parent
321a6901b4
commit
3b98f73a0f
4 changed files with 41 additions and 30 deletions
|
@ -30,7 +30,7 @@ type Props = {
|
||||||
claim: ?StreamClaim,
|
claim: ?StreamClaim,
|
||||||
hideComments: boolean,
|
hideComments: boolean,
|
||||||
isCurrentClaimLive: boolean,
|
isCurrentClaimLive: boolean,
|
||||||
release: any,
|
releaseTimeMs: number,
|
||||||
showLivestream: boolean,
|
showLivestream: boolean,
|
||||||
showScheduledInfo: boolean,
|
showScheduledInfo: boolean,
|
||||||
uri: string,
|
uri: string,
|
||||||
|
@ -44,7 +44,7 @@ export default function LivestreamLayout(props: Props) {
|
||||||
claim,
|
claim,
|
||||||
hideComments,
|
hideComments,
|
||||||
isCurrentClaimLive,
|
isCurrentClaimLive,
|
||||||
release,
|
releaseTimeMs,
|
||||||
showLivestream,
|
showLivestream,
|
||||||
showScheduledInfo,
|
showScheduledInfo,
|
||||||
uri,
|
uri,
|
||||||
|
@ -72,7 +72,7 @@ export default function LivestreamLayout(props: Props) {
|
||||||
<div className={PRIMARY_PLAYER_WRAPPER_CLASS}>
|
<div className={PRIMARY_PLAYER_WRAPPER_CLASS}>
|
||||||
<FileRenderInitiator
|
<FileRenderInitiator
|
||||||
uri={claim.canonical_url}
|
uri={claim.canonical_url}
|
||||||
customAction={showScheduledInfo && <LivestreamScheduledInfo release={release} />}
|
customAction={showScheduledInfo && <LivestreamScheduledInfo releaseTimeMs={releaseTimeMs} />}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -5,29 +5,36 @@ import * as ICONS from 'constants/icons';
|
||||||
import Icon from 'component/common/icon';
|
import Icon from 'component/common/icon';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import 'scss/component/livestream-scheduled-info.scss';
|
import 'scss/component/livestream-scheduled-info.scss';
|
||||||
|
import I18nMessage from 'component/i18nMessage';
|
||||||
|
import { getTimeAgoStr } from 'util/time';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
release: any,
|
releaseTimeMs: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function LivestreamScheduledInfo(props: Props) {
|
export default function LivestreamScheduledInfo(props: Props) {
|
||||||
const { release } = props;
|
const { releaseTimeMs } = props;
|
||||||
const [startDateFromNow, setStartDateFromNow] = useState(release.fromNow());
|
const [startDateFromNow, setStartDateFromNow] = useState('');
|
||||||
const [inPast, setInPast] = useState('pending');
|
const [inPast, setInPast] = useState('pending');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const calcTime = () => {
|
const calcTime = () => {
|
||||||
setStartDateFromNow(release.fromNow());
|
const zeroDurationStr = '---';
|
||||||
setInPast(release.isBefore(moment()));
|
const timeAgoStr = getTimeAgoStr(releaseTimeMs, true, true, zeroDurationStr);
|
||||||
};
|
|
||||||
calcTime();
|
|
||||||
const intervalId = setInterval(calcTime, 1000);
|
|
||||||
return () => {
|
|
||||||
clearInterval(intervalId);
|
|
||||||
};
|
|
||||||
}, [release]);
|
|
||||||
|
|
||||||
const startDate = release.format('MMMM Do, h:mm a');
|
if (timeAgoStr === zeroDurationStr) {
|
||||||
|
setInPast(true);
|
||||||
|
} else {
|
||||||
|
setStartDateFromNow(timeAgoStr);
|
||||||
|
setInPast(releaseTimeMs < Date.now());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const intervalId = setInterval(calcTime, 1000);
|
||||||
|
return () => clearInterval(intervalId);
|
||||||
|
}, [releaseTimeMs]);
|
||||||
|
|
||||||
|
const startDate = moment(releaseTimeMs).format('MMMM Do, h:mm a');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
inPast !== 'pending' && (
|
inPast !== 'pending' && (
|
||||||
|
@ -36,7 +43,7 @@ export default function LivestreamScheduledInfo(props: Props) {
|
||||||
<p className={'livestream-scheduled__time'}>
|
<p className={'livestream-scheduled__time'}>
|
||||||
{!inPast && (
|
{!inPast && (
|
||||||
<span>
|
<span>
|
||||||
<span>{__('Live %time_date%', { time_date: startDateFromNow })}</span>
|
<I18nMessage tokens={{ time_date: startDateFromNow }}>Live %time_date%</I18nMessage>
|
||||||
<br />
|
<br />
|
||||||
<span className={'livestream-scheduled__date'}>{startDate}</span>
|
<span className={'livestream-scheduled__date'}>{startDate}</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -59,12 +59,11 @@ export default function LivestreamPage(props: Props) {
|
||||||
const isCurrentClaimLive = isChannelBroadcasting && activeLivestreamForChannel.claimId === claimId;
|
const isCurrentClaimLive = isChannelBroadcasting && activeLivestreamForChannel.claimId === claimId;
|
||||||
const livestreamChannelId = channelClaimId || '';
|
const livestreamChannelId = channelClaimId || '';
|
||||||
|
|
||||||
// $FlowFixMe
|
const releaseTime: moment = moment.unix(claim?.value?.release_time || 0);
|
||||||
const release = moment.unix(claim.value.release_time);
|
|
||||||
const stringifiedClaim = JSON.stringify(claim);
|
const stringifiedClaim = JSON.stringify(claim);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
// TODO: This should not be needed one we unify the livestream player (?)
|
// TODO: This should not be needed once we unify the livestream player (?)
|
||||||
analytics.playerLoadedEvent('livestream', false);
|
analytics.playerLoadedEvent('livestream', false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
@ -88,10 +87,10 @@ export default function LivestreamPage(props: Props) {
|
||||||
}, [channelUrl, claim, doCommentSocketConnect, uri]);
|
}, [channelUrl, claim, doCommentSocketConnect, uri]);
|
||||||
|
|
||||||
const claimReleaseStartingSoonStatic = () =>
|
const claimReleaseStartingSoonStatic = () =>
|
||||||
release.isBetween(moment(), moment().add(LIVESTREAM_STARTS_SOON_BUFFER, 'minutes'));
|
releaseTime.isBetween(moment(), moment().add(LIVESTREAM_STARTS_SOON_BUFFER, 'minutes'));
|
||||||
|
|
||||||
const claimReleaseStartedRecentlyStatic = () =>
|
const claimReleaseStartedRecentlyStatic = () =>
|
||||||
release.isBetween(moment().subtract(LIVESTREAM_STARTED_RECENTLY_BUFFER, 'minutes'), moment());
|
releaseTime.isBetween(moment().subtract(LIVESTREAM_STARTED_RECENTLY_BUFFER, 'minutes'), moment());
|
||||||
|
|
||||||
// Find out current channels status + active live claim every 30 seconds (or 15 if not live)
|
// Find out current channels status + active live claim every 30 seconds (or 15 if not live)
|
||||||
const fasterPoll = !isCurrentClaimLive && (claimReleaseStartingSoonStatic() || claimReleaseStartedRecentlyStatic());
|
const fasterPoll = !isCurrentClaimLive && (claimReleaseStartingSoonStatic() || claimReleaseStartedRecentlyStatic());
|
||||||
|
@ -105,14 +104,14 @@ export default function LivestreamPage(props: Props) {
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!isInitialized) return;
|
if (!isInitialized) return;
|
||||||
|
|
||||||
const claimReleaseInFuture = () => release.isAfter();
|
const claimReleaseInFuture = () => releaseTime.isAfter();
|
||||||
const claimReleaseInPast = () => release.isBefore();
|
const claimReleaseInPast = () => releaseTime.isBefore();
|
||||||
|
|
||||||
const claimReleaseStartingSoon = () =>
|
const claimReleaseStartingSoon = () =>
|
||||||
release.isBetween(moment(), moment().add(LIVESTREAM_STARTS_SOON_BUFFER, 'minutes'));
|
releaseTime.isBetween(moment(), moment().add(LIVESTREAM_STARTS_SOON_BUFFER, 'minutes'));
|
||||||
|
|
||||||
const claimReleaseStartedRecently = () =>
|
const claimReleaseStartedRecently = () =>
|
||||||
release.isBetween(moment().subtract(LIVESTREAM_STARTED_RECENTLY_BUFFER, 'minutes'), moment());
|
releaseTime.isBetween(moment().subtract(LIVESTREAM_STARTED_RECENTLY_BUFFER, 'minutes'), moment());
|
||||||
|
|
||||||
const checkShowLivestream = () =>
|
const checkShowLivestream = () =>
|
||||||
isChannelBroadcasting &&
|
isChannelBroadcasting &&
|
||||||
|
@ -141,7 +140,7 @@ export default function LivestreamPage(props: Props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => clearInterval(intervalId);
|
return () => clearInterval(intervalId);
|
||||||
}, [chatDisabled, isChannelBroadcasting, release, isCurrentClaimLive, isInitialized]);
|
}, [chatDisabled, isChannelBroadcasting, releaseTime, isCurrentClaimLive, isInitialized]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (uri && stringifiedClaim) {
|
if (uri && stringifiedClaim) {
|
||||||
|
@ -181,7 +180,7 @@ export default function LivestreamPage(props: Props) {
|
||||||
<LivestreamLayout
|
<LivestreamLayout
|
||||||
uri={uri}
|
uri={uri}
|
||||||
hideComments={hideComments}
|
hideComments={hideComments}
|
||||||
release={release}
|
releaseTimeMs={releaseTime.unix() * 1000}
|
||||||
isCurrentClaimLive={isCurrentClaimLive}
|
isCurrentClaimLive={isCurrentClaimLive}
|
||||||
showLivestream={showLivestream}
|
showLivestream={showLivestream}
|
||||||
showScheduledInfo={showScheduledInfo}
|
showScheduledInfo={showScheduledInfo}
|
||||||
|
|
|
@ -36,7 +36,12 @@ export function hmsToSeconds(str: string) {
|
||||||
|
|
||||||
// Only intended use of future dates is for claims, in case of scheduled
|
// Only intended use of future dates is for claims, in case of scheduled
|
||||||
// publishes or livestreams, used in util/formatAriaLabel
|
// publishes or livestreams, used in util/formatAriaLabel
|
||||||
export function getTimeAgoStr(date: any, showFutureDate?: boolean, genericSecondsString?: boolean) {
|
export function getTimeAgoStr(
|
||||||
|
date: any,
|
||||||
|
showFutureDate?: boolean,
|
||||||
|
genericSecondsString?: boolean,
|
||||||
|
zeroDurationStr: string = 'Just now'
|
||||||
|
) {
|
||||||
const suffixList = ['years', 'months', 'days', 'hours', 'minutes', 'seconds'];
|
const suffixList = ['years', 'months', 'days', 'hours', 'minutes', 'seconds'];
|
||||||
let duration = 0;
|
let duration = 0;
|
||||||
let suffix = '';
|
let suffix = '';
|
||||||
|
@ -59,7 +64,7 @@ export function getTimeAgoStr(date: any, showFutureDate?: boolean, genericSecond
|
||||||
str = suffix === 'seconds' ? 'in a few seconds' : 'in %duration% ' + suffix;
|
str = suffix === 'seconds' ? 'in a few seconds' : 'in %duration% ' + suffix;
|
||||||
duration = duration * -1;
|
duration = duration * -1;
|
||||||
} else if (duration <= 0) {
|
} else if (duration <= 0) {
|
||||||
str = 'Just now';
|
str = zeroDurationStr;
|
||||||
} else {
|
} else {
|
||||||
str = suffix === 'seconds' && genericSecondsString ? 'A few seconds ago' : '%duration% ' + suffix + ' ago';
|
str = suffix === 'seconds' && genericSecondsString ? 'A few seconds ago' : '%duration% ' + suffix + ' ago';
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue