forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
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/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}