Bluesky app fork with some witchin' additions 馃挮
at main 82 lines 2.7 kB view raw
1import {useMemo} from 'react' 2import { 3 type $Typed, 4 type AppBskyActorDefs, 5 AppBskyEmbedExternal, 6} from '@atproto/api' 7import {isAfter, parseISO} from 'date-fns' 8 9import {useMaybeProfileShadow} from '#/state/cache/profile-shadow' 10import {type LiveNowConfig, useLiveNowConfig} from '#/state/service-config' 11import {useTickEveryMinute} from '#/state/shell' 12import type * as bsky from '#/types/bsky' 13 14export function useActorStatus(actor?: bsky.profile.AnyProfileView) { 15 const shadowed = useMaybeProfileShadow(actor) 16 const tick = useTickEveryMinute() 17 const config = useLiveNowConfig() 18 19 return useMemo(() => { 20 void tick // revalidate every minute 21 22 if (shadowed && 'status' in shadowed && shadowed.status) { 23 const isValid = validateStatus(shadowed.status, config) 24 const isDisabled = shadowed.status.isDisabled || false 25 const isActive = isStatusStillActive(shadowed.status.expiresAt) 26 if (isValid && !isDisabled && isActive) { 27 return { 28 uri: shadowed.status.uri, 29 cid: shadowed.status.cid, 30 isDisabled: false, 31 isActive: true, 32 status: 'app.bsky.actor.status#live', 33 embed: shadowed.status.embed as $Typed<AppBskyEmbedExternal.View>, // temp_isStatusValid asserts this 34 expiresAt: shadowed.status.expiresAt!, // isStatusStillActive asserts this 35 record: shadowed.status.record, 36 } satisfies AppBskyActorDefs.StatusView 37 } 38 return { 39 uri: shadowed.status.uri, 40 cid: shadowed.status.cid, 41 isDisabled, 42 isActive: false, 43 status: 'app.bsky.actor.status#live', 44 embed: shadowed.status.embed as $Typed<AppBskyEmbedExternal.View>, // temp_isStatusValid asserts this 45 expiresAt: shadowed.status.expiresAt!, // isStatusStillActive asserts this 46 record: shadowed.status.record, 47 } satisfies AppBskyActorDefs.StatusView 48 } else { 49 return { 50 status: '', 51 isDisabled: false, 52 isActive: false, 53 record: {}, 54 } satisfies AppBskyActorDefs.StatusView 55 } 56 }, [shadowed, config, tick]) 57} 58 59export function isStatusStillActive(timeStr: string | undefined) { 60 if (!timeStr) return false 61 const now = new Date() 62 const expiry = parseISO(timeStr) 63 64 return isAfter(expiry, now) 65} 66 67export function validateStatus( 68 status: AppBskyActorDefs.StatusView, 69 config: LiveNowConfig, 70) { 71 if (status.status !== 'app.bsky.actor.status#live') return false 72 try { 73 if (AppBskyEmbedExternal.isView(status.embed)) { 74 const url = new URL(status.embed.external.uri) 75 return config.allowedDomains.has(url.hostname) 76 } else { 77 return false 78 } 79 } catch { 80 return false 81 } 82}