Bluesky app fork with some witchin' additions 馃挮 witchsky.app
bluesky fork client
117
fork

Configure Feed

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

at a876aae44ea07494ebea9727350aa060b81f317b 166 lines 4.2 kB view raw
1import { 2 type $Typed, 3 type AppBskyActorDefs, 4 type AppBskyGraphGetStarterPack, 5 type BskyAgent, 6 type ComAtprotoRepoApplyWrites, 7 type Facet, 8} from '@atproto/api' 9import {msg} from '@lingui/core/macro' 10import {useLingui} from '@lingui/react' 11import {useMutation} from '@tanstack/react-query' 12 13import {until} from '#/lib/async/until' 14import {sanitizeDisplayName} from '#/lib/strings/display-names' 15import {sanitizeHandle} from '#/lib/strings/handles' 16import {enforceLen} from '#/lib/strings/helpers' 17import {useAgent} from '#/state/session' 18import {pdsAgent} from '#/state/session/agent' 19import type * as bsky from '#/types/bsky' 20 21export const createStarterPackList = async ({ 22 name, 23 description, 24 descriptionFacets, 25 profiles, 26 agent, 27}: { 28 name: string 29 description?: string 30 descriptionFacets?: Facet[] 31 profiles: bsky.profile.AnyProfileView[] 32 agent: BskyAgent 33}): Promise<{uri: string; cid: string}> => { 34 if (profiles.length === 0) throw new Error('No profiles given') 35 36 const list = await agent.app.bsky.graph.list.create( 37 {repo: agent.session!.did}, 38 { 39 name, 40 description, 41 descriptionFacets, 42 avatar: undefined, 43 createdAt: new Date().toISOString(), 44 purpose: 'app.bsky.graph.defs#referencelist', 45 }, 46 ) 47 if (!list) throw new Error('List creation failed') 48 await pdsAgent(agent).com.atproto.repo.applyWrites({ 49 repo: agent.session!.did, 50 writes: profiles.map(p => createListItem({did: p.did, listUri: list.uri})), 51 }) 52 53 return list 54} 55 56export function useGenerateStarterPackMutation({ 57 onSuccess, 58 onError, 59}: { 60 onSuccess: ({uri, cid}: {uri: string; cid: string}) => void 61 onError: (e: Error) => void 62}) { 63 const {_} = useLingui() 64 const agent = useAgent() 65 66 return useMutation<{uri: string; cid: string}, Error, void>({ 67 mutationFn: async () => { 68 let profile: AppBskyActorDefs.ProfileViewDetailed | undefined 69 let profiles: AppBskyActorDefs.ProfileView[] | undefined 70 71 await Promise.all([ 72 (async () => { 73 profile = ( 74 await agent.app.bsky.actor.getProfile({ 75 actor: agent.session!.did, 76 }) 77 ).data 78 })(), 79 (async () => { 80 profiles = ( 81 await agent.app.bsky.actor.searchActors({ 82 q: encodeURIComponent('*'), 83 limit: 49, 84 }) 85 ).data.actors.filter(p => p.viewer?.following) 86 })(), 87 ]) 88 89 if (!profile || !profiles) { 90 throw new Error('ERROR_DATA') 91 } 92 93 // We include ourselves when we make the list 94 if (profiles.length < 7) { 95 throw new Error('NOT_ENOUGH_FOLLOWERS') 96 } 97 98 const displayName = enforceLen( 99 profile.displayName 100 ? sanitizeDisplayName(profile.displayName) 101 : `@${sanitizeHandle(profile.handle)}`, 102 25, 103 true, 104 ) 105 const starterPackName = _(msg`${displayName}'s Starter Pack`) 106 107 const list = await createStarterPackList({ 108 name: starterPackName, 109 profiles, 110 agent, 111 }) 112 113 return await agent.app.bsky.graph.starterpack.create( 114 { 115 repo: agent.session!.did, 116 }, 117 { 118 name: starterPackName, 119 list: list.uri, 120 createdAt: new Date().toISOString(), 121 }, 122 ) 123 }, 124 onSuccess: async data => { 125 await whenAppViewReady(agent, data.uri, v => { 126 return typeof v?.data.starterPack.uri === 'string' 127 }) 128 onSuccess(data) 129 }, 130 onError: error => { 131 onError(error) 132 }, 133 }) 134} 135 136function createListItem({ 137 did, 138 listUri, 139}: { 140 did: string 141 listUri: string 142}): $Typed<ComAtprotoRepoApplyWrites.Create> { 143 return { 144 $type: 'com.atproto.repo.applyWrites#create', 145 collection: 'app.bsky.graph.listitem', 146 value: { 147 $type: 'app.bsky.graph.listitem', 148 subject: did, 149 list: listUri, 150 createdAt: new Date().toISOString(), 151 }, 152 } 153} 154 155async function whenAppViewReady( 156 agent: BskyAgent, 157 uri: string, 158 fn: (res?: AppBskyGraphGetStarterPack.Response) => boolean, 159) { 160 await until( 161 5, // 5 tries 162 1e3, // 1s delay between tries 163 fn, 164 () => agent.app.bsky.graph.getStarterPack({starterPack: uri}), 165 ) 166}