@atproto/bsky 0.0.83 → 0.0.84
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 +14 -0
- package/dist/api/app/bsky/feed/getAuthorFeed.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getAuthorFeed.js +27 -7
- package/dist/api/app/bsky/feed/getAuthorFeed.js.map +1 -1
- package/dist/api/app/bsky/graph/getSuggestedFollowsByActor.js +3 -1
- package/dist/api/app/bsky/graph/getSuggestedFollowsByActor.js.map +1 -1
- package/dist/data-plane/server/db/migrations/20240831T134810923Z-pinned-posts.d.ts +4 -0
- package/dist/data-plane/server/db/migrations/20240831T134810923Z-pinned-posts.d.ts.map +1 -0
- package/dist/data-plane/server/db/migrations/20240831T134810923Z-pinned-posts.js +20 -0
- package/dist/data-plane/server/db/migrations/20240831T134810923Z-pinned-posts.js.map +1 -0
- package/dist/data-plane/server/db/migrations/index.d.ts +1 -0
- package/dist/data-plane/server/db/migrations/index.d.ts.map +1 -1
- package/dist/data-plane/server/db/migrations/index.js +2 -1
- package/dist/data-plane/server/db/migrations/index.js.map +1 -1
- package/dist/data-plane/server/db/tables/profile.d.ts +2 -0
- package/dist/data-plane/server/db/tables/profile.d.ts.map +1 -1
- package/dist/hydration/feed.d.ts +5 -0
- package/dist/hydration/feed.d.ts.map +1 -1
- package/dist/hydration/feed.js.map +1 -1
- package/dist/lexicon/lexicons.d.ts +33 -0
- package/dist/lexicon/lexicons.d.ts.map +1 -1
- package/dist/lexicon/lexicons.js +42 -3
- package/dist/lexicon/lexicons.js.map +1 -1
- package/dist/lexicon/types/app/bsky/actor/defs.d.ts +2 -0
- package/dist/lexicon/types/app/bsky/actor/defs.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/actor/defs.js.map +1 -1
- package/dist/lexicon/types/app/bsky/actor/profile.d.ts +1 -0
- package/dist/lexicon/types/app/bsky/actor/profile.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/actor/profile.js.map +1 -1
- package/dist/lexicon/types/app/bsky/feed/defs.d.ts +13 -2
- package/dist/lexicon/types/app/bsky/feed/defs.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/feed/defs.js +21 -1
- package/dist/lexicon/types/app/bsky/feed/defs.js.map +1 -1
- package/dist/lexicon/types/app/bsky/feed/getAuthorFeed.d.ts +1 -0
- package/dist/lexicon/types/app/bsky/feed/getAuthorFeed.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/graph/getSuggestedFollowsByActor.d.ts +2 -0
- package/dist/lexicon/types/app/bsky/graph/getSuggestedFollowsByActor.d.ts.map +1 -1
- package/dist/lexicon/types/app/bsky/unspecced/getSuggestionsSkeleton.d.ts +2 -0
- package/dist/lexicon/types/app/bsky/unspecced/getSuggestionsSkeleton.d.ts.map +1 -1
- package/dist/views/index.d.ts +4 -0
- package/dist/views/index.d.ts.map +1 -1
- package/dist/views/index.js +22 -1
- package/dist/views/index.js.map +1 -1
- package/package.json +12 -12
- package/src/api/app/bsky/feed/getAuthorFeed.ts +32 -7
- package/src/api/app/bsky/graph/getSuggestedFollowsByActor.ts +4 -1
- package/src/data-plane/server/db/migrations/20240831T134810923Z-pinned-posts.ts +17 -0
- package/src/data-plane/server/db/migrations/index.ts +1 -0
- package/src/data-plane/server/db/tables/profile.ts +2 -0
- package/src/hydration/feed.ts +9 -1
- package/src/lexicon/lexicons.ts +44 -3
- package/src/lexicon/types/app/bsky/actor/defs.ts +2 -0
- package/src/lexicon/types/app/bsky/actor/profile.ts +1 -0
- package/src/lexicon/types/app/bsky/feed/defs.ts +38 -2
- package/src/lexicon/types/app/bsky/feed/getAuthorFeed.ts +1 -0
- package/src/lexicon/types/app/bsky/graph/getSuggestedFollowsByActor.ts +2 -0
- package/src/lexicon/types/app/bsky/unspecced/getSuggestionsSkeleton.ts +2 -0
- package/src/views/index.ts +22 -2
- package/tests/views/__snapshots__/author-feed.test.ts.snap +1795 -0
- package/tests/views/author-feed.test.ts +132 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/bsky",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.84",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Reference implementation of app.bsky App View (Bluesky API)",
|
|
6
6
|
"keywords": [
|
|
@@ -41,15 +41,15 @@
|
|
|
41
41
|
"structured-headers": "^1.0.1",
|
|
42
42
|
"typed-emitter": "^2.1.0",
|
|
43
43
|
"uint8arrays": "3.0.0",
|
|
44
|
-
"@atproto/api": "^0.13.
|
|
45
|
-
"@atproto/common": "^0.4.
|
|
44
|
+
"@atproto/api": "^0.13.8",
|
|
45
|
+
"@atproto/common": "^0.4.3",
|
|
46
46
|
"@atproto/crypto": "^0.4.1",
|
|
47
|
-
"@atproto/identity": "^0.4.
|
|
48
|
-
"@atproto/lexicon": "^0.4.
|
|
49
|
-
"@atproto/repo": "^0.5.
|
|
50
|
-
"@atproto/sync": "^0.1.
|
|
47
|
+
"@atproto/identity": "^0.4.2",
|
|
48
|
+
"@atproto/lexicon": "^0.4.2",
|
|
49
|
+
"@atproto/repo": "^0.5.2",
|
|
50
|
+
"@atproto/sync": "^0.1.2",
|
|
51
51
|
"@atproto/syntax": "^0.3.0",
|
|
52
|
-
"@atproto/xrpc-server": "^0.
|
|
52
|
+
"@atproto/xrpc-server": "^0.7.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@bufbuild/buf": "^1.28.1",
|
|
@@ -64,10 +64,10 @@
|
|
|
64
64
|
"axios": "^0.27.2",
|
|
65
65
|
"jest": "^28.1.2",
|
|
66
66
|
"ts-node": "^10.8.2",
|
|
67
|
-
"@atproto/api": "^0.13.
|
|
68
|
-
"@atproto/lex-cli": "^0.5.
|
|
69
|
-
"@atproto/pds": "^0.4.
|
|
70
|
-
"@atproto/xrpc": "^0.6.
|
|
67
|
+
"@atproto/api": "^0.13.8",
|
|
68
|
+
"@atproto/lex-cli": "^0.5.1",
|
|
69
|
+
"@atproto/pds": "^0.4.60",
|
|
70
|
+
"@atproto/xrpc": "^0.6.3"
|
|
71
71
|
},
|
|
72
72
|
"scripts": {
|
|
73
73
|
"codegen": "lex gen-server --yes ./src/lexicon ../../lexicons/com/atproto/*/* ../../lexicons/app/bsky/*/* ../../lexicons/chat/bsky/*/*",
|
|
@@ -79,21 +79,46 @@ export const skeleton = async (inputs: {
|
|
|
79
79
|
if (clearlyBadCursor(params.cursor)) {
|
|
80
80
|
return { actor, filter: params.filter, items: [] }
|
|
81
81
|
}
|
|
82
|
+
|
|
83
|
+
const isFirstPageRequest = !params.cursor
|
|
84
|
+
const shouldInsertPinnedPost =
|
|
85
|
+
isFirstPageRequest && params.includePins && !!actor.profile?.pinnedPost
|
|
86
|
+
|
|
82
87
|
const res = await ctx.dataplane.getAuthorFeed({
|
|
83
88
|
actorDid: did,
|
|
84
89
|
limit: params.limit,
|
|
85
90
|
cursor: params.cursor,
|
|
86
91
|
feedType: FILTER_TO_FEED_TYPE[params.filter],
|
|
87
92
|
})
|
|
93
|
+
|
|
94
|
+
let items: FeedItem[] = res.items.map((item) => ({
|
|
95
|
+
post: { uri: item.uri, cid: item.cid || undefined },
|
|
96
|
+
repost: item.repost
|
|
97
|
+
? { uri: item.repost, cid: item.repostCid || undefined }
|
|
98
|
+
: undefined,
|
|
99
|
+
}))
|
|
100
|
+
|
|
101
|
+
if (shouldInsertPinnedPost && actor.profile?.pinnedPost) {
|
|
102
|
+
const pinnedItem = {
|
|
103
|
+
post: {
|
|
104
|
+
uri: actor.profile.pinnedPost.uri,
|
|
105
|
+
cid: actor.profile.pinnedPost.cid,
|
|
106
|
+
},
|
|
107
|
+
authorPinned: true,
|
|
108
|
+
}
|
|
109
|
+
if (params.limit === 1) {
|
|
110
|
+
items[0] = pinnedItem
|
|
111
|
+
} else {
|
|
112
|
+
// filter pinned post from first page only
|
|
113
|
+
items = items.filter((item) => item.post.uri !== pinnedItem.post.uri)
|
|
114
|
+
items.unshift(pinnedItem)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
88
118
|
return {
|
|
89
119
|
actor,
|
|
90
120
|
filter: params.filter,
|
|
91
|
-
items
|
|
92
|
-
post: { uri: item.uri, cid: item.cid || undefined },
|
|
93
|
-
repost: item.repost
|
|
94
|
-
? { uri: item.repost, cid: item.repostCid || undefined }
|
|
95
|
-
: undefined,
|
|
96
|
-
})),
|
|
121
|
+
items,
|
|
97
122
|
cursor: parseString(res.cursor),
|
|
98
123
|
}
|
|
99
124
|
}
|
|
@@ -147,7 +172,7 @@ const noBlocksOrMutedReposts = (inputs: {
|
|
|
147
172
|
skeleton.items = skeleton.items.filter((item) => {
|
|
148
173
|
return (
|
|
149
174
|
checkBlocksAndMutes(item) &&
|
|
150
|
-
(item.repost || selfThread.ok(item.post.uri))
|
|
175
|
+
(item.repost || item.authorPinned || selfThread.ok(item.post.uri))
|
|
151
176
|
)
|
|
152
177
|
})
|
|
153
178
|
} else {
|
|
@@ -71,6 +71,7 @@ const skeleton = async (input: SkeletonFnInput<Context, Params>) => {
|
|
|
71
71
|
{ headers: params.headers },
|
|
72
72
|
)
|
|
73
73
|
return {
|
|
74
|
+
isFallback: !res.data.relativeToDid,
|
|
74
75
|
suggestedDids: res.data.actors.map((a) => a.did),
|
|
75
76
|
headers: res.headers,
|
|
76
77
|
}
|
|
@@ -80,6 +81,7 @@ const skeleton = async (input: SkeletonFnInput<Context, Params>) => {
|
|
|
80
81
|
relativeToDid,
|
|
81
82
|
})
|
|
82
83
|
return {
|
|
84
|
+
isFallback: true,
|
|
83
85
|
suggestedDids: dids,
|
|
84
86
|
}
|
|
85
87
|
}
|
|
@@ -113,7 +115,7 @@ const presentation = (
|
|
|
113
115
|
const suggestions = mapDefined(suggestedDids, (did) =>
|
|
114
116
|
ctx.views.profileDetailed(did, hydration),
|
|
115
117
|
)
|
|
116
|
-
return { suggestions, headers }
|
|
118
|
+
return { isFallback: skeleton.isFallback, suggestions, headers }
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
type Context = {
|
|
@@ -129,6 +131,7 @@ type Params = QueryParams & {
|
|
|
129
131
|
}
|
|
130
132
|
|
|
131
133
|
type SkeletonState = {
|
|
134
|
+
isFallback: boolean
|
|
132
135
|
suggestedDids: string[]
|
|
133
136
|
headers?: Record<string, string>
|
|
134
137
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Kysely } from 'kysely'
|
|
2
|
+
|
|
3
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
4
|
+
await db.schema
|
|
5
|
+
.alterTable('profile')
|
|
6
|
+
.addColumn('pinnedPost', 'varchar')
|
|
7
|
+
.execute()
|
|
8
|
+
await db.schema
|
|
9
|
+
.alterTable('profile')
|
|
10
|
+
.addColumn('pinnedPostCid', 'varchar')
|
|
11
|
+
.execute()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
15
|
+
await db.schema.alterTable('profile').dropColumn('pinnedPost').execute()
|
|
16
|
+
await db.schema.alterTable('profile').dropColumn('pinnedPostCid').execute()
|
|
17
|
+
}
|
|
@@ -43,3 +43,4 @@ export * as _20240723T220703655Z from './20240723T220703655Z-quotes'
|
|
|
43
43
|
export * as _20240801T193939827Z from './20240801T193939827Z-post-gate'
|
|
44
44
|
export * as _20240808T224251220Z from './20240808T224251220Z-post-gate-flags'
|
|
45
45
|
export * as _20240829T211238293Z from './20240829T211238293Z-simplify-actor-sync'
|
|
46
|
+
export * as _20240831T134810923Z from './20240831T134810923Z-pinned-posts'
|
package/src/hydration/feed.ts
CHANGED
|
@@ -71,7 +71,15 @@ export type ThreadRef = ItemRef & { threadRoot: string }
|
|
|
71
71
|
|
|
72
72
|
// @NOTE the feed item types in the protos for author feeds and timelines
|
|
73
73
|
// technically have additional fields, not supported by the mock dataplane.
|
|
74
|
-
export type FeedItem = {
|
|
74
|
+
export type FeedItem = {
|
|
75
|
+
post: ItemRef
|
|
76
|
+
repost?: ItemRef
|
|
77
|
+
/**
|
|
78
|
+
* If true, overrides the `reason` with `app.bsky.feed.defs#reasonPin`. Used
|
|
79
|
+
* only in author feeds.
|
|
80
|
+
*/
|
|
81
|
+
authorPinned?: boolean
|
|
82
|
+
}
|
|
75
83
|
|
|
76
84
|
export class FeedHydrator {
|
|
77
85
|
constructor(public dataplane: DataPlaneClient) {}
|
package/src/lexicon/lexicons.ts
CHANGED
|
@@ -4190,6 +4190,10 @@ export const schemaDict = {
|
|
|
4190
4190
|
ref: 'lex:com.atproto.label.defs#label',
|
|
4191
4191
|
},
|
|
4192
4192
|
},
|
|
4193
|
+
pinnedPost: {
|
|
4194
|
+
type: 'ref',
|
|
4195
|
+
ref: 'lex:com.atproto.repo.strongRef',
|
|
4196
|
+
},
|
|
4193
4197
|
},
|
|
4194
4198
|
},
|
|
4195
4199
|
profileAssociated: {
|
|
@@ -4812,6 +4816,10 @@ export const schemaDict = {
|
|
|
4812
4816
|
type: 'ref',
|
|
4813
4817
|
ref: 'lex:com.atproto.repo.strongRef',
|
|
4814
4818
|
},
|
|
4819
|
+
pinnedPost: {
|
|
4820
|
+
type: 'ref',
|
|
4821
|
+
ref: 'lex:com.atproto.repo.strongRef',
|
|
4822
|
+
},
|
|
4815
4823
|
createdAt: {
|
|
4816
4824
|
type: 'string',
|
|
4817
4825
|
format: 'datetime',
|
|
@@ -5468,6 +5476,9 @@ export const schemaDict = {
|
|
|
5468
5476
|
embeddingDisabled: {
|
|
5469
5477
|
type: 'boolean',
|
|
5470
5478
|
},
|
|
5479
|
+
pinned: {
|
|
5480
|
+
type: 'boolean',
|
|
5481
|
+
},
|
|
5471
5482
|
},
|
|
5472
5483
|
},
|
|
5473
5484
|
feedViewPost: {
|
|
@@ -5484,7 +5495,10 @@ export const schemaDict = {
|
|
|
5484
5495
|
},
|
|
5485
5496
|
reason: {
|
|
5486
5497
|
type: 'union',
|
|
5487
|
-
refs: [
|
|
5498
|
+
refs: [
|
|
5499
|
+
'lex:app.bsky.feed.defs#reasonRepost',
|
|
5500
|
+
'lex:app.bsky.feed.defs#reasonPin',
|
|
5501
|
+
],
|
|
5488
5502
|
},
|
|
5489
5503
|
feedContext: {
|
|
5490
5504
|
type: 'string',
|
|
@@ -5536,6 +5550,10 @@ export const schemaDict = {
|
|
|
5536
5550
|
},
|
|
5537
5551
|
},
|
|
5538
5552
|
},
|
|
5553
|
+
reasonPin: {
|
|
5554
|
+
type: 'object',
|
|
5555
|
+
properties: {},
|
|
5556
|
+
},
|
|
5539
5557
|
threadViewPost: {
|
|
5540
5558
|
type: 'object',
|
|
5541
5559
|
required: ['post'],
|
|
@@ -5693,7 +5711,10 @@ export const schemaDict = {
|
|
|
5693
5711
|
},
|
|
5694
5712
|
reason: {
|
|
5695
5713
|
type: 'union',
|
|
5696
|
-
refs: [
|
|
5714
|
+
refs: [
|
|
5715
|
+
'lex:app.bsky.feed.defs#skeletonReasonRepost',
|
|
5716
|
+
'lex:app.bsky.feed.defs#skeletonReasonPin',
|
|
5717
|
+
],
|
|
5697
5718
|
},
|
|
5698
5719
|
feedContext: {
|
|
5699
5720
|
type: 'string',
|
|
@@ -5713,6 +5734,10 @@ export const schemaDict = {
|
|
|
5713
5734
|
},
|
|
5714
5735
|
},
|
|
5715
5736
|
},
|
|
5737
|
+
skeletonReasonPin: {
|
|
5738
|
+
type: 'object',
|
|
5739
|
+
properties: {},
|
|
5740
|
+
},
|
|
5716
5741
|
threadgateView: {
|
|
5717
5742
|
type: 'object',
|
|
5718
5743
|
properties: {
|
|
@@ -6078,6 +6103,10 @@ export const schemaDict = {
|
|
|
6078
6103
|
],
|
|
6079
6104
|
default: 'posts_with_replies',
|
|
6080
6105
|
},
|
|
6106
|
+
includePins: {
|
|
6107
|
+
type: 'boolean',
|
|
6108
|
+
default: false,
|
|
6109
|
+
},
|
|
6081
6110
|
},
|
|
6082
6111
|
},
|
|
6083
6112
|
output: {
|
|
@@ -7162,7 +7191,7 @@ export const schemaDict = {
|
|
|
7162
7191
|
type: 'record',
|
|
7163
7192
|
key: 'tid',
|
|
7164
7193
|
description:
|
|
7165
|
-
"Record defining interaction gating rules for a thread (aka, reply controls). The record key (rkey) of the threadgate record must match the record key of the thread's root post, and that record must be in the same repository
|
|
7194
|
+
"Record defining interaction gating rules for a thread (aka, reply controls). The record key (rkey) of the threadgate record must match the record key of the thread's root post, and that record must be in the same repository.",
|
|
7166
7195
|
record: {
|
|
7167
7196
|
type: 'object',
|
|
7168
7197
|
required: ['post', 'createdAt'],
|
|
@@ -8236,6 +8265,12 @@ export const schemaDict = {
|
|
|
8236
8265
|
ref: 'lex:app.bsky.actor.defs#profileView',
|
|
8237
8266
|
},
|
|
8238
8267
|
},
|
|
8268
|
+
isFallback: {
|
|
8269
|
+
type: 'boolean',
|
|
8270
|
+
description:
|
|
8271
|
+
'If true, response has fallen-back to generic results, and is not scoped using relativeToDid',
|
|
8272
|
+
default: false,
|
|
8273
|
+
},
|
|
8239
8274
|
},
|
|
8240
8275
|
},
|
|
8241
8276
|
},
|
|
@@ -9192,6 +9227,12 @@ export const schemaDict = {
|
|
|
9192
9227
|
ref: 'lex:app.bsky.unspecced.defs#skeletonSearchActor',
|
|
9193
9228
|
},
|
|
9194
9229
|
},
|
|
9230
|
+
relativeToDid: {
|
|
9231
|
+
type: 'string',
|
|
9232
|
+
format: 'did',
|
|
9233
|
+
description:
|
|
9234
|
+
'DID of the account these suggestions are relative to. If this is returned undefined, suggestions are based on the viewer.',
|
|
9235
|
+
},
|
|
9195
9236
|
},
|
|
9196
9237
|
},
|
|
9197
9238
|
},
|
|
@@ -7,6 +7,7 @@ import { isObj, hasProp } from '../../../../util'
|
|
|
7
7
|
import { CID } from 'multiformats/cid'
|
|
8
8
|
import * as ComAtprotoLabelDefs from '../../../com/atproto/label/defs'
|
|
9
9
|
import * as AppBskyGraphDefs from '../graph/defs'
|
|
10
|
+
import * as ComAtprotoRepoStrongRef from '../../../com/atproto/repo/strongRef'
|
|
10
11
|
|
|
11
12
|
export interface ProfileViewBasic {
|
|
12
13
|
did: string
|
|
@@ -74,6 +75,7 @@ export interface ProfileViewDetailed {
|
|
|
74
75
|
createdAt?: string
|
|
75
76
|
viewer?: ViewerState
|
|
76
77
|
labels?: ComAtprotoLabelDefs.Label[]
|
|
78
|
+
pinnedPost?: ComAtprotoRepoStrongRef.Main
|
|
77
79
|
[k: string]: unknown
|
|
78
80
|
}
|
|
79
81
|
|
|
@@ -55,6 +55,7 @@ export interface ViewerState {
|
|
|
55
55
|
threadMuted?: boolean
|
|
56
56
|
replyDisabled?: boolean
|
|
57
57
|
embeddingDisabled?: boolean
|
|
58
|
+
pinned?: boolean
|
|
58
59
|
[k: string]: unknown
|
|
59
60
|
}
|
|
60
61
|
|
|
@@ -73,7 +74,7 @@ export function validateViewerState(v: unknown): ValidationResult {
|
|
|
73
74
|
export interface FeedViewPost {
|
|
74
75
|
post: PostView
|
|
75
76
|
reply?: ReplyRef
|
|
76
|
-
reason?: ReasonRepost | { $type: string; [k: string]: unknown }
|
|
77
|
+
reason?: ReasonRepost | ReasonPin | { $type: string; [k: string]: unknown }
|
|
77
78
|
/** Context provided by feed generator that may be passed back alongside interactions. */
|
|
78
79
|
feedContext?: string
|
|
79
80
|
[k: string]: unknown
|
|
@@ -134,6 +135,22 @@ export function validateReasonRepost(v: unknown): ValidationResult {
|
|
|
134
135
|
return lexicons.validate('app.bsky.feed.defs#reasonRepost', v)
|
|
135
136
|
}
|
|
136
137
|
|
|
138
|
+
export interface ReasonPin {
|
|
139
|
+
[k: string]: unknown
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function isReasonPin(v: unknown): v is ReasonPin {
|
|
143
|
+
return (
|
|
144
|
+
isObj(v) &&
|
|
145
|
+
hasProp(v, '$type') &&
|
|
146
|
+
v.$type === 'app.bsky.feed.defs#reasonPin'
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function validateReasonPin(v: unknown): ValidationResult {
|
|
151
|
+
return lexicons.validate('app.bsky.feed.defs#reasonPin', v)
|
|
152
|
+
}
|
|
153
|
+
|
|
137
154
|
export interface ThreadViewPost {
|
|
138
155
|
post: PostView
|
|
139
156
|
parent?:
|
|
@@ -265,7 +282,10 @@ export function validateGeneratorViewerState(v: unknown): ValidationResult {
|
|
|
265
282
|
|
|
266
283
|
export interface SkeletonFeedPost {
|
|
267
284
|
post: string
|
|
268
|
-
reason?:
|
|
285
|
+
reason?:
|
|
286
|
+
| SkeletonReasonRepost
|
|
287
|
+
| SkeletonReasonPin
|
|
288
|
+
| { $type: string; [k: string]: unknown }
|
|
269
289
|
/** Context that will be passed through to client and may be passed to feed generator back alongside interactions. */
|
|
270
290
|
feedContext?: string
|
|
271
291
|
[k: string]: unknown
|
|
@@ -300,6 +320,22 @@ export function validateSkeletonReasonRepost(v: unknown): ValidationResult {
|
|
|
300
320
|
return lexicons.validate('app.bsky.feed.defs#skeletonReasonRepost', v)
|
|
301
321
|
}
|
|
302
322
|
|
|
323
|
+
export interface SkeletonReasonPin {
|
|
324
|
+
[k: string]: unknown
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export function isSkeletonReasonPin(v: unknown): v is SkeletonReasonPin {
|
|
328
|
+
return (
|
|
329
|
+
isObj(v) &&
|
|
330
|
+
hasProp(v, '$type') &&
|
|
331
|
+
v.$type === 'app.bsky.feed.defs#skeletonReasonPin'
|
|
332
|
+
)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export function validateSkeletonReasonPin(v: unknown): ValidationResult {
|
|
336
|
+
return lexicons.validate('app.bsky.feed.defs#skeletonReasonPin', v)
|
|
337
|
+
}
|
|
338
|
+
|
|
303
339
|
export interface ThreadgateView {
|
|
304
340
|
uri?: string
|
|
305
341
|
cid?: string
|
|
@@ -17,6 +17,8 @@ export type InputSchema = undefined
|
|
|
17
17
|
|
|
18
18
|
export interface OutputSchema {
|
|
19
19
|
suggestions: AppBskyActorDefs.ProfileView[]
|
|
20
|
+
/** If true, response has fallen-back to generic results, and is not scoped using relativeToDid */
|
|
21
|
+
isFallback?: boolean
|
|
20
22
|
[k: string]: unknown
|
|
21
23
|
}
|
|
22
24
|
|
|
@@ -23,6 +23,8 @@ export type InputSchema = undefined
|
|
|
23
23
|
export interface OutputSchema {
|
|
24
24
|
cursor?: string
|
|
25
25
|
actors: AppBskyUnspeccedDefs.SkeletonSearchActor[]
|
|
26
|
+
/** DID of the account these suggestions are relative to. If this is returned undefined, suggestions are based on the viewer. */
|
|
27
|
+
relativeToDid?: string
|
|
26
28
|
[k: string]: unknown
|
|
27
29
|
}
|
|
28
30
|
|
package/src/views/index.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
NotFoundPost,
|
|
17
17
|
PostView,
|
|
18
18
|
ReasonRepost,
|
|
19
|
+
ReasonPin,
|
|
19
20
|
ReplyRef,
|
|
20
21
|
ThreadViewPost,
|
|
21
22
|
ThreadgateView,
|
|
@@ -169,6 +170,7 @@ export class Views {
|
|
|
169
170
|
joinedViaStarterPack: actor.profile?.joinedViaStarterPack
|
|
170
171
|
? this.starterPackBasic(actor.profile.joinedViaStarterPack.uri, state)
|
|
171
172
|
: undefined,
|
|
173
|
+
pinnedPost: actor.profile?.pinnedPost,
|
|
172
174
|
}
|
|
173
175
|
}
|
|
174
176
|
|
|
@@ -606,6 +608,7 @@ export class Views {
|
|
|
606
608
|
threadMuted: viewer.threadMuted,
|
|
607
609
|
replyDisabled: this.userReplyDisabled(uri, state),
|
|
608
610
|
embeddingDisabled: this.userPostEmbeddingDisabled(uri, state),
|
|
611
|
+
pinned: this.viewerPinned(uri, state, authorDid),
|
|
609
612
|
}
|
|
610
613
|
: undefined,
|
|
611
614
|
labels,
|
|
@@ -620,8 +623,10 @@ export class Views {
|
|
|
620
623
|
state: HydrationState,
|
|
621
624
|
): FeedViewPost | undefined {
|
|
622
625
|
const postInfo = state.posts?.get(item.post.uri)
|
|
623
|
-
let reason: ReasonRepost | undefined
|
|
624
|
-
if (item.
|
|
626
|
+
let reason: ReasonRepost | ReasonPin | undefined
|
|
627
|
+
if (item.authorPinned) {
|
|
628
|
+
reason = this.reasonPin()
|
|
629
|
+
} else if (item.repost) {
|
|
625
630
|
const repost = state.reposts?.get(item.repost.uri)
|
|
626
631
|
if (!repost) return
|
|
627
632
|
if (repost.record.subject.uri !== item.post.uri) return
|
|
@@ -723,6 +728,12 @@ export class Views {
|
|
|
723
728
|
}
|
|
724
729
|
}
|
|
725
730
|
|
|
731
|
+
reasonPin() {
|
|
732
|
+
return {
|
|
733
|
+
$type: 'app.bsky.feed.defs#reasonPin',
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
726
737
|
// Threads
|
|
727
738
|
// ------------
|
|
728
739
|
|
|
@@ -1128,6 +1139,15 @@ export class Views {
|
|
|
1128
1139
|
return true
|
|
1129
1140
|
}
|
|
1130
1141
|
|
|
1142
|
+
viewerPinned(uri: string, state: HydrationState, authorDid: string) {
|
|
1143
|
+
if (!state.ctx?.viewer || state.ctx.viewer !== authorDid) return
|
|
1144
|
+
const actor = state.actors?.get(authorDid)
|
|
1145
|
+
if (!actor) return
|
|
1146
|
+
const pinnedPost = actor.profile?.pinnedPost
|
|
1147
|
+
if (!pinnedPost) return undefined
|
|
1148
|
+
return pinnedPost.uri === uri
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1131
1151
|
notification(
|
|
1132
1152
|
notif: Notification,
|
|
1133
1153
|
lastSeenAt: string | undefined,
|