wip bsky client for the web & android
at main 91 lines 2.5 kB view raw
1import type { ComAtprotoServerDescribeServer } from '@atcute/atproto' 2import { Client, simpleFetchHandler } from '@atcute/client' 3 4export type BaseProvider = { 5 name: string 6 url: URL 7 location: string 8 subtitle?: string 9 /** whether this provider should be prominently displayed as a login option */ 10 default?: boolean 11 /** whether this provider should be pinned when displayed */ 12 pin?: boolean 13 /** if the PDS has a policy on who can use what handles, blacksky, for example. */ 14 handlePolicy?: URL 15} 16 17export type HydratedProvider = BaseProvider & { 18 server: ComAtprotoServerDescribeServer.$output 19} 20 21export const PROVIDERS: BaseProvider[] = [ 22 { 23 name: 'selfhosted.social', 24 url: new URL('https://selfhosted.social/'), 25 subtitle: 'A popular community-run PDS', 26 location: 'US', 27 pin: true, 28 }, 29 { 30 name: 'Blacksky', 31 url: new URL('https://blacksky.app/'), 32 subtitle: 'A PDS for the black community and allies.', 33 location: 'US', 34 handlePolicy: new URL( 35 'https://docs.blacksky.community/migrating-to-blacksky-pds-complete-guide#who-can-use-blacksky-services', 36 ), 37 default: true, 38 }, 39 { 40 name: 'Bluesky', 41 url: new URL('https://bsky.social/'), 42 location: 'US', 43 default: true, 44 }, 45 { 46 name: 'Tophhie Social', 47 url: new URL('https://pds.tophhie.cloud'), 48 location: 'GB', 49 }, 50] 51 52export async function getHydratedProviders(): Promise<HydratedProvider[]> { 53 return ( 54 await Promise.all( 55 PROVIDERS.map(async (provider) => { 56 const xrpc = new Client({ 57 handler: simpleFetchHandler({ service: provider.url }), 58 }) 59 60 const { data, ok } = await xrpc.get('com.atproto.server.describeServer', {}) 61 if (!ok) return undefined 62 63 return { 64 ...provider, 65 server: data, 66 } 67 }), 68 ) 69 ).filter(Boolean) as HydratedProvider[] 70} 71 72function shuffle<T>(array: T[]): T[] { 73 const shuffled = [...array] 74 for (let i = shuffled.length - 1; i > 0; i--) { 75 const j = Math.floor(Math.random() * (i + 1)) 76 const temp = shuffled[i]! 77 shuffled[i] = shuffled[j]! 78 shuffled[j] = temp 79 } 80 81 return shuffled 82} 83 84export async function getProviderList(shuffled = true): Promise<HydratedProvider[]> { 85 const hydratedProviders = await getHydratedProviders() 86 const pinnedProviders = hydratedProviders.filter((provider) => provider.pin) 87 const unpinnedProviders = hydratedProviders.filter((provider) => !provider.pin) 88 89 if (shuffled) return [...pinnedProviders, ...shuffle(unpinnedProviders)] 90 return [...pinnedProviders, ...unpinnedProviders] 91}