diff --git a/src/features/liveNow/index.tsx b/src/features/liveNow/index.tsx index c30ce78fa7..77d1289415 100644 --- a/src/features/liveNow/index.tsx +++ b/src/features/liveNow/index.tsx @@ -26,7 +26,7 @@ import {useTickEveryMinute} from '#/state/shell' import * as Toast from '#/view/com/util/Toast' import {useDialogContext} from '#/components/Dialog' import {useAnalytics} from '#/analytics' -import {getLiveServiceNames} from '#/features/liveNow/utils' +import {getLiveNowHost, getLiveServiceNames} from '#/features/liveNow/utils' import type * as bsky from '#/types/bsky' export * from '#/features/liveNow/utils' @@ -35,10 +35,6 @@ export const DEFAULT_ALLOWED_DOMAINS = [ 'twitch.tv', 'stream.place', 'bluecast.app', - - // TODO remove need for subdomains - 'www.twitch.tv', - 'www.bluecast.app', ] export type LiveNowConfig = { @@ -153,10 +149,10 @@ export function isStatusValidForViewers( try { const {host: liveDid} = new AtUri(status.uri) if (AppBskyEmbedExternal.isView(status.embed)) { - const url = new URL(status.embed.external.uri) + const host = getLiveNowHost(status.embed.external.uri) const exception = config.allowedHostsExceptionsByDid.get(liveDid) - const isValidException = exception ? exception.has(url.hostname) : false - const isValidForAnyone = config.defaultAllowedHosts.has(url.hostname) + const isValidException = exception ? exception.has(host) : false + const isValidForAnyone = config.defaultAllowedHosts.has(host) return isValidException || isValidForAnyone } else { return false @@ -176,8 +172,8 @@ export function useLiveLinkMetaQuery(url: string | null) { queryKey: ['link-meta', url], queryFn: async () => { if (!url) return undefined - const urlp = new URL(url) - if (!liveNowConfig.currentAccountAllowedHosts.has(urlp.hostname)) { + const host = getLiveNowHost(url) + if (!liveNowConfig.currentAccountAllowedHosts.has(host)) { const {formatted} = getLiveServiceNames( liveNowConfig.currentAccountAllowedHosts, ) diff --git a/src/features/liveNow/utils.ts b/src/features/liveNow/utils.ts index ad9a5c3c46..90a6877c08 100644 --- a/src/features/liveNow/utils.ts +++ b/src/features/liveNow/utils.ts @@ -1,5 +1,6 @@ import {type I18n} from '@lingui/core' import {plural} from '@lingui/macro' +import psl from 'psl' export function displayDuration(i18n: I18n, durationInMinutes: number) { const roundedDurationInMinutes = Math.round(durationInMinutes) @@ -25,27 +26,48 @@ export function displayDuration(i18n: I18n, durationInMinutes: number) { const serviceUrlToNameMap: Record = { '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', 'bluecast.app': 'Bluecast', - 'www.bluecast.app': 'Bluecast', } export function getLiveServiceNames(domains: Set) { + console.log(domains) const names = Array.from( - new Set(Array.from(domains.values()).map(d => serviceUrlToNameMap[d] || d)), + new Set( + Array.from(domains.values()) + .map(d => sanitizeLiveNowHost(d)) + .map(d => serviceUrlToNameMap[d] || d), + ), ) return { names, formatted: names.join(', '), } } + +export function sanitizeLiveNowHost(hostname: string) { + // special case this one + if (hostname === 'nba.smart.link') { + return hostname + } + const parsed = psl.parse(hostname) + if (parsed.error || !parsed.listed || !parsed.domain) { + // fall back to dumb version + return hostname.replace(/^www\./, '') + } + return parsed.domain +} + +/** + * Extracts the apex domain from a given URL, for use when matching allowed + * Live Now hosts. + */ +export function getLiveNowHost(url: string) { + const {hostname} = new URL(url) + return sanitizeLiveNowHost(hostname) +}