Stop re-parsing dates in isStatusStillActive (#11574)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,6 @@ import {moderateStatus} from '@bsky/sdk/moderation'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
|
||||
import {isAfter, parseISO} from 'date-fns'
|
||||
|
||||
import {uploadBlob} from '#/lib/api'
|
||||
import {imageToThumb} from '#/lib/api/resolve'
|
||||
@@ -23,7 +22,11 @@ import {useTickEveryMinute} from '#/state/shell'
|
||||
import {useDialogContext} from '#/components/Dialog'
|
||||
import * as Toast from '#/components/Toast'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {getLiveNowHost, getLiveServiceNames} from '#/features/liveNow/utils'
|
||||
import {
|
||||
getLiveNowHost,
|
||||
getLiveServiceNames,
|
||||
isStatusStillActive,
|
||||
} from '#/features/liveNow/utils'
|
||||
import {app, com} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
|
||||
@@ -151,14 +154,6 @@ export function useActorStatus(actor?: bsky.profile.AnyProfileView) {
|
||||
}, [shadowed, config, tick, moderation])
|
||||
}
|
||||
|
||||
export function isStatusStillActive(timeStr: string | undefined) {
|
||||
if (!timeStr) return false
|
||||
const now = new Date()
|
||||
const expiry = parseISO(timeStr)
|
||||
|
||||
return isAfter(expiry, now)
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates whether the live status is valid for display in the app. Does NOT
|
||||
* validate if the status is valid for the acting user e.g. as they go live.
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import {isStatusStillActive} from '#/features/liveNow/utils'
|
||||
|
||||
describe('isStatusStillActive', () => {
|
||||
const iso = (offsetMs: number) =>
|
||||
new Date(Date.now() + offsetMs).toISOString()
|
||||
|
||||
it('is active for a future expiry', () => {
|
||||
expect(isStatusStillActive(iso(60_000))).toBe(true)
|
||||
})
|
||||
|
||||
it('is not active for a past expiry', () => {
|
||||
expect(isStatusStillActive(iso(-60_000))).toBe(false)
|
||||
})
|
||||
|
||||
it('is not active without a value', () => {
|
||||
expect(isStatusStillActive(undefined)).toBe(false)
|
||||
expect(isStatusStillActive('')).toBe(false)
|
||||
})
|
||||
|
||||
/*
|
||||
* The predicate reads an atproto `datetime`, which always carries an offset.
|
||||
* An unparseable value yields NaN, and every NaN comparison is false, so a
|
||||
* bad timestamp reads as "not live" rather than throwing or showing a stale
|
||||
* live badge.
|
||||
*/
|
||||
it('is not active for an unparseable value', () => {
|
||||
expect(isStatusStillActive('not-a-date')).toBe(false)
|
||||
})
|
||||
|
||||
it('handles offsets and fractional seconds', () => {
|
||||
const future = new Date(Date.now() + 3_600_000)
|
||||
expect(isStatusStillActive(future.toISOString())).toBe(true)
|
||||
expect(isStatusStillActive('2000-01-01T00:00:00.123+00:00')).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -97,3 +97,16 @@ export function getLiveNowHost(url: string) {
|
||||
const {hostname} = new URL(url)
|
||||
return sanitizeLiveNowHost(hostname)
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a live status has not yet expired.
|
||||
*
|
||||
* Called per post, per render while scrolling, so this stays on primitives -
|
||||
* `parseISO` plus two `Date` objects showed up in the scroll profile.
|
||||
* `Date.parse` returns `NaN` for an unparseable value and `NaN > n` is `false`,
|
||||
* which is the same "not active" answer the date-fns version gave.
|
||||
*/
|
||||
export function isStatusStillActive(timeStr: string | undefined) {
|
||||
if (!timeStr) return false
|
||||
return Date.parse(timeStr) > Date.now()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user