From e58ddbc8097135f2e77a7088b6859043b5f7d583 Mon Sep 17 00:00:00 2001 From: infinite-persistence <64950861+infinite-persistence@users.noreply.github.com> Date: Tue, 11 Jan 2022 08:42:12 -0800 Subject: [PATCH] View/Follower count: only use compact when > 10k (#664) * Tooltip: add 'followCursor' and 'placement' option When used on a `` with short text but large empty area, the location of the tooltip was at the bottom-center of the area, which isn't ideal. I think 'followCursor' should be the default, but making it optional for now to minimize testing. Also added the 'placement' prop -- for the span case again, the mouse cursor is blocking the tooltip. * View/Follower count: only use compact when > 10k ## Issue Received complaints -- some people prefer to see full resolution. ## Changes - As a compromise, we'll only apply the compact notation when the value is greater than 10k, with the exception of Tile View Count, where we'll always apply it due to space limitation. - Also added Tooltip for Follower count. ## Fixes - The string was always in 'en' locale in some instances, so it wasn't grouping up digits properly in Japanese (groups of 4), for example. --- ui/component/claimPreview/index.js | 3 ++- ui/component/claimPreview/view.jsx | 15 +++++++++------ ui/component/common/tooltip.jsx | 14 +++++++++++++- ui/component/fileViewCount/index.js | 2 ++ ui/component/fileViewCount/view.jsx | 7 ++++--- ui/util/string.js | 24 +++++++++++++++--------- 6 files changed, 45 insertions(+), 20 deletions(-) diff --git a/ui/component/claimPreview/index.js b/ui/component/claimPreview/index.js index 34028fca5..124705c73 100644 --- a/ui/component/claimPreview/index.js +++ b/ui/component/claimPreview/index.js @@ -22,7 +22,7 @@ import { doCollectionEdit } from 'redux/actions/collections'; import { doFileGet } from 'redux/actions/file'; import { selectBanStateForUri } from 'lbryinc'; import { selectIsActiveLivestreamForUri } from 'redux/selectors/livestream'; -import { selectShowMatureContent } from 'redux/selectors/settings'; +import { selectLanguage, selectShowMatureContent } from 'redux/selectors/settings'; import { makeSelectHasVisitedUri } from 'redux/selectors/content'; import { selectIsSubscribedForUri } from 'redux/selectors/subscriptions'; import { isClaimNsfw } from 'util/claim'; @@ -57,6 +57,7 @@ const select = (state, props) => { isCollectionMine: makeSelectCollectionIsMine(props.collectionId)(state), collectionUris: makeSelectUrlsForCollectionId(props.collectionId)(state), collectionIndex: makeSelectIndexForUrlInCollection(props.uri, props.collectionId)(state), + lang: selectLanguage(state), }; }; diff --git a/ui/component/claimPreview/view.jsx b/ui/component/claimPreview/view.jsx index 523c89671..7d84b9faa 100644 --- a/ui/component/claimPreview/view.jsx +++ b/ui/component/claimPreview/view.jsx @@ -11,6 +11,7 @@ import { isChannelClaim } from 'util/claim'; import { formatLbryUrlForWeb } from 'util/url'; import { formatClaimPreviewTitle } from 'util/formatAriaLabel'; import { toCompactNotation } from 'util/string'; +import Tooltip from 'component/common/tooltip'; import FileThumbnail from 'component/fileThumbnail'; import UriIndicator from 'component/uriIndicator'; import PreviewOverlayProperties from 'component/previewOverlayProperties'; @@ -88,6 +89,7 @@ type Props = { indexInContainer?: number, // The index order of this component within 'containerId'. channelSubCount?: number, swipeLayout: boolean, + lang: string, }; const ClaimPreview = forwardRef((props: Props, ref: any) => { @@ -149,6 +151,7 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { indexInContainer, channelSubCount, swipeLayout = false, + lang, } = props; const isCollection = claim && claim.value_type === 'collection'; @@ -166,13 +169,13 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { if (channelSubCount === undefined) { return ; } - + const formattedSubCount = toCompactNotation(channelSubCount, lang, 10000); return ( - - {channelSubCount === 1 - ? __('1 Follower') - : __('%formattedSubCount% Followers', { formattedSubCount: toCompactNotation(channelSubCount) })} - + + + {channelSubCount === 1 ? __('1 Follower') : __('%formattedSubCount% Followers', { formattedSubCount })} + + ); }, [channelSubCount]); const isValid = uri && isURIValid(uri, false); diff --git a/ui/component/common/tooltip.jsx b/ui/component/common/tooltip.jsx index 11753e539..14f794a3e 100644 --- a/ui/component/common/tooltip.jsx +++ b/ui/component/common/tooltip.jsx @@ -9,10 +9,20 @@ type Props = { disableInteractive?: boolean, enterDelay?: number, title?: string | Node, + followCursor?: boolean, + placement?: string, // https://mui.com/api/tooltip/ }; function Tooltip(props: Props) { - const { arrow = true, children, disableInteractive = true, enterDelay = 300, title } = props; + const { + arrow = true, + children, + disableInteractive = true, + enterDelay = 300, + title, + followCursor = false, + placement = 'bottom', + } = props; return ( {children} diff --git a/ui/component/fileViewCount/index.js b/ui/component/fileViewCount/index.js index 3cbe99db3..4573f2f4b 100644 --- a/ui/component/fileViewCount/index.js +++ b/ui/component/fileViewCount/index.js @@ -1,6 +1,7 @@ import { connect } from 'react-redux'; import { selectClaimIdForUri } from 'redux/selectors/claims'; import { selectViewersForId } from 'redux/selectors/livestream'; +import { selectLanguage } from 'redux/selectors/settings'; import { doFetchViewCount, selectViewCountForUri } from 'lbryinc'; import FileViewCount from './view'; @@ -10,6 +11,7 @@ const select = (state, props) => { claimId, viewCount: selectViewCountForUri(state, props.uri), activeViewers: props.livestream && claimId ? selectViewersForId(state, claimId) : undefined, + lang: selectLanguage(state), }; }; diff --git a/ui/component/fileViewCount/view.jsx b/ui/component/fileViewCount/view.jsx index 45a001a9f..2bd6af440 100644 --- a/ui/component/fileViewCount/view.jsx +++ b/ui/component/fileViewCount/view.jsx @@ -14,12 +14,13 @@ type Props = { uri: string, viewCount: string, activeViewers?: number, + lang: string, }; function FileViewCount(props: Props) { - const { claimId, fetchViewCount, viewCount, livestream, activeViewers, isLive = false } = props; + const { claimId, fetchViewCount, viewCount, livestream, activeViewers, isLive = false, lang } = props; const count = livestream ? activeViewers || 0 : viewCount; - const countCompact = toCompactNotation(count); + const countCompact = toCompactNotation(count, lang, 10000); const countFullResolution = Number(count).toLocaleString(); React.useEffect(() => { @@ -29,7 +30,7 @@ function FileViewCount(props: Props) { }, [claimId]); // eslint-disable-line react-hooks/exhaustive-deps return ( - + {livestream && __('%viewer_count% currently %viewer_state%', { diff --git a/ui/util/string.js b/ui/util/string.js index d5784e9ad..2c7f761a3 100644 --- a/ui/util/string.js +++ b/ui/util/string.js @@ -4,14 +4,20 @@ export function toCapitalCase(string: string) { return string.charAt(0).toUpperCase() + string.slice(1); } -export function toCompactNotation(number: string | number, lang: ?string) { - try { - return Number(number).toLocaleString(lang || 'en', { - compactDisplay: 'short', - notation: 'compact', - }); - } catch (err) { - // Not all browsers support the addition options. - return Number(number).toLocaleString(); +export function toCompactNotation(number: string | number, lang: ?string, minThresholdToApply?: string | number) { + const locale = lang || 'en'; + + if (minThresholdToApply && Number(number) >= Number(minThresholdToApply)) { + try { + return Number(number).toLocaleString(locale, { + compactDisplay: 'short', + notation: 'compact', + }); + } catch (err) { + // Not all browsers support the additional options. + return Number(number).toLocaleString(locale); + } + } else { + return Number(number).toLocaleString(locale); } }