handle all singular units appropriately

This commit is contained in:
keyan 2023-08-08 16:31:43 -05:00
parent 9941bc6519
commit 3cfeede46a
6 changed files with 43 additions and 12 deletions

View File

@ -2,6 +2,7 @@ import Badge from 'react-bootstrap/Badge'
import OverlayTrigger from 'react-bootstrap/OverlayTrigger' import OverlayTrigger from 'react-bootstrap/OverlayTrigger'
import Tooltip from 'react-bootstrap/Tooltip' import Tooltip from 'react-bootstrap/Tooltip'
import CowboyHatIcon from '../svgs/cowboy.svg' import CowboyHatIcon from '../svgs/cowboy.svg'
import { numWithUnits } from '../lib/format'
export default function CowboyHat ({ user, badge, className = 'ms-1', height = 16, width = 16 }) { export default function CowboyHat ({ user, badge, className = 'ms-1', height = 16, width = 16 }) {
if (user?.streak === null || user.hideCowboyHat) { if (user?.streak === null || user.hideCowboyHat) {
@ -10,7 +11,10 @@ export default function CowboyHat ({ user, badge, className = 'ms-1', height = 1
const streak = user.streak const streak = user.streak
return ( return (
<HatTooltip overlayText={streak ? `${streak} days` : 'new'}> <HatTooltip overlayText={streak
? `${numWithUnits(streak, { abbreviate: false, unitSingular: 'day', unitPlural: 'days' })}`
: 'new'}
>
{badge {badge
? ( ? (
<Badge bg='grey-medium' className='ms-2 d-inline-flex align-items-center'> <Badge bg='grey-medium' className='ms-2 d-inline-flex align-items-center'>

View File

@ -17,7 +17,10 @@ import BookmarkDropdownItem from './bookmark'
import SubscribeDropdownItem from './subscribe' import SubscribeDropdownItem from './subscribe'
import { CopyLinkDropdownItem } from './share' import { CopyLinkDropdownItem } from './share'
export default function ItemInfo ({ item, pendingSats, full, commentsText, commentTextSingular, className, embellishUser, extraInfo, onEdit, editText }) { export default function ItemInfo ({
item, pendingSats, full, commentsText = 'comments',
commentTextSingular = 'comment', className, embellishUser, extraInfo, onEdit, editText
}) {
const editThreshold = new Date(item.createdAt).getTime() + 10 * 60000 const editThreshold = new Date(item.createdAt).getTime() + 10 * 60000
const me = useMe() const me = useMe()
const router = useRouter() const router = useRouter()
@ -34,7 +37,11 @@ export default function ItemInfo ({ item, pendingSats, full, commentsText, comme
<div className={className || `${styles.other}`}> <div className={className || `${styles.other}`}>
{!item.position && {!item.position &&
<> <>
<span title={`from ${item.upvotes} stackers ${item.mine <span title={`from ${numWithUnits(item.upvotes, {
abbreviate: false,
unitSingular: 'stacker',
unitPlural: 'stackers'
})} ${item.mine
? `\\ ${numWithUnits(item.meSats, { abbreviate: false })} to post` ? `\\ ${numWithUnits(item.meSats, { abbreviate: false })} to post`
: `(${numWithUnits(item.meSats + pendingSats, { abbreviate: false })} from me)`} `} : `(${numWithUnits(item.meSats + pendingSats, { abbreviate: false })} from me)`} `}
> >
@ -58,7 +65,11 @@ export default function ItemInfo ({ item, pendingSats, full, commentsText, comme
} }
}} title={numWithUnits(item.commentSats)} className='text-reset position-relative' }} title={numWithUnits(item.commentSats)} className='text-reset position-relative'
> >
{item.ncomments} {item.ncomments === 1 ? commentTextSingular || 'comment' : commentsText || 'comments'} {numWithUnits(item.ncomments, {
abbreviate: false,
unitPlural: commentsText,
unitSingular: commentTextSingular
})}
{hasNewComments && {hasNewComments &&
<span className={styles.notification}> <span className={styles.notification}>
<span className='invisible'>{' '}</span> <span className='invisible'>{' '}</span>

View File

@ -133,7 +133,11 @@ function Streak ({ n }) {
] ]
if (n.days) { if (n.days) {
return `After ${n.days} days, ` + LOST_BLURBS[index] return `After ${numWithUnits(n.days, {
abbreviate: false,
unitSingular: 'day',
unitPlural: 'days'
})}, ` + LOST_BLURBS[index]
} }
return FOUND_BLURBS[index] return FOUND_BLURBS[index]
@ -177,7 +181,12 @@ function Invitification ({ n }) {
return ( return (
<> <>
<small className='fw-bold text-secondary ms-2'> <small className='fw-bold text-secondary ms-2'>
your invite has been redeemed by {n.invite.invitees.length} stackers your invite has been redeemed by
{numWithUnits(n.invite.invitees.length, {
abbreviate: false,
unitSingular: 'stacker',
unitPlural: 'stackers'
})}
</small> </small>
<div className='ms-4 me-2 mt-1'> <div className='ms-4 me-2 mt-1'>
<Invite <Invite
@ -198,7 +207,7 @@ function NostrZap ({ n }) {
return ( return (
<> <>
<div className='fw-bold text-nostr ms-2 py-1'> <div className='fw-bold text-nostr ms-2 py-1'>
<NostrIcon width={24} height={24} className='fill-nostr me-1' />{n.earnedSats} sats zap from <NostrIcon width={24} height={24} className='fill-nostr me-1' />{numWithUnits(n.earnedSats)} zap from
<Link className='mx-1 text-reset text-underline' target='_blank' href={`https://snort.social/p/${npub}`} rel='noreferrer'> <Link className='mx-1 text-reset text-underline' target='_blank' href={`https://snort.social/p/${npub}`} rel='noreferrer'>
{npub.slice(0, 10)}... {npub.slice(0, 10)}...
</Link> </Link>

View File

@ -1,6 +1,6 @@
import { gql, useMutation } from '@apollo/client' import { gql, useMutation } from '@apollo/client'
import Button from 'react-bootstrap/Button' import Button from 'react-bootstrap/Button'
import { fixedDecimal } from '../lib/format' import { fixedDecimal, numWithUnits } from '../lib/format'
import { timeLeft } from '../lib/time' import { timeLeft } from '../lib/time'
import { useMe } from './me' import { useMe } from './me'
import styles from './poll.module.css' import styles from './poll.module.css'
@ -86,7 +86,7 @@ export default function Poll ({ item }) {
key={v.id} v={v} key={v.id} v={v}
progress={item.poll.count ? fixedDecimal(v.count * 100 / item.poll.count, 1) : 0} progress={item.poll.count ? fixedDecimal(v.count * 100 / item.poll.count, 1) : 0}
/>)} />)}
<div className='text-muted mt-1'>{item.poll.count} votes \ {expiresIn ? `${expiresIn} left` : 'poll ended'}</div> <div className='text-muted mt-1'>{numWithUnits(item.poll.count, { unitSingular: 'vote', unitPlural: 'votes' })} \ {expiresIn ? `${expiresIn} left` : 'poll ended'}</div>
</div> </div>
) )
} }

View File

@ -59,7 +59,7 @@ export default function Seo ({ sub, item, user }) {
desc = `@${item.user.name} stacked ${numWithUnits(item.sats)} ${item.url ? `posting ${item.url}` : 'with this discussion'}` desc = `@${item.user.name} stacked ${numWithUnits(item.sats)} ${item.url ? `posting ${item.url}` : 'with this discussion'}`
} }
if (item.ncomments) { if (item.ncomments) {
desc += ` [${item.ncomments} comments` desc += ` [${numWithUnits(item.ncomments, { unitSingular: 'comment', unitPlural: 'comments' })}`
if (item.boost) { if (item.boost) {
desc += `, ${item.boost} boost` desc += `, ${item.boost} boost`
} }
@ -69,7 +69,7 @@ export default function Seo ({ sub, item, user }) {
} }
} }
if (user) { if (user) {
desc = `@${user.name} has [${user.stacked} stacked, ${user.nposts} posts, ${user.ncomments} comments]` desc = `@${user.name} has [${user.stacked} stacked, ${numWithUnits(user.nitems, { unitSingular: 'item', unitPlural: 'items' })}]`
} }
return ( return (

View File

@ -17,6 +17,7 @@ import Avatar from './avatar'
import CowboyHat from './cowboy-hat' import CowboyHat from './cowboy-hat'
import { userSchema } from '../lib/validate' import { userSchema } from '../lib/validate'
import { useShowModal } from './modal' import { useShowModal } from './modal'
import { numWithUnits } from '../lib/format'
export default function UserHeader ({ user }) { export default function UserHeader ({ user }) {
const router = useRouter() const router = useRouter()
@ -35,7 +36,13 @@ export default function UserHeader ({ user }) {
</Nav.Item> </Nav.Item>
<Nav.Item> <Nav.Item>
<Link href={'/' + user.name + '/all'} passHref legacyBehavior> <Link href={'/' + user.name + '/all'} passHref legacyBehavior>
<Nav.Link eventKey>{user.nitems} items</Nav.Link> <Nav.Link eventKey>
{numWithUnits(user.nitems, {
abbreviate: false,
unitSingular: 'item',
unitPlural: 'items'
})}
</Nav.Link>
</Link> </Link>
</Nav.Item> </Nav.Item>
</Nav> </Nav>