Live via service config (#8378)
* add config (with temp config) * only allow whitelisted domains in form * move config to generic config * use array-based config * update deps * rm expect-error
This commit is contained in:
@@ -9,7 +9,7 @@ import {
|
||||
useTrendingSettingsApi,
|
||||
} from '#/state/preferences/trending'
|
||||
import {useTrendingTopics} from '#/state/queries/trending/useTrendingTopics'
|
||||
import {useTrendingConfig} from '#/state/trending-config'
|
||||
import {useTrendingConfig} from '#/state/service-config'
|
||||
import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
||||
import {BlockDrawerGesture} from '#/view/shell/BlockDrawerGesture'
|
||||
import {atoms as a, useGutters, useTheme} from '#/alf'
|
||||
|
||||
@@ -10,7 +10,8 @@ import {cleanError} from '#/lib/strings/errors'
|
||||
import {toNiceDomain} from '#/lib/strings/url-helpers'
|
||||
import {definitelyUrl} from '#/lib/strings/url-helpers'
|
||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
import {useAgent} from '#/state/session'
|
||||
import {useLiveNowConfig} from '#/state/service-config'
|
||||
import {useAgent, useSession} from '#/state/session'
|
||||
import {useTickEveryMinute} from '#/state/shell'
|
||||
import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
||||
import {atoms as a, ios, native, platform, useTheme, web} from '#/alf'
|
||||
@@ -58,6 +59,10 @@ function DialogInner({profile}: {profile: bsky.profile.AnyProfileView}) {
|
||||
const [duration, setDuration] = useState(60)
|
||||
const moderationOpts = useModerationOpts()
|
||||
const tick = useTickEveryMinute()
|
||||
const liveNowConfig = useLiveNowConfig()
|
||||
const {currentAccount} = useSession()
|
||||
|
||||
const config = liveNowConfig.find(cfg => cfg.did === currentAccount?.did)
|
||||
|
||||
const time = useCallback(
|
||||
(offset: number) => {
|
||||
@@ -79,7 +84,6 @@ function DialogInner({profile}: {profile: bsky.profile.AnyProfileView}) {
|
||||
|
||||
const liveLinkUrl = definitelyUrl(liveLink)
|
||||
const debouncedUrl = useDebouncedValue(liveLinkUrl, 500)
|
||||
const hasLink = !!debouncedUrl
|
||||
|
||||
const {
|
||||
data: linkMeta,
|
||||
@@ -91,6 +95,13 @@ function DialogInner({profile}: {profile: bsky.profile.AnyProfileView}) {
|
||||
queryKey: ['link-meta', debouncedUrl],
|
||||
queryFn: async () => {
|
||||
if (!debouncedUrl) return null
|
||||
if (!config) throw new Error(_(msg`You are not allowed to go live`))
|
||||
|
||||
const urlp = new URL(debouncedUrl)
|
||||
if (!config.domains.includes(urlp.hostname)) {
|
||||
throw new Error(_(msg`${urlp.hostname} is not a valid URL`))
|
||||
}
|
||||
|
||||
return getLinkMeta(agent, debouncedUrl)
|
||||
},
|
||||
})
|
||||
@@ -101,6 +112,10 @@ function DialogInner({profile}: {profile: bsky.profile.AnyProfileView}) {
|
||||
error: goLiveError,
|
||||
} = useUpsertLiveStatusMutation(duration, linkMeta)
|
||||
|
||||
const isSourceInvalid = !!liveLinkError || !!linkMetaError
|
||||
|
||||
const hasLink = !!debouncedUrl && !isSourceInvalid
|
||||
|
||||
return (
|
||||
<Dialog.ScrollableInner
|
||||
label={_(msg`Go Live`)}
|
||||
@@ -136,7 +151,7 @@ function DialogInner({profile}: {profile: bsky.profile.AnyProfileView}) {
|
||||
<TextField.LabelText>
|
||||
<Trans>Live link</Trans>
|
||||
</TextField.LabelText>
|
||||
<TextField.Root isInvalid={!!liveLinkError || !!linkMetaError}>
|
||||
<TextField.Root isInvalid={isSourceInvalid}>
|
||||
<TextField.Input
|
||||
label={_(msg`Live link`)}
|
||||
placeholder={_(msg`www.mylivestream.tv`)}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import {type AppBskyActorDefs, AppBskyEmbedExternal} from '@atproto/api'
|
||||
|
||||
import {DISCOVER_DEBUG_DIDS} from '#/lib/constants'
|
||||
import type * as bsky from '#/types/bsky'
|
||||
|
||||
export const LIVE_DIDS: Record<string, true> = {
|
||||
'did:plc:7sfnardo5xxznxc6esxc5ooe': true, // nba.com
|
||||
'did:plc:gx6fyi3jcfxd7ammq2t7mzp2': true, // rtgame.bsky.social
|
||||
}
|
||||
|
||||
export const LIVE_SOURCES: Record<string, true> = {
|
||||
'nba.com': true,
|
||||
'twitch.tv': true,
|
||||
}
|
||||
|
||||
// TEMP: dumb gating
|
||||
export function temp__canBeLive(profile: bsky.profile.AnyProfileView) {
|
||||
if (__DEV__)
|
||||
return !!DISCOVER_DEBUG_DIDS[profile.did] || !!LIVE_DIDS[profile.did]
|
||||
return !!LIVE_DIDS[profile.did]
|
||||
}
|
||||
|
||||
export function temp__canGoLive(profile: bsky.profile.AnyProfileView) {
|
||||
if (__DEV__) return true
|
||||
return !!LIVE_DIDS[profile.did]
|
||||
}
|
||||
|
||||
// status must have a embed, and the embed must be an approved host for the status to be valid
|
||||
export function temp__isStatusValid(status: AppBskyActorDefs.StatusView) {
|
||||
if (status.status !== 'app.bsky.actor.status#live') return false
|
||||
try {
|
||||
if (AppBskyEmbedExternal.isView(status.embed)) {
|
||||
const url = new URL(status.embed.external.uri)
|
||||
return !!LIVE_SOURCES[url.hostname]
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user