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