@atproto/bsky 0.0.237 → 0.0.239
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/dist/api/app/bsky/actor/searchActors.d.ts.map +1 -1
- package/dist/api/app/bsky/actor/searchActors.js +26 -1
- package/dist/api/app/bsky/actor/searchActors.js.map +1 -1
- package/dist/api/app/bsky/actor/searchActorsTypeahead.d.ts.map +1 -1
- package/dist/api/app/bsky/actor/searchActorsTypeahead.js +26 -2
- package/dist/api/app/bsky/actor/searchActorsTypeahead.js.map +1 -1
- package/dist/api/app/bsky/feed/getFeed.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getFeed.js +1 -0
- package/dist/api/app/bsky/feed/getFeed.js.map +1 -1
- package/dist/api/app/bsky/feed/searchPosts.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/searchPosts.js +56 -1
- package/dist/api/app/bsky/feed/searchPosts.js.map +1 -1
- package/dist/api/app/bsky/graph/searchStarterPacks.d.ts.map +1 -1
- package/dist/api/app/bsky/graph/searchStarterPacks.js +26 -1
- package/dist/api/app/bsky/graph/searchStarterPacks.js.map +1 -1
- package/dist/api/app/bsky/unspecced/getPopularFeedGenerators.d.ts.map +1 -1
- package/dist/api/app/bsky/unspecced/getPopularFeedGenerators.js +27 -6
- package/dist/api/app/bsky/unspecced/getPopularFeedGenerators.js.map +1 -1
- package/dist/api/com/atproto/repo/getRecord.js +2 -2
- package/dist/api/com/atproto/repo/getRecord.js.map +1 -1
- package/dist/data-plane/server/routes/feed-gens.d.ts.map +1 -1
- package/dist/data-plane/server/routes/feed-gens.js +21 -12
- package/dist/data-plane/server/routes/feed-gens.js.map +1 -1
- package/dist/data-plane/server/routes/search.d.ts.map +1 -1
- package/dist/data-plane/server/routes/search.js +62 -12
- package/dist/data-plane/server/routes/search.js.map +1 -1
- package/dist/feature-gates/gates.d.ts +1 -0
- package/dist/feature-gates/gates.d.ts.map +1 -1
- package/dist/feature-gates/gates.js +1 -0
- package/dist/feature-gates/gates.js.map +1 -1
- package/dist/hydration/hydrator.d.ts.map +1 -1
- package/dist/hydration/hydrator.js +3 -2
- package/dist/hydration/hydrator.js.map +1 -1
- package/dist/hydration/label.d.ts +1 -1
- package/dist/hydration/label.d.ts.map +1 -1
- package/dist/hydration/label.js +4 -4
- package/dist/hydration/label.js.map +1 -1
- package/dist/lexicons/app/bsky/actor/profile.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/actor/status.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/draft/defs.defs.d.ts +22 -0
- package/dist/lexicons/app/bsky/draft/defs.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/draft/defs.defs.js +11 -0
- package/dist/lexicons/app/bsky/draft/defs.defs.js.map +1 -1
- package/dist/lexicons/app/bsky/embed/gallery.d.ts +3 -0
- package/dist/lexicons/app/bsky/embed/gallery.d.ts.map +1 -0
- package/dist/lexicons/app/bsky/embed/gallery.defs.d.ts +130 -0
- package/dist/lexicons/app/bsky/embed/gallery.defs.d.ts.map +1 -0
- package/dist/lexicons/app/bsky/embed/gallery.defs.js +47 -0
- package/dist/lexicons/app/bsky/embed/gallery.defs.js.map +1 -0
- package/dist/lexicons/app/bsky/embed/gallery.js +6 -0
- package/dist/lexicons/app/bsky/embed/gallery.js.map +1 -0
- package/dist/lexicons/app/bsky/embed/record.defs.d.ts +2 -1
- package/dist/lexicons/app/bsky/embed/record.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/embed/record.defs.js +2 -0
- package/dist/lexicons/app/bsky/embed/record.defs.js.map +1 -1
- package/dist/lexicons/app/bsky/embed/recordWithMedia.defs.d.ts +13 -12
- package/dist/lexicons/app/bsky/embed/recordWithMedia.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/embed/recordWithMedia.defs.js +3 -0
- package/dist/lexicons/app/bsky/embed/recordWithMedia.defs.js.map +1 -1
- package/dist/lexicons/app/bsky/embed.d.ts +1 -0
- package/dist/lexicons/app/bsky/embed.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/embed.js +1 -0
- package/dist/lexicons/app/bsky/embed.js.map +1 -1
- package/dist/lexicons/app/bsky/feed/defs.defs.d.ts +2 -1
- package/dist/lexicons/app/bsky/feed/defs.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/feed/defs.defs.js +2 -0
- package/dist/lexicons/app/bsky/feed/defs.defs.js.map +1 -1
- package/dist/lexicons/app/bsky/feed/generator.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/feed/like.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/feed/post.defs.d.ts +12 -11
- package/dist/lexicons/app/bsky/feed/post.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/feed/post.defs.js +2 -0
- package/dist/lexicons/app/bsky/feed/post.defs.js.map +1 -1
- package/dist/lexicons/app/bsky/feed/postgate.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/feed/repost.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/feed/threadgate.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/graph/block.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/graph/follow.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/graph/list.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/graph/listblock.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/graph/listitem.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/graph/starterpack.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/graph/verification.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/labeler/service.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/notification/declaration.defs.d.ts.map +1 -1
- package/dist/lexicons/chat/bsky/actor/declaration.defs.d.ts.map +1 -1
- package/dist/lexicons/chat/bsky/group/createGroup.defs.d.ts +4 -4
- package/dist/lexicons/chat/bsky/group/createGroup.defs.js +2 -2
- package/dist/lexicons/chat/bsky/group/createGroup.defs.js.map +1 -1
- package/dist/lexicons/chat/bsky/group/defs.defs.d.ts +8 -1
- package/dist/lexicons/chat/bsky/group/defs.defs.d.ts.map +1 -1
- package/dist/lexicons/chat/bsky/group/defs.defs.js +5 -1
- package/dist/lexicons/chat/bsky/group/defs.defs.js.map +1 -1
- package/dist/lexicons/com/atproto/lexicon/schema.defs.d.ts.map +1 -1
- package/dist/lexicons/com/germnetwork/declaration.defs.d.ts.map +1 -1
- package/dist/lexicons/site/standard/document.defs.d.ts.map +1 -1
- package/dist/lexicons/site/standard/graph/recommend.defs.d.ts.map +1 -1
- package/dist/lexicons/site/standard/graph/subscription.defs.d.ts.map +1 -1
- package/dist/lexicons/site/standard/publication.defs.d.ts.map +1 -1
- package/dist/lexicons/site/standard/theme/basic.defs.d.ts.map +1 -1
- package/dist/proto/bsky_connect.d.ts +49 -2
- package/dist/proto/bsky_connect.d.ts.map +1 -1
- package/dist/proto/bsky_connect.js +49 -2
- package/dist/proto/bsky_connect.js.map +1 -1
- package/dist/proto/bsky_pb.d.ts +482 -0
- package/dist/proto/bsky_pb.d.ts.map +1 -1
- package/dist/proto/bsky_pb.js +608 -0
- package/dist/proto/bsky_pb.js.map +1 -1
- package/dist/views/index.d.ts +4 -1
- package/dist/views/index.d.ts.map +1 -1
- package/dist/views/index.js +39 -5
- package/dist/views/index.js.map +1 -1
- package/dist/views/types.d.ts +8 -2
- package/dist/views/types.d.ts.map +1 -1
- package/dist/views/types.js +2 -0
- package/dist/views/types.js.map +1 -1
- package/package.json +7 -7
- package/proto/bsky.proto +166 -2
- package/src/api/app/bsky/actor/searchActors.ts +35 -1
- package/src/api/app/bsky/actor/searchActorsTypeahead.ts +35 -2
- package/src/api/app/bsky/feed/getFeed.ts +1 -0
- package/src/api/app/bsky/feed/searchPosts.ts +60 -1
- package/src/api/app/bsky/graph/searchStarterPacks.ts +35 -1
- package/src/api/app/bsky/unspecced/getPopularFeedGenerators.ts +28 -6
- package/src/api/com/atproto/repo/getRecord.ts +2 -2
- package/src/data-plane/server/routes/feed-gens.ts +33 -14
- package/src/data-plane/server/routes/search.ts +81 -13
- package/src/feature-gates/gates.ts +1 -0
- package/src/hydration/hydrator.ts +3 -6
- package/src/hydration/label.ts +3 -3
- package/src/views/index.ts +61 -11
- package/src/views/types.ts +9 -0
- package/tests/data-plane/handle-invalidation.test.ts +2 -1
- package/tests/views/__snapshots__/posts.test.ts.snap +251 -0
- package/tests/views/drafts.test.ts +105 -0
- package/tests/views/posts.test.ts +134 -0
- package/tsconfig.build.json +2 -2
- package/tsconfig.build.tsbuildinfo +1 -1
- package/tsconfig.json +2 -2
- package/tsconfig.tests.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { atUri } from '@atproto/lex'
|
|
2
2
|
import { InvalidRequestError, Server } from '@atproto/xrpc-server'
|
|
3
3
|
import { AppContext } from '../../../../context.js'
|
|
4
4
|
import { com } from '../../../../lexicons/index.js'
|
|
@@ -21,7 +21,7 @@ export default function (server: Server, ctx: AppContext) {
|
|
|
21
21
|
throw new InvalidRequestError(`Could not find repo: ${repo}`)
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
const uri =
|
|
24
|
+
const uri = atUri(did, collection, rkey)
|
|
25
25
|
const result = await ctx.hydrator.getRecord(uri, includeTakedowns)
|
|
26
26
|
|
|
27
27
|
if (!result || (cid && result.cid !== cid)) {
|
|
@@ -45,22 +45,18 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
45
45
|
},
|
|
46
46
|
|
|
47
47
|
async searchFeedGenerators(req) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
.
|
|
55
|
-
|
|
56
|
-
ref('feed_generator.createdAt'),
|
|
57
|
-
ref('feed_generator.cid'),
|
|
48
|
+
return searchFeedGeneratorsImpl(db, req.query, req.limit)
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
async searchFeedGeneratorsV2(req) {
|
|
52
|
+
const { uris, cursor } = await searchFeedGeneratorsImpl(
|
|
53
|
+
db,
|
|
54
|
+
req.params?.query ?? '',
|
|
55
|
+
req.params?.limit ?? 25,
|
|
58
56
|
)
|
|
59
|
-
builder = paginate(builder, { limit, keyset })
|
|
60
|
-
const feeds = await builder.execute()
|
|
61
57
|
return {
|
|
62
|
-
|
|
63
|
-
cursor:
|
|
58
|
+
feedGenerators: uris.map((uri) => ({ uri, score: 0 })),
|
|
59
|
+
pageInfo: { cursor: cursor ?? '', hitsTotal: 0n },
|
|
64
60
|
}
|
|
65
61
|
},
|
|
66
62
|
|
|
@@ -68,3 +64,26 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
68
64
|
throw new Error('unimplemented')
|
|
69
65
|
},
|
|
70
66
|
})
|
|
67
|
+
|
|
68
|
+
const searchFeedGeneratorsImpl = async (
|
|
69
|
+
db: Database,
|
|
70
|
+
query: string,
|
|
71
|
+
limit: number,
|
|
72
|
+
) => {
|
|
73
|
+
const { ref } = db.db.dynamic
|
|
74
|
+
const trimmed = query.trim()
|
|
75
|
+
let builder = db.db
|
|
76
|
+
.selectFrom('feed_generator')
|
|
77
|
+
.if(!!trimmed, (q) => q.where('displayName', 'ilike', `%${trimmed}%`))
|
|
78
|
+
.selectAll()
|
|
79
|
+
const keyset = new TimeCidKeyset(
|
|
80
|
+
ref('feed_generator.createdAt'),
|
|
81
|
+
ref('feed_generator.cid'),
|
|
82
|
+
)
|
|
83
|
+
builder = paginate(builder, { limit, keyset })
|
|
84
|
+
const feeds = await builder.execute()
|
|
85
|
+
return {
|
|
86
|
+
uris: feeds.map((f) => f.uri),
|
|
87
|
+
cursor: keyset.packFromResult(feeds),
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -8,9 +8,12 @@ import {
|
|
|
8
8
|
} from '../db/pagination.js'
|
|
9
9
|
import { parsePostSearchQuery } from '../util.js'
|
|
10
10
|
|
|
11
|
-
export default (db: Database): Partial<ServiceImpl<typeof Service>> =>
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
export default (db: Database): Partial<ServiceImpl<typeof Service>> => {
|
|
12
|
+
const searchActorsImpl = async (req: {
|
|
13
|
+
term: string
|
|
14
|
+
limit: number
|
|
15
|
+
cursor?: string
|
|
16
|
+
}) => {
|
|
14
17
|
const { term, limit, cursor } = req
|
|
15
18
|
const { ref } = db.db.dynamic
|
|
16
19
|
let builder = db.db
|
|
@@ -35,10 +38,13 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
35
38
|
dids: res.map((row) => row.did),
|
|
36
39
|
cursor: keyset.packFromResult(res),
|
|
37
40
|
}
|
|
38
|
-
}
|
|
41
|
+
}
|
|
39
42
|
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
const searchPostsImpl = async (req: {
|
|
44
|
+
term: string
|
|
45
|
+
limit: number
|
|
46
|
+
cursor?: string
|
|
47
|
+
}) => {
|
|
42
48
|
const { term, limit, cursor } = req
|
|
43
49
|
const { q, author } = parsePostSearchQuery(term)
|
|
44
50
|
|
|
@@ -75,9 +81,13 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
75
81
|
uris: res.map((row) => row.uri),
|
|
76
82
|
cursor: keyset.packFromResult(res),
|
|
77
83
|
}
|
|
78
|
-
}
|
|
84
|
+
}
|
|
79
85
|
|
|
80
|
-
async
|
|
86
|
+
const searchStarterPacksImpl = async (req: {
|
|
87
|
+
term: string
|
|
88
|
+
limit: number
|
|
89
|
+
cursor?: string
|
|
90
|
+
}) => {
|
|
81
91
|
const { term, limit, cursor } = req
|
|
82
92
|
const { ref } = db.db.dynamic
|
|
83
93
|
let builder = db.db
|
|
@@ -99,14 +109,72 @@ export default (db: Database): Partial<ServiceImpl<typeof Service>> => ({
|
|
|
99
109
|
|
|
100
110
|
const res = await builder.execute()
|
|
101
111
|
|
|
102
|
-
const cur = keyset.packFromResult(res)
|
|
103
|
-
|
|
104
112
|
return {
|
|
105
113
|
uris: res.map((row) => row.uri),
|
|
106
|
-
cursor:
|
|
114
|
+
cursor: keyset.packFromResult(res),
|
|
107
115
|
}
|
|
108
|
-
}
|
|
109
|
-
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
// @TODO actor search endpoints still fall back to search service
|
|
120
|
+
searchActors: searchActorsImpl,
|
|
121
|
+
|
|
122
|
+
// @TODO post search endpoint still falls back to search service
|
|
123
|
+
searchPosts: searchPostsImpl,
|
|
124
|
+
|
|
125
|
+
searchStarterPacks: searchStarterPacksImpl,
|
|
126
|
+
|
|
127
|
+
// V2 endpoints reuse the V1 SQL for dev env and reshape the response.
|
|
128
|
+
async searchActorsV2(req) {
|
|
129
|
+
const { dids, cursor } = await searchActorsImpl({
|
|
130
|
+
term: req.params?.query ?? '',
|
|
131
|
+
limit: req.params?.limit ?? 25,
|
|
132
|
+
cursor: req.params?.cursor,
|
|
133
|
+
})
|
|
134
|
+
return {
|
|
135
|
+
actors: dids.map((did) => ({ did, score: 0 })),
|
|
136
|
+
pageInfo: { cursor: cursor ?? '', hitsTotal: 0n },
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
async searchActorsTypeahead(req) {
|
|
141
|
+
const { dids } = await searchActorsImpl({
|
|
142
|
+
term: req.query,
|
|
143
|
+
limit: req.limit || 10,
|
|
144
|
+
})
|
|
145
|
+
return {
|
|
146
|
+
actors: dids.map((did) => ({ did, score: 0 })),
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
async searchPostsV2(req) {
|
|
151
|
+
const author = req.filters?.authors?.[0]
|
|
152
|
+
const baseQuery = req.params?.query ?? ''
|
|
153
|
+
const term = author ? `${baseQuery} from:${author}` : baseQuery
|
|
154
|
+
const { uris, cursor } = await searchPostsImpl({
|
|
155
|
+
term,
|
|
156
|
+
limit: req.params?.limit ?? 25,
|
|
157
|
+
cursor: req.params?.cursor,
|
|
158
|
+
})
|
|
159
|
+
return {
|
|
160
|
+
posts: uris.map((uri) => ({ uri, score: 0 })),
|
|
161
|
+
pageInfo: { cursor: cursor ?? '', hitsTotal: 0n },
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
async searchStarterPacksV2(req) {
|
|
166
|
+
const { uris, cursor } = await searchStarterPacksImpl({
|
|
167
|
+
term: req.params?.query ?? '',
|
|
168
|
+
limit: req.params?.limit ?? 25,
|
|
169
|
+
cursor: req.params?.cursor,
|
|
170
|
+
})
|
|
171
|
+
return {
|
|
172
|
+
starterPacks: uris.map((uri) => ({ uri, score: 0 })),
|
|
173
|
+
pageInfo: { cursor: cursor ?? '', hitsTotal: 0n },
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
}
|
|
177
|
+
}
|
|
110
178
|
|
|
111
179
|
// Remove leading @ in case a handle is input that way
|
|
112
180
|
const cleanQuery = (query: string) => query.trim().replace(/^@/g, '')
|
|
@@ -11,6 +11,7 @@ export enum Gate {
|
|
|
11
11
|
SuggestedUsersForExploreEnable = 'suggested_users:for_explore:enable',
|
|
12
12
|
SuggestedUsersForDiscoverEnable = 'suggested_users:for_discover:enable',
|
|
13
13
|
SuggestedUsersForSeeMoreEnable = 'suggested_users:for_see_more:enable',
|
|
14
|
+
SearchV2Enable = 'search:v2:enable',
|
|
14
15
|
|
|
15
16
|
// temp
|
|
16
17
|
AATest = 'aa-test-appview',
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import assert from 'node:assert'
|
|
2
2
|
import { dedupeStrs, mapDefined } from '@atproto/common'
|
|
3
|
+
import { atUri } from '@atproto/lex'
|
|
3
4
|
import { AtUri, AtUriString, DidString, UriString } from '@atproto/syntax'
|
|
4
5
|
import { DataPlaneClient } from '../data-plane/client/index.js'
|
|
5
6
|
import {
|
|
@@ -1575,12 +1576,8 @@ const isModList = (
|
|
|
1575
1576
|
const labelSubjectsForDid = (dids: DidString[]) => {
|
|
1576
1577
|
return [
|
|
1577
1578
|
...dids,
|
|
1578
|
-
...dids.map((did) =>
|
|
1579
|
-
|
|
1580
|
-
),
|
|
1581
|
-
...dids.map((did) =>
|
|
1582
|
-
AtUri.make(did, app.bsky.actor.status.$type, 'self').toString(),
|
|
1583
|
-
),
|
|
1579
|
+
...dids.map((did) => atUri(did, app.bsky.actor.profile)),
|
|
1580
|
+
...dids.map((did) => atUri(did, app.bsky.actor.status)),
|
|
1584
1581
|
]
|
|
1585
1582
|
}
|
|
1586
1583
|
|
package/src/hydration/label.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AtUriString, DidString, UriString, atUri } from '@atproto/lex'
|
|
2
2
|
import { DataPlaneClient } from '../data-plane/client/index.js'
|
|
3
3
|
import { app, com } from '../lexicons/index.js'
|
|
4
4
|
import { ParsedLabelers } from '../util.js'
|
|
@@ -198,8 +198,8 @@ export class LabelHydrator {
|
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
-
|
|
202
|
-
return
|
|
201
|
+
function labelerDidToUri<T extends DidString>(did: T) {
|
|
202
|
+
return atUri(did, app.bsky.labeler.service)
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
const IMPERSONATION_LABEL = 'impersonation'
|
package/src/views/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
Un$Typed,
|
|
5
5
|
Unknown$TypedObject,
|
|
6
6
|
UriString,
|
|
7
|
+
atUri,
|
|
7
8
|
getBlobCidString,
|
|
8
9
|
} from '@atproto/lex'
|
|
9
10
|
import {
|
|
@@ -66,6 +67,10 @@ import {
|
|
|
66
67
|
ExternalEmbedView,
|
|
67
68
|
FeedViewPost,
|
|
68
69
|
FollowRecord,
|
|
70
|
+
GalleryEmbed,
|
|
71
|
+
GalleryEmbedView,
|
|
72
|
+
GalleryImageEmbed,
|
|
73
|
+
GalleryImageEmbedView,
|
|
69
74
|
GeneratorView,
|
|
70
75
|
GetPostThreadV2QueryParams,
|
|
71
76
|
ImagesEmbed,
|
|
@@ -115,6 +120,8 @@ import {
|
|
|
115
120
|
VideoEmbed,
|
|
116
121
|
VideoEmbedView,
|
|
117
122
|
isExternalEmbedType,
|
|
123
|
+
isGalleryEmbedType,
|
|
124
|
+
isGalleryImageEmbedType,
|
|
118
125
|
isImagesEmbedType,
|
|
119
126
|
isLabelerRecordType,
|
|
120
127
|
isListRuleType,
|
|
@@ -135,6 +142,11 @@ const notificationDeletedRecord =
|
|
|
135
142
|
const notificationDeletedRecordCid =
|
|
136
143
|
'bafyreidad6nyekfa4a67yfb573ptxiv6s7kyxyg2ra6qbbemcruadvtuim'
|
|
137
144
|
|
|
145
|
+
// Soft-limit for `app.bsky.embed.gallery#main.items`. The lexicon's
|
|
146
|
+
// schema-level cap is 20, but clients are expected to enforce a soft limit
|
|
147
|
+
// of 10 today. The AppView trims defensively at the view boundary.
|
|
148
|
+
const GALLERY_SOFT_LIMIT = 10
|
|
149
|
+
|
|
138
150
|
export class Views {
|
|
139
151
|
public imgUriBuilder: ImageUriBuilder = this.opts.imgUriBuilder
|
|
140
152
|
public videoUriBuilder: VideoUriBuilder = this.opts.videoUriBuilder
|
|
@@ -348,11 +360,7 @@ export class Views {
|
|
|
348
360
|
): Un$Typed<ProfileViewBasic> | undefined {
|
|
349
361
|
const actor = state.actors?.get(did)
|
|
350
362
|
if (!actor) return
|
|
351
|
-
const profileUri =
|
|
352
|
-
did,
|
|
353
|
-
app.bsky.actor.profile.$nsid,
|
|
354
|
-
'self',
|
|
355
|
-
).toString()
|
|
363
|
+
const profileUri = atUri(did, app.bsky.actor.profile)
|
|
356
364
|
const labels = [
|
|
357
365
|
...(state.labels?.getBySubject(did) ?? []),
|
|
358
366
|
...(state.labels?.getBySubject(profileUri) ?? []),
|
|
@@ -603,7 +611,7 @@ export class Views {
|
|
|
603
611
|
return undefined
|
|
604
612
|
}
|
|
605
613
|
|
|
606
|
-
const uri =
|
|
614
|
+
const uri = atUri(did, app.bsky.actor.status)
|
|
607
615
|
const labels = state.labels?.getBySubject(uri)
|
|
608
616
|
|
|
609
617
|
const minDuration = 5 * MINUTE
|
|
@@ -838,11 +846,7 @@ export class Views {
|
|
|
838
846
|
const viewer = state.labelerViewers?.get(did)
|
|
839
847
|
const aggs = state.labelerAggs?.get(did)
|
|
840
848
|
|
|
841
|
-
const uri =
|
|
842
|
-
did,
|
|
843
|
-
app.bsky.labeler.service.$type,
|
|
844
|
-
'self',
|
|
845
|
-
).toString()
|
|
849
|
+
const uri = atUri(did, app.bsky.labeler.service)
|
|
846
850
|
const labels = [
|
|
847
851
|
...(state.labels?.getBySubject(uri) ?? []),
|
|
848
852
|
...this.selfLabels({
|
|
@@ -2090,6 +2094,8 @@ export class Views {
|
|
|
2090
2094
|
return this.imagesEmbed(creatorFromUri(postUri), embed)
|
|
2091
2095
|
} else if (isVideoEmbedType(embed)) {
|
|
2092
2096
|
return this.videoEmbed(creatorFromUri(postUri), embed)
|
|
2097
|
+
} else if (isGalleryEmbedType(embed)) {
|
|
2098
|
+
return this.galleryEmbed(creatorFromUri(postUri), embed)
|
|
2093
2099
|
} else if (isExternalEmbedType(embed)) {
|
|
2094
2100
|
return this.externalEmbed(creatorFromUri(postUri), embed, state)
|
|
2095
2101
|
} else if (isRecordEmbedType(embed)) {
|
|
@@ -2133,6 +2139,47 @@ export class Views {
|
|
|
2133
2139
|
})
|
|
2134
2140
|
}
|
|
2135
2141
|
|
|
2142
|
+
galleryEmbed(did: DidString, embed: GalleryEmbed): $Typed<GalleryEmbedView> {
|
|
2143
|
+
// The lexicon's schema-level cap is 20, but clients are expected to
|
|
2144
|
+
// enforce a soft limit of 10. Trim defensively at the view boundary so
|
|
2145
|
+
// viewers see at most 10 items regardless of what was authored.
|
|
2146
|
+
const items = embed.items.slice(0, GALLERY_SOFT_LIMIT).flatMap((item) => {
|
|
2147
|
+
const view = this.galleryItemView(did, item)
|
|
2148
|
+
return view ? [view] : []
|
|
2149
|
+
})
|
|
2150
|
+
return app.bsky.embed.gallery.view.$build({ items })
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
private galleryItemView(
|
|
2154
|
+
did: DidString,
|
|
2155
|
+
item: GalleryEmbed['items'][number],
|
|
2156
|
+
): $Typed<GalleryImageEmbedView> | undefined {
|
|
2157
|
+
if (isGalleryImageEmbedType(item)) {
|
|
2158
|
+
return this.galleryImageView(did, item)
|
|
2159
|
+
}
|
|
2160
|
+
return undefined
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2163
|
+
private galleryImageView(
|
|
2164
|
+
did: DidString,
|
|
2165
|
+
item: GalleryImageEmbed,
|
|
2166
|
+
): $Typed<GalleryImageEmbedView> {
|
|
2167
|
+
return app.bsky.embed.gallery.viewImage.$build({
|
|
2168
|
+
thumbnail: this.imgUriBuilder.getPresetUri(
|
|
2169
|
+
'feed_thumbnail',
|
|
2170
|
+
did,
|
|
2171
|
+
getBlobCidString(item.image),
|
|
2172
|
+
),
|
|
2173
|
+
fullsize: this.imgUriBuilder.getPresetUri(
|
|
2174
|
+
'feed_fullsize',
|
|
2175
|
+
did,
|
|
2176
|
+
getBlobCidString(item.image),
|
|
2177
|
+
),
|
|
2178
|
+
alt: item.alt,
|
|
2179
|
+
aspectRatio: item.aspectRatio,
|
|
2180
|
+
})
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2136
2183
|
externalEmbed(
|
|
2137
2184
|
did: DidString,
|
|
2138
2185
|
embed: ExternalEmbed,
|
|
@@ -2528,11 +2575,14 @@ export class Views {
|
|
|
2528
2575
|
let mediaEmbed:
|
|
2529
2576
|
| $Typed<ImagesEmbedView>
|
|
2530
2577
|
| $Typed<VideoEmbedView>
|
|
2578
|
+
| $Typed<GalleryEmbedView>
|
|
2531
2579
|
| $Typed<ExternalEmbedView>
|
|
2532
2580
|
if (isImagesEmbedType(embed.media)) {
|
|
2533
2581
|
mediaEmbed = this.imagesEmbed(creator, embed.media)
|
|
2534
2582
|
} else if (isVideoEmbedType(embed.media)) {
|
|
2535
2583
|
mediaEmbed = this.videoEmbed(creator, embed.media)
|
|
2584
|
+
} else if (isGalleryEmbedType(embed.media)) {
|
|
2585
|
+
mediaEmbed = this.galleryEmbed(creator, embed.media)
|
|
2536
2586
|
} else if (isExternalEmbedType(embed.media)) {
|
|
2537
2587
|
mediaEmbed = this.externalEmbed(creator, embed.media, state)
|
|
2538
2588
|
} else {
|
package/src/views/types.ts
CHANGED
|
@@ -33,6 +33,13 @@ export const isVideoEmbedType = app.bsky.embed.video.$isTypeOf
|
|
|
33
33
|
export type VideoEmbed = app.bsky.embed.video.Main
|
|
34
34
|
export type VideoEmbedView = app.bsky.embed.video.View
|
|
35
35
|
|
|
36
|
+
export const isGalleryEmbedType = app.bsky.embed.gallery.$isTypeOf
|
|
37
|
+
export type GalleryEmbed = app.bsky.embed.gallery.Main
|
|
38
|
+
export type GalleryEmbedView = app.bsky.embed.gallery.View
|
|
39
|
+
export const isGalleryImageEmbedType = app.bsky.embed.gallery.image.$isTypeOf
|
|
40
|
+
export type GalleryImageEmbed = app.bsky.embed.gallery.Image
|
|
41
|
+
export type GalleryImageEmbedView = app.bsky.embed.gallery.ViewImage
|
|
42
|
+
|
|
36
43
|
export const isExternalEmbedType = app.bsky.embed.external.$isTypeOf
|
|
37
44
|
export type ExternalEmbed = app.bsky.embed.external.Main
|
|
38
45
|
export type ExternalEmbedView = app.bsky.embed.external.View
|
|
@@ -57,6 +64,7 @@ export type RecordWithMediaEmbedView = app.bsky.embed.recordWithMedia.View
|
|
|
57
64
|
export type Embed =
|
|
58
65
|
| ImagesEmbed
|
|
59
66
|
| VideoEmbed
|
|
67
|
+
| GalleryEmbed
|
|
60
68
|
| ExternalEmbed
|
|
61
69
|
| RecordEmbed
|
|
62
70
|
| RecordWithMedia
|
|
@@ -64,6 +72,7 @@ export type Embed =
|
|
|
64
72
|
export type EmbedView =
|
|
65
73
|
| ImagesEmbedView
|
|
66
74
|
| VideoEmbedView
|
|
75
|
+
| GalleryEmbedView
|
|
67
76
|
| ExternalEmbedView
|
|
68
77
|
| RecordEmbedView
|
|
69
78
|
| RecordWithMediaView
|
|
@@ -116,7 +116,8 @@ describe('handle invalidation', () => {
|
|
|
116
116
|
|
|
117
117
|
it('deals with handle contention', async () => {
|
|
118
118
|
await backdateIndexedAt(bob)
|
|
119
|
-
//
|
|
119
|
+
// make alice lose her handle so that the pds will let bob take her old handle
|
|
120
|
+
mockHandles['not-alice.test'] = null
|
|
120
121
|
await network.pds.ctx.accountManager.updateHandle(alice, 'not-alice.test')
|
|
121
122
|
|
|
122
123
|
await pdsAgent.api.com.atproto.identity.updateHandle(
|
|
@@ -1,5 +1,256 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
|
+
exports[`pds posts views > embeds gallery with record. 1`] = `
|
|
4
|
+
{
|
|
5
|
+
"author": {
|
|
6
|
+
"associated": {
|
|
7
|
+
"activitySubscription": {
|
|
8
|
+
"allowSubscriptions": "followers",
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
"avatar": "https://bsky.public.url/img/avatar/plain/user(1)/cids(1)",
|
|
12
|
+
"createdAt": "1970-01-01T00:00:00.000Z",
|
|
13
|
+
"did": "user(0)",
|
|
14
|
+
"displayName": "ali",
|
|
15
|
+
"handle": "alice.test",
|
|
16
|
+
"labels": [
|
|
17
|
+
{
|
|
18
|
+
"cid": "cids(2)",
|
|
19
|
+
"cts": "1970-01-01T00:00:00.000Z",
|
|
20
|
+
"src": "user(0)",
|
|
21
|
+
"uri": "record(1)",
|
|
22
|
+
"val": "self-label-a",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"cid": "cids(2)",
|
|
26
|
+
"cts": "1970-01-01T00:00:00.000Z",
|
|
27
|
+
"src": "user(0)",
|
|
28
|
+
"uri": "record(1)",
|
|
29
|
+
"val": "self-label-b",
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
"bookmarkCount": 0,
|
|
34
|
+
"cid": "cids(0)",
|
|
35
|
+
"embed": {
|
|
36
|
+
"$type": "app.bsky.embed.recordWithMedia#view",
|
|
37
|
+
"media": {
|
|
38
|
+
"$type": "app.bsky.embed.gallery#view",
|
|
39
|
+
"items": [
|
|
40
|
+
{
|
|
41
|
+
"$type": "app.bsky.embed.gallery#viewImage",
|
|
42
|
+
"alt": "landscape",
|
|
43
|
+
"aspectRatio": {
|
|
44
|
+
"height": 3,
|
|
45
|
+
"width": 4,
|
|
46
|
+
},
|
|
47
|
+
"fullsize": "https://bsky.public.url/img/feed_fullsize/plain/user(1)/cids(3)",
|
|
48
|
+
"thumbnail": "https://bsky.public.url/img/feed_thumbnail/plain/user(1)/cids(3)",
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
"record": {
|
|
53
|
+
"record": {
|
|
54
|
+
"$type": "app.bsky.embed.record#viewRecord",
|
|
55
|
+
"author": {
|
|
56
|
+
"associated": {
|
|
57
|
+
"activitySubscription": {
|
|
58
|
+
"allowSubscriptions": "followers",
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
"avatar": "https://bsky.public.url/img/avatar/plain/user(1)/cids(1)",
|
|
62
|
+
"createdAt": "1970-01-01T00:00:00.000Z",
|
|
63
|
+
"did": "user(0)",
|
|
64
|
+
"displayName": "ali",
|
|
65
|
+
"handle": "alice.test",
|
|
66
|
+
"labels": [
|
|
67
|
+
{
|
|
68
|
+
"cid": "cids(2)",
|
|
69
|
+
"cts": "1970-01-01T00:00:00.000Z",
|
|
70
|
+
"src": "user(0)",
|
|
71
|
+
"uri": "record(1)",
|
|
72
|
+
"val": "self-label-a",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"cid": "cids(2)",
|
|
76
|
+
"cts": "1970-01-01T00:00:00.000Z",
|
|
77
|
+
"src": "user(0)",
|
|
78
|
+
"uri": "record(1)",
|
|
79
|
+
"val": "self-label-b",
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
"cid": "cids(4)",
|
|
84
|
+
"embeds": [],
|
|
85
|
+
"indexedAt": "1970-01-01T00:00:00.000Z",
|
|
86
|
+
"labels": [],
|
|
87
|
+
"likeCount": 0,
|
|
88
|
+
"quoteCount": 1,
|
|
89
|
+
"replyCount": 0,
|
|
90
|
+
"repostCount": 0,
|
|
91
|
+
"uri": "record(2)",
|
|
92
|
+
"value": {
|
|
93
|
+
"$type": "app.bsky.feed.post",
|
|
94
|
+
"createdAt": "1970-01-01T00:00:00.000Z",
|
|
95
|
+
"text": "embedded",
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
"indexedAt": "1970-01-01T00:00:00.000Z",
|
|
101
|
+
"labels": [],
|
|
102
|
+
"likeCount": 0,
|
|
103
|
+
"quoteCount": 0,
|
|
104
|
+
"record": {
|
|
105
|
+
"$type": "app.bsky.feed.post",
|
|
106
|
+
"createdAt": "1970-01-01T00:00:00.000Z",
|
|
107
|
+
"embed": {
|
|
108
|
+
"$type": "app.bsky.embed.recordWithMedia",
|
|
109
|
+
"media": {
|
|
110
|
+
"$type": "app.bsky.embed.gallery",
|
|
111
|
+
"items": [
|
|
112
|
+
{
|
|
113
|
+
"$type": "app.bsky.embed.gallery#image",
|
|
114
|
+
"alt": "landscape",
|
|
115
|
+
"aspectRatio": {
|
|
116
|
+
"height": 3,
|
|
117
|
+
"width": 4,
|
|
118
|
+
},
|
|
119
|
+
"image": {
|
|
120
|
+
"$type": "blob",
|
|
121
|
+
"mimeType": "image/jpeg",
|
|
122
|
+
"ref": {
|
|
123
|
+
"$link": "cids(3)",
|
|
124
|
+
},
|
|
125
|
+
"size": 4114,
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
"record": {
|
|
131
|
+
"record": {
|
|
132
|
+
"cid": "cids(4)",
|
|
133
|
+
"uri": "record(2)",
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
"text": "gallery + record",
|
|
138
|
+
},
|
|
139
|
+
"replyCount": 0,
|
|
140
|
+
"repostCount": 0,
|
|
141
|
+
"uri": "record(0)",
|
|
142
|
+
}
|
|
143
|
+
`;
|
|
144
|
+
|
|
145
|
+
exports[`pds posts views > embeds gallery. 1`] = `
|
|
146
|
+
{
|
|
147
|
+
"author": {
|
|
148
|
+
"associated": {
|
|
149
|
+
"activitySubscription": {
|
|
150
|
+
"allowSubscriptions": "followers",
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
"avatar": "https://bsky.public.url/img/avatar/plain/user(1)/cids(1)",
|
|
154
|
+
"createdAt": "1970-01-01T00:00:00.000Z",
|
|
155
|
+
"did": "user(0)",
|
|
156
|
+
"displayName": "ali",
|
|
157
|
+
"handle": "alice.test",
|
|
158
|
+
"labels": [
|
|
159
|
+
{
|
|
160
|
+
"cid": "cids(2)",
|
|
161
|
+
"cts": "1970-01-01T00:00:00.000Z",
|
|
162
|
+
"src": "user(0)",
|
|
163
|
+
"uri": "record(1)",
|
|
164
|
+
"val": "self-label-a",
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"cid": "cids(2)",
|
|
168
|
+
"cts": "1970-01-01T00:00:00.000Z",
|
|
169
|
+
"src": "user(0)",
|
|
170
|
+
"uri": "record(1)",
|
|
171
|
+
"val": "self-label-b",
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
},
|
|
175
|
+
"bookmarkCount": 0,
|
|
176
|
+
"cid": "cids(0)",
|
|
177
|
+
"embed": {
|
|
178
|
+
"$type": "app.bsky.embed.gallery#view",
|
|
179
|
+
"items": [
|
|
180
|
+
{
|
|
181
|
+
"$type": "app.bsky.embed.gallery#viewImage",
|
|
182
|
+
"alt": "landscape",
|
|
183
|
+
"aspectRatio": {
|
|
184
|
+
"height": 3,
|
|
185
|
+
"width": 4,
|
|
186
|
+
},
|
|
187
|
+
"fullsize": "https://bsky.public.url/img/feed_fullsize/plain/user(1)/cids(3)",
|
|
188
|
+
"thumbnail": "https://bsky.public.url/img/feed_thumbnail/plain/user(1)/cids(3)",
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"$type": "app.bsky.embed.gallery#viewImage",
|
|
192
|
+
"alt": "portrait",
|
|
193
|
+
"aspectRatio": {
|
|
194
|
+
"height": 4,
|
|
195
|
+
"width": 3,
|
|
196
|
+
},
|
|
197
|
+
"fullsize": "https://bsky.public.url/img/feed_fullsize/plain/user(1)/cids(1)",
|
|
198
|
+
"thumbnail": "https://bsky.public.url/img/feed_thumbnail/plain/user(1)/cids(1)",
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
},
|
|
202
|
+
"indexedAt": "1970-01-01T00:00:00.000Z",
|
|
203
|
+
"labels": [],
|
|
204
|
+
"likeCount": 0,
|
|
205
|
+
"quoteCount": 0,
|
|
206
|
+
"record": {
|
|
207
|
+
"$type": "app.bsky.feed.post",
|
|
208
|
+
"createdAt": "1970-01-01T00:00:00.000Z",
|
|
209
|
+
"embed": {
|
|
210
|
+
"$type": "app.bsky.embed.gallery",
|
|
211
|
+
"items": [
|
|
212
|
+
{
|
|
213
|
+
"$type": "app.bsky.embed.gallery#image",
|
|
214
|
+
"alt": "landscape",
|
|
215
|
+
"aspectRatio": {
|
|
216
|
+
"height": 3,
|
|
217
|
+
"width": 4,
|
|
218
|
+
},
|
|
219
|
+
"image": {
|
|
220
|
+
"$type": "blob",
|
|
221
|
+
"mimeType": "image/jpeg",
|
|
222
|
+
"ref": {
|
|
223
|
+
"$link": "cids(3)",
|
|
224
|
+
},
|
|
225
|
+
"size": 4114,
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"$type": "app.bsky.embed.gallery#image",
|
|
230
|
+
"alt": "portrait",
|
|
231
|
+
"aspectRatio": {
|
|
232
|
+
"height": 4,
|
|
233
|
+
"width": 3,
|
|
234
|
+
},
|
|
235
|
+
"image": {
|
|
236
|
+
"$type": "blob",
|
|
237
|
+
"mimeType": "image/jpeg",
|
|
238
|
+
"ref": {
|
|
239
|
+
"$link": "cids(1)",
|
|
240
|
+
},
|
|
241
|
+
"size": 3976,
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
},
|
|
246
|
+
"text": "gallery",
|
|
247
|
+
},
|
|
248
|
+
"replyCount": 0,
|
|
249
|
+
"repostCount": 0,
|
|
250
|
+
"uri": "record(0)",
|
|
251
|
+
}
|
|
252
|
+
`;
|
|
253
|
+
|
|
3
254
|
exports[`pds posts views > embeds video with record. 1`] = `
|
|
4
255
|
{
|
|
5
256
|
"author": {
|