[APP-1759] Live Now Open Beta (#9699)
* Add report dialog to LiveStatus dialog * Mr Worldwide™ * Bug fix bug fix * Copy update * Simplify parsing * Bump api sdk * update dev-env * Update yarn.lock * Bump api sdk * Refactor useActorStatus, add disablement and appeal dialog * Fix types * Guard against chat profile views * Go live (disabled) * Fix types * Update config and gates * Add allowed services * Merge conflicts * Fix gradient handling for nudge * Only show nudge on your own profile * Fix live avi overflow breakage * Add TODO * Feedback * Update nux image --------- Co-authored-by: Samuel Newman <mozzius@protonmail.com>
This commit is contained in:
@@ -18,7 +18,6 @@ import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import * as TextField from '#/components/forms/TextField'
|
||||
import {Clock_Stroke2_Corner0_Rounded as ClockIcon} from '#/components/icons/Clock'
|
||||
import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {LinkPreview} from './LinkPreview'
|
||||
@@ -172,26 +171,13 @@ function DialogInner({
|
||||
</TextField.Root>
|
||||
</View>
|
||||
{(liveLinkError || linkMetaError) && (
|
||||
<View style={[a.flex_row, a.gap_xs, a.align_center]}>
|
||||
<WarningIcon
|
||||
style={[{color: t.palette.negative_500}]}
|
||||
size="sm"
|
||||
/>
|
||||
<Text
|
||||
style={[
|
||||
a.text_sm,
|
||||
a.leading_snug,
|
||||
a.flex_1,
|
||||
a.font_semi_bold,
|
||||
{color: t.palette.negative_500},
|
||||
]}>
|
||||
{liveLinkError ? (
|
||||
<Trans>This is not a valid link</Trans>
|
||||
) : (
|
||||
cleanError(linkMetaError)
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
<Admonition type="error">
|
||||
{liveLinkError ? (
|
||||
<Trans>This is not a valid link</Trans>
|
||||
) : (
|
||||
cleanError(linkMetaError)
|
||||
)}
|
||||
</Admonition>
|
||||
)}
|
||||
|
||||
<LinkPreview linkMeta={linkMeta} loading={linkMetaLoading} />
|
||||
|
||||
@@ -6,13 +6,14 @@ import {useLingui} from '@lingui/react'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {definitelyUrl} from '#/lib/strings/url-helpers'
|
||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
import {useLiveNowConfig} from '#/state/service-config'
|
||||
import {useTickEveryMinute} from '#/state/shell'
|
||||
import {atoms as a, ios, native, platform, useTheme, web} from '#/alf'
|
||||
import {Admonition} from '#/components/Admonition'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import * as TextField from '#/components/forms/TextField'
|
||||
import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning'
|
||||
import {getLiveServiceNames} from '#/components/live/utils'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import * as ProfileCard from '#/components/ProfileCard'
|
||||
import * as Select from '#/components/Select'
|
||||
@@ -49,6 +50,10 @@ function DialogInner({profile}: {profile: bsky.profile.AnyProfileView}) {
|
||||
const [duration, setDuration] = useState(60)
|
||||
const moderationOpts = useModerationOpts()
|
||||
const tick = useTickEveryMinute()
|
||||
const liveNowConfig = useLiveNowConfig()
|
||||
const {formatted: allowedServices} = getLiveServiceNames(
|
||||
liveNowConfig.allowedDomains,
|
||||
)
|
||||
|
||||
const time = useCallback(
|
||||
(offset: number) => {
|
||||
@@ -139,27 +144,21 @@ function DialogInner({profile}: {profile: bsky.profile.AnyProfileView}) {
|
||||
/>
|
||||
</TextField.Root>
|
||||
</View>
|
||||
{(liveLinkError || linkMetaError) && (
|
||||
<View style={[a.flex_row, a.gap_xs, a.align_center]}>
|
||||
<WarningIcon
|
||||
style={[{color: t.palette.negative_500}]}
|
||||
size="sm"
|
||||
/>
|
||||
<Text
|
||||
style={[
|
||||
a.text_sm,
|
||||
a.leading_snug,
|
||||
a.flex_1,
|
||||
a.font_semi_bold,
|
||||
{color: t.palette.negative_500},
|
||||
]}>
|
||||
{liveLinkError ? (
|
||||
<Trans>This is not a valid link</Trans>
|
||||
) : (
|
||||
cleanError(linkMetaError)
|
||||
)}
|
||||
</Text>
|
||||
</View>
|
||||
{liveLinkError || linkMetaError ? (
|
||||
<Admonition type="error">
|
||||
{liveLinkError ? (
|
||||
<Trans>This is not a valid link</Trans>
|
||||
) : (
|
||||
cleanError(linkMetaError)
|
||||
)}
|
||||
</Admonition>
|
||||
) : (
|
||||
<Admonition type="tip">
|
||||
<Trans>
|
||||
The following services are enabled for your account:{' '}
|
||||
{allowedServices}
|
||||
</Trans>
|
||||
</Admonition>
|
||||
)}
|
||||
|
||||
<LinkPreview linkMeta={linkMeta} loading={linkMetaLoading} />
|
||||
|
||||
@@ -18,10 +18,10 @@ import {useLiveNowConfig} from '#/state/service-config'
|
||||
import {useAgent, useSession} from '#/state/session'
|
||||
import * as Toast from '#/view/com/util/Toast'
|
||||
import {useDialogContext} from '#/components/Dialog'
|
||||
import {getLiveServiceNames} from '#/components/live/utils'
|
||||
|
||||
export function useLiveLinkMetaQuery(url: string | null) {
|
||||
const liveNowConfig = useLiveNowConfig()
|
||||
const {currentAccount} = useSession()
|
||||
const {_} = useLingui()
|
||||
|
||||
const agent = useAgent()
|
||||
@@ -30,13 +30,14 @@ export function useLiveLinkMetaQuery(url: string | null) {
|
||||
queryKey: ['link-meta', url],
|
||||
queryFn: async () => {
|
||||
if (!url) return undefined
|
||||
const config = liveNowConfig.find(cfg => cfg.did === currentAccount?.did)
|
||||
|
||||
if (!config) throw new Error(_(msg`You are not allowed to go live`))
|
||||
|
||||
const urlp = new URL(url)
|
||||
if (!config.domains.includes(urlp.hostname)) {
|
||||
throw new Error(_(msg`${urlp.hostname} is not a valid URL`))
|
||||
if (!liveNowConfig.allowedDomains.has(urlp.hostname)) {
|
||||
const {formatted} = getLiveServiceNames(liveNowConfig.allowedDomains)
|
||||
throw new Error(
|
||||
_(
|
||||
msg`This service is not supported while the Live feature is in beta. Allowed services: ${formatted}.`,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return await getLinkMeta(agent, url)
|
||||
|
||||
@@ -35,3 +35,28 @@ export function useDebouncedValue<T>(val: T, delayMs: number): T {
|
||||
|
||||
return prev
|
||||
}
|
||||
|
||||
const serviceUrlToNameMap: Record<string, string> = {
|
||||
'twitch.tv': 'Twitch',
|
||||
'www.twitch.tv': 'Twitch',
|
||||
'youtube.com': 'YouTube',
|
||||
'www.youtube.com': 'YouTube',
|
||||
'youtu.be': 'YouTube',
|
||||
'nba.com': 'NBA',
|
||||
'www.nba.com': 'NBA',
|
||||
'nba.smart.link': 'nba.smart.link',
|
||||
'espn.com': 'ESPN',
|
||||
'www.espn.com': 'ESPN',
|
||||
'stream.place': 'Streamplace',
|
||||
'skylight.social': 'Skylight',
|
||||
}
|
||||
|
||||
export function getLiveServiceNames(domains: Set<string>) {
|
||||
const names = Array.from(
|
||||
new Set(Array.from(domains.values()).map(d => serviceUrlToNameMap[d] || d)),
|
||||
)
|
||||
return {
|
||||
names,
|
||||
formatted: names.join(', '),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user