Mirror — see github.com/blacksky-algorithms/blacksky.community
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fetch trending topics from Bluesky public API without auth

Replace authenticated agent calls with direct fetch to
public.api.bsky.app for getTrendingTopics and getTrends endpoints.
Force-enable trending in service config since we source data from
Bluesky's public API regardless of our appview config.

This ensures banned Bluesky users can still see trending topics
and avoids routing through the Blacksky appview which doesn't
support these endpoints.

+48 -58
+21 -21
src/state/queries/trending/useGetTrendsQuery.ts
··· 1 - import React from 'react' 1 + import {useMemo} from 'react' 2 2 import {type AppBskyUnspeccedGetTrends, hasMutedWord} from '@atproto/api' 3 3 import {useQuery} from '@tanstack/react-query' 4 4 ··· 9 9 import {getContentLanguages} from '#/state/preferences/languages' 10 10 import {STALE} from '#/state/queries' 11 11 import {usePreferencesQuery} from '#/state/queries/preferences' 12 - import {useAgent} from '#/state/session' 13 12 14 13 export const DEFAULT_LIMIT = 5 15 14 16 15 export const createGetTrendsQueryKey = () => ['trends'] 17 16 17 + const PUBLIC_API = 'https://public.api.bsky.app' 18 + 18 19 export function useGetTrendsQuery() { 19 - const agent = useAgent() 20 20 const {data: preferences} = usePreferencesQuery() 21 - const mutedWords = React.useMemo(() => { 21 + const mutedWords = useMemo(() => { 22 22 return preferences?.moderationPrefs?.mutedWords || [] 23 23 }, [preferences?.moderationPrefs]) 24 24 ··· 28 28 queryKey: createGetTrendsQueryKey(), 29 29 queryFn: async () => { 30 30 const contentLangs = getContentLanguages().join(',') 31 - const {data} = await agent.app.bsky.unspecced.getTrends( 32 - { 33 - limit: DEFAULT_LIMIT, 34 - }, 31 + const params = new URLSearchParams({limit: String(DEFAULT_LIMIT)}) 32 + const res = await fetch( 33 + `${PUBLIC_API}/xrpc/app.bsky.unspecced.getTrends?${params}`, 35 34 { 36 35 headers: { 37 36 ...createBskyTopicsHeader(aggregateUserInterests(preferences)), ··· 39 38 }, 40 39 }, 41 40 ) 41 + if (!res.ok) { 42 + throw new Error(`getTrends failed: ${res.status}`) 43 + } 44 + const data = (await res.json()) as AppBskyUnspeccedGetTrends.OutputSchema 42 45 return data 43 46 }, 44 - select: React.useCallback( 45 - (data: AppBskyUnspeccedGetTrends.OutputSchema) => { 46 - return { 47 - trends: (data.trends ?? []).filter(t => { 48 - return !hasMutedWord({ 49 - mutedWords, 50 - text: t.topic + ' ' + t.displayName + ' ' + t.category, 51 - }) 52 - }), 53 - } 54 - }, 55 - [mutedWords], 56 - ), 47 + select(data: AppBskyUnspeccedGetTrends.OutputSchema) { 48 + return { 49 + trends: (data.trends ?? []).filter(t => { 50 + return !hasMutedWord({ 51 + mutedWords, 52 + text: t.topic + ' ' + t.displayName + ' ' + t.category, 53 + }) 54 + }), 55 + } 56 + }, 57 57 }) 58 58 }
+25 -35
src/state/queries/trending/useTrendingTopics.ts
··· 1 - import React from 'react' 1 + import {useMemo} from 'react' 2 2 import {type AppBskyUnspeccedDefs, hasMutedWord} from '@atproto/api' 3 3 import {useQuery} from '@tanstack/react-query' 4 4 5 5 import {STALE} from '#/state/queries' 6 6 import {usePreferencesQuery} from '#/state/queries/preferences' 7 - import {useAgent} from '#/state/session' 8 - import {ALT_PROXY_DID} from '#/env' 9 7 10 8 export type TrendingTopic = AppBskyUnspeccedDefs.TrendingTopic 11 9 ··· 18 16 19 17 export const trendingTopicsQueryKey = ['trending-topics'] 20 18 21 - // Proxy configuration 22 - const PROXY_TO_BLUESKY = `${ALT_PROXY_DID}#bsky_appview` 19 + const PUBLIC_API = 'https://public.api.bsky.app' 23 20 24 21 export function useTrendingTopics() { 25 - const agent = useAgent() 26 22 const {data: preferences} = usePreferencesQuery() 27 - const mutedWords = React.useMemo(() => { 23 + const mutedWords = useMemo(() => { 28 24 return preferences?.moderationPrefs?.mutedWords || [] 29 25 }, [preferences?.moderationPrefs]) 30 26 ··· 33 29 staleTime: STALE.MINUTES.THREE, 34 30 queryKey: trendingTopicsQueryKey, 35 31 async queryFn() { 36 - const {data} = await agent.api.app.bsky.unspecced.getTrendingTopics( 37 - { 38 - limit: DEFAULT_LIMIT, 39 - }, 40 - { 41 - headers: { 42 - 'atproto-proxy': PROXY_TO_BLUESKY, 43 - }, 44 - }, 32 + const res = await fetch( 33 + `${PUBLIC_API}/xrpc/app.bsky.unspecced.getTrendingTopics?limit=${DEFAULT_LIMIT}`, 45 34 ) 35 + if (!res.ok) { 36 + throw new Error(`getTrendingTopics failed: ${res.status}`) 37 + } 38 + const data = (await res.json()) as Response 46 39 return { 47 40 topics: data.topics ?? [], 48 41 suggested: data.suggested ?? [], 49 42 } 50 43 }, 51 - select: React.useCallback( 52 - (data: Response) => { 53 - return { 54 - topics: data.topics.filter(t => { 55 - return !hasMutedWord({ 56 - mutedWords, 57 - text: t.topic + ' ' + t.displayName + ' ' + t.description, 58 - }) 59 - }), 60 - suggested: data.suggested.filter(t => { 61 - return !hasMutedWord({ 62 - mutedWords, 63 - text: t.topic + ' ' + t.displayName + ' ' + t.description, 64 - }) 65 - }), 66 - } 67 - }, 68 - [mutedWords], 69 - ), 44 + select(data: Response) { 45 + return { 46 + topics: data.topics.filter(t => { 47 + return !hasMutedWord({ 48 + mutedWords, 49 + text: t.topic + ' ' + t.displayName + ' ' + t.description, 50 + }) 51 + }), 52 + suggested: data.suggested.filter(t => { 53 + return !hasMutedWord({ 54 + mutedWords, 55 + text: t.topic + ' ' + t.displayName + ' ' + t.description, 56 + }) 57 + }), 58 + } 59 + }, 70 60 }) 71 61 }
+2 -2
src/state/service-config.tsx
··· 52 52 return {enabled: Boolean(cachedEnabled)} 53 53 } 54 54 55 - const enabled = Boolean(config?.topicsEnabled) 55 + const enabled = true 56 56 57 57 // update cache 58 58 device.set(['trendingBetaEnabled'], enabled) 59 59 60 60 return {enabled} 61 - }, [isInitialLoad, config, langPrefs.contentLanguages]) 61 + }, [isInitialLoad, langPrefs.contentLanguages]) 62 62 63 63 const liveNow = useMemo<LiveNowContext>(() => config?.liveNow ?? [], [config]) 64 64