about summary refs log tree commit diff
path: root/src/components/live
diff options
context:
space:
mode:
authorSamuel Newman <mozzius@protonmail.com>2025-05-17 01:38:34 +0300
committerGitHub <noreply@github.com>2025-05-16 15:38:34 -0700
commit1cdbfc709235ed1933ba51403d941762f384690b (patch)
treef55aa9a16f05645bcc5c2d63839113725b1ca308 /src/components/live
parent75ffb3d243a5415d173f2bca8a5334b70451a1f4 (diff)
downloadvoidsky-1cdbfc709235ed1933ba51403d941762f384690b.tar.zst
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
Diffstat (limited to 'src/components/live')
-rw-r--r--src/components/live/GoLiveDialog.tsx21
-rw-r--r--src/components/live/temp.ts41
2 files changed, 18 insertions, 44 deletions
diff --git a/src/components/live/GoLiveDialog.tsx b/src/components/live/GoLiveDialog.tsx
index 2fad009fd..027447272 100644
--- a/src/components/live/GoLiveDialog.tsx
+++ b/src/components/live/GoLiveDialog.tsx
@@ -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`)}
diff --git a/src/components/live/temp.ts b/src/components/live/temp.ts
deleted file mode 100644
index fb26b8c06..000000000
--- a/src/components/live/temp.ts
+++ /dev/null
@@ -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
-  }
-}