@atproto/bsky 0.0.117 → 0.0.118
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 +9 -0
- package/dist/api/app/bsky/feed/getQuotes.js +1 -1
- package/dist/api/app/bsky/feed/getQuotes.js.map +1 -1
- package/dist/api/app/bsky/notification/listNotifications.js +1 -1
- package/dist/api/app/bsky/notification/listNotifications.js.map +1 -1
- package/dist/data-plane/server/db/migrations/20250207T174822012Z-add-label-exp.d.ts +4 -0
- package/dist/data-plane/server/db/migrations/20250207T174822012Z-add-label-exp.d.ts.map +1 -0
- package/dist/data-plane/server/db/migrations/20250207T174822012Z-add-label-exp.js +11 -0
- package/dist/data-plane/server/db/migrations/20250207T174822012Z-add-label-exp.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/label.d.ts +1 -0
- package/dist/data-plane/server/db/tables/label.d.ts.map +1 -1
- package/dist/data-plane/server/routes/labels.d.ts.map +1 -1
- package/dist/data-plane/server/routes/labels.js +3 -0
- package/dist/data-plane/server/routes/labels.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 +12 -5
- package/dist/views/index.js.map +1 -1
- package/package.json +5 -5
- package/src/api/app/bsky/feed/getQuotes.ts +1 -1
- package/src/api/app/bsky/notification/listNotifications.ts +1 -1
- package/src/data-plane/server/db/migrations/20250207T174822012Z-add-label-exp.ts +9 -0
- package/src/data-plane/server/db/migrations/index.ts +1 -0
- package/src/data-plane/server/db/tables/label.ts +1 -0
- package/src/data-plane/server/routes/labels.ts +6 -1
- package/src/views/index.ts +17 -7
- package/tests/label-hydration.test.ts +56 -8
- package/tests/query-labels.test.ts +1 -0
- package/tests/views/labels-needs-review.test.ts +252 -106
- package/tests/views/labels-takedown.test.ts +1 -0
- package/tests/views/timeline.test.ts +1 -0
- package/tsconfig.build.tsbuildinfo +1 -1
package/src/views/index.ts
CHANGED
|
@@ -176,13 +176,23 @@ export class Views {
|
|
|
176
176
|
return uri
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
-
viewerSeesNeedsReview(
|
|
179
|
+
viewerSeesNeedsReview(
|
|
180
|
+
{ did, uri }: { did?: string; uri?: string },
|
|
181
|
+
state: HydrationState,
|
|
182
|
+
): boolean {
|
|
180
183
|
const { labels, profileViewers, ctx } = state
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
184
|
+
did = did || (uri && uriToDid(uri))
|
|
185
|
+
if (!did) {
|
|
186
|
+
return true
|
|
187
|
+
}
|
|
188
|
+
if (
|
|
189
|
+
labels?.get(did)?.needsReview ||
|
|
190
|
+
(uri && labels?.get(uri)?.needsReview)
|
|
191
|
+
) {
|
|
192
|
+
// content marked as needs review
|
|
193
|
+
return ctx?.viewer === did || !!profileViewers?.get(did)?.following
|
|
194
|
+
}
|
|
195
|
+
return true
|
|
186
196
|
}
|
|
187
197
|
|
|
188
198
|
replyIsHiddenByThreadgate(
|
|
@@ -972,7 +982,7 @@ export class Views {
|
|
|
972
982
|
if (this.viewerBlockExists(post.author.did, state)) {
|
|
973
983
|
return this.blockedPost(uri, post.author.did, state)
|
|
974
984
|
}
|
|
975
|
-
if (!this.viewerSeesNeedsReview(post.author.did, state)) {
|
|
985
|
+
if (!this.viewerSeesNeedsReview({ uri, did: post.author.did }, state)) {
|
|
976
986
|
return undefined
|
|
977
987
|
}
|
|
978
988
|
return {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import assert from 'node:assert'
|
|
1
2
|
import { AtpAgent } from '@atproto/api'
|
|
3
|
+
import { MINUTE } from '@atproto/common'
|
|
2
4
|
import { SeedClient, TestNetwork, basicSeed } from '@atproto/dev-env'
|
|
3
5
|
|
|
4
6
|
describe('label hydration', () => {
|
|
@@ -10,6 +12,7 @@ describe('label hydration', () => {
|
|
|
10
12
|
let bob: string
|
|
11
13
|
let carol: string
|
|
12
14
|
let labelerDid: string
|
|
15
|
+
let labeler2Did: string
|
|
13
16
|
|
|
14
17
|
beforeAll(async () => {
|
|
15
18
|
network = await TestNetwork.create({
|
|
@@ -22,6 +25,7 @@ describe('label hydration', () => {
|
|
|
22
25
|
bob = sc.dids.bob
|
|
23
26
|
carol = sc.dids.carol
|
|
24
27
|
labelerDid = network.bsky.ctx.cfg.labelsFromIssuerDids[0]
|
|
28
|
+
labeler2Did = network.bsky.ctx.cfg.labelsFromIssuerDids[1]
|
|
25
29
|
await createLabel({ src: alice, uri: carol, cid: '', val: 'spam' })
|
|
26
30
|
await createLabel({ src: bob, uri: carol, cid: '', val: 'impersonation' })
|
|
27
31
|
await createLabel({
|
|
@@ -30,6 +34,20 @@ describe('label hydration', () => {
|
|
|
30
34
|
cid: '',
|
|
31
35
|
val: 'misleading',
|
|
32
36
|
})
|
|
37
|
+
await createLabel({
|
|
38
|
+
src: labeler2Did,
|
|
39
|
+
uri: carol,
|
|
40
|
+
cid: '',
|
|
41
|
+
val: 'expired',
|
|
42
|
+
exp: new Date(Date.now() - MINUTE).toISOString(),
|
|
43
|
+
})
|
|
44
|
+
await createLabel({
|
|
45
|
+
src: labeler2Did,
|
|
46
|
+
uri: carol,
|
|
47
|
+
cid: '',
|
|
48
|
+
val: 'not-expired',
|
|
49
|
+
exp: new Date(Date.now() + MINUTE).toISOString(),
|
|
50
|
+
})
|
|
33
51
|
await network.processAll()
|
|
34
52
|
})
|
|
35
53
|
|
|
@@ -39,17 +57,33 @@ describe('label hydration', () => {
|
|
|
39
57
|
|
|
40
58
|
it('hydrates labels based on a supplied labeler header', async () => {
|
|
41
59
|
AtpAgent.configure({ appLabelers: [alice] })
|
|
42
|
-
pdsAgent.configureLabelers([])
|
|
60
|
+
pdsAgent.configureLabelers([labeler2Did])
|
|
43
61
|
const res = await pdsAgent.api.app.bsky.actor.getProfile(
|
|
44
62
|
{ actor: carol },
|
|
45
63
|
{
|
|
46
64
|
headers: sc.getHeaders(bob),
|
|
47
65
|
},
|
|
48
66
|
)
|
|
49
|
-
expect(res.data.labels?.length).toBe(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
67
|
+
expect(res.data.labels?.length).toBe(2)
|
|
68
|
+
assert(res.data.labels)
|
|
69
|
+
|
|
70
|
+
const sortedLabels = res.data.labels.sort((a, b) =>
|
|
71
|
+
a.src.localeCompare(b.src),
|
|
72
|
+
)
|
|
73
|
+
const sortedExpected = [
|
|
74
|
+
{ src: labeler2Did, val: 'not-expired' },
|
|
75
|
+
{ src: alice, val: 'spam' },
|
|
76
|
+
].sort((a, b) => a.src.localeCompare(b.src))
|
|
77
|
+
|
|
78
|
+
expect(sortedLabels[0].src).toBe(sortedExpected[0].src)
|
|
79
|
+
expect(sortedLabels[0].val).toBe(sortedExpected[0].val)
|
|
80
|
+
|
|
81
|
+
expect(sortedLabels[1].src).toBe(sortedExpected[1].src)
|
|
82
|
+
expect(sortedLabels[1].val).toBe(sortedExpected[1].val)
|
|
83
|
+
|
|
84
|
+
expect(res.headers['atproto-content-labelers']).toEqual(
|
|
85
|
+
`${alice};redact,${labeler2Did}`,
|
|
86
|
+
)
|
|
53
87
|
})
|
|
54
88
|
|
|
55
89
|
it('hydrates labels based on multiple a supplied labelers', async () => {
|
|
@@ -88,9 +122,21 @@ describe('label hydration', () => {
|
|
|
88
122
|
{ headers: sc.getHeaders(bob) },
|
|
89
123
|
)
|
|
90
124
|
const data = await res.json()
|
|
91
|
-
|
|
92
|
-
expect(data.labels?.
|
|
93
|
-
|
|
125
|
+
|
|
126
|
+
expect(data.labels?.length).toBe(2)
|
|
127
|
+
assert(data.labels)
|
|
128
|
+
|
|
129
|
+
const sortedLabels = data.labels.sort((a, b) => a.src.localeCompare(b.src))
|
|
130
|
+
const sortedExpected = [
|
|
131
|
+
{ src: labeler2Did, val: 'not-expired' },
|
|
132
|
+
{ src: labelerDid, val: 'misleading' },
|
|
133
|
+
].sort((a, b) => a.src.localeCompare(b.src))
|
|
134
|
+
|
|
135
|
+
expect(sortedLabels[0].src).toBe(sortedExpected[0].src)
|
|
136
|
+
expect(sortedLabels[0].val).toBe(sortedExpected[0].val)
|
|
137
|
+
|
|
138
|
+
expect(sortedLabels[1].src).toBe(sortedExpected[1].src)
|
|
139
|
+
expect(sortedLabels[1].val).toBe(sortedExpected[1].val)
|
|
94
140
|
|
|
95
141
|
expect(res.headers.get('atproto-content-labelers')).toEqual(
|
|
96
142
|
network.bsky.ctx.cfg.labelsFromIssuerDids
|
|
@@ -181,6 +227,7 @@ describe('label hydration', () => {
|
|
|
181
227
|
uri: string
|
|
182
228
|
cid: string
|
|
183
229
|
val: string
|
|
230
|
+
exp?: string
|
|
184
231
|
}) => {
|
|
185
232
|
await network.bsky.db.db
|
|
186
233
|
.insertInto('label')
|
|
@@ -189,6 +236,7 @@ describe('label hydration', () => {
|
|
|
189
236
|
cid: opts.cid,
|
|
190
237
|
val: opts.val,
|
|
191
238
|
cts: new Date().toISOString(),
|
|
239
|
+
exp: opts.exp ?? null,
|
|
192
240
|
neg: false,
|
|
193
241
|
src: opts.src ?? labelerDid,
|
|
194
242
|
})
|
|
@@ -43,126 +43,272 @@ describe('bsky needs-review labels', () => {
|
|
|
43
43
|
await network.processAll()
|
|
44
44
|
|
|
45
45
|
AtpAgent.configure({ appLabelers: [network.ozone.ctx.cfg.service.did] })
|
|
46
|
-
await network.bsky.db.db
|
|
47
|
-
.insertInto('label')
|
|
48
|
-
.values({
|
|
49
|
-
src: network.ozone.ctx.cfg.service.did,
|
|
50
|
-
uri: sc.dids.geoff,
|
|
51
|
-
cid: '',
|
|
52
|
-
val: 'needs-review',
|
|
53
|
-
neg: false,
|
|
54
|
-
cts: new Date().toISOString(),
|
|
55
|
-
})
|
|
56
|
-
.execute()
|
|
57
46
|
})
|
|
58
47
|
|
|
59
48
|
afterAll(async () => {
|
|
60
49
|
await network.close()
|
|
61
50
|
})
|
|
62
51
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
52
|
+
describe('account-level', () => {
|
|
53
|
+
beforeAll(async () => {
|
|
54
|
+
await network.bsky.db.db
|
|
55
|
+
.insertInto('label')
|
|
56
|
+
.values({
|
|
57
|
+
src: network.ozone.ctx.cfg.service.did,
|
|
58
|
+
uri: sc.dids.geoff,
|
|
59
|
+
cid: '',
|
|
60
|
+
val: 'needs-review',
|
|
61
|
+
exp: null,
|
|
62
|
+
neg: false,
|
|
63
|
+
cts: new Date().toISOString(),
|
|
64
|
+
})
|
|
65
|
+
.execute()
|
|
68
66
|
})
|
|
69
|
-
assert(isThreadViewPost(thread))
|
|
70
|
-
expect(
|
|
71
|
-
thread.replies?.some((reply) => {
|
|
72
|
-
return (
|
|
73
|
-
isThreadViewPost(reply) && reply.post.author.did === sc.dids.geoff
|
|
74
|
-
)
|
|
75
|
-
}),
|
|
76
|
-
).toBe(false)
|
|
77
|
-
})
|
|
78
67
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
68
|
+
afterAll(async () => {
|
|
69
|
+
await network.bsky.db.db
|
|
70
|
+
.deleteFrom('label')
|
|
71
|
+
.where('src', '=', network.ozone.ctx.cfg.service.did)
|
|
72
|
+
.execute()
|
|
84
73
|
})
|
|
85
|
-
expect(
|
|
86
|
-
posts.some((post) => {
|
|
87
|
-
return post.author.did === sc.dids.geoff
|
|
88
|
-
}),
|
|
89
|
-
).toBe(false)
|
|
90
|
-
})
|
|
91
74
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}),
|
|
108
|
-
).toBe(false)
|
|
109
|
-
expect(
|
|
110
|
-
notifications.some((notif) => {
|
|
111
|
-
return notif.reason === 'quote' && notif.author.did === sc.dids.geoff
|
|
112
|
-
}),
|
|
113
|
-
).toBe(false)
|
|
114
|
-
expect(
|
|
115
|
-
notifications.some((notif) => {
|
|
116
|
-
return notif.reason === 'mention' && notif.author.did === sc.dids.geoff
|
|
117
|
-
}),
|
|
118
|
-
).toBe(false)
|
|
119
|
-
})
|
|
75
|
+
it('applies to thread replies.', async () => {
|
|
76
|
+
const {
|
|
77
|
+
data: { thread },
|
|
78
|
+
} = await agent.app.bsky.feed.getPostThread({
|
|
79
|
+
uri: sc.posts[sc.dids.alice][0].ref.uriStr,
|
|
80
|
+
})
|
|
81
|
+
assert(isThreadViewPost(thread))
|
|
82
|
+
expect(
|
|
83
|
+
thread.replies?.some((reply) => {
|
|
84
|
+
return (
|
|
85
|
+
isThreadViewPost(reply) && reply.post.author.did === sc.dids.geoff
|
|
86
|
+
)
|
|
87
|
+
}),
|
|
88
|
+
).toBe(false)
|
|
89
|
+
})
|
|
120
90
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
{
|
|
91
|
+
it('applies to quote lists.', async () => {
|
|
92
|
+
const {
|
|
93
|
+
data: { posts },
|
|
94
|
+
} = await agent.app.bsky.feed.getQuotes({
|
|
126
95
|
uri: sc.posts[sc.dids.alice][0].ref.uriStr,
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
sc.dids.geoff
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
96
|
+
})
|
|
97
|
+
expect(
|
|
98
|
+
posts.some((post) => {
|
|
99
|
+
return post.author.did === sc.dids.geoff
|
|
100
|
+
}),
|
|
101
|
+
).toBe(false)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('applies to reply, quote, and mention notifications.', async () => {
|
|
105
|
+
const {
|
|
106
|
+
data: { notifications },
|
|
107
|
+
} = await agent.app.bsky.notification.listNotifications(
|
|
108
|
+
{},
|
|
109
|
+
{
|
|
110
|
+
headers: await network.serviceHeaders(
|
|
111
|
+
sc.dids.alice,
|
|
112
|
+
ids.AppBskyNotificationListNotifications,
|
|
113
|
+
),
|
|
114
|
+
},
|
|
115
|
+
)
|
|
116
|
+
expect(
|
|
117
|
+
notifications.some((notif) => {
|
|
118
|
+
return notif.reason === 'reply' && notif.author.did === sc.dids.geoff
|
|
119
|
+
}),
|
|
120
|
+
).toBe(false)
|
|
121
|
+
expect(
|
|
122
|
+
notifications.some((notif) => {
|
|
123
|
+
return notif.reason === 'quote' && notif.author.did === sc.dids.geoff
|
|
124
|
+
}),
|
|
125
|
+
).toBe(false)
|
|
126
|
+
expect(
|
|
127
|
+
notifications.some((notif) => {
|
|
128
|
+
return (
|
|
129
|
+
notif.reason === 'mention' && notif.author.did === sc.dids.geoff
|
|
130
|
+
)
|
|
131
|
+
}),
|
|
132
|
+
).toBe(false)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('does not apply to self.', async () => {
|
|
136
|
+
const {
|
|
137
|
+
data: { thread },
|
|
138
|
+
} = await agent.app.bsky.feed.getPostThread(
|
|
139
|
+
{
|
|
140
|
+
uri: sc.posts[sc.dids.alice][0].ref.uriStr,
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
headers: await network.serviceHeaders(
|
|
144
|
+
sc.dids.geoff,
|
|
145
|
+
ids.AppBskyFeedGetPostThread,
|
|
146
|
+
),
|
|
147
|
+
},
|
|
148
|
+
)
|
|
149
|
+
assert(isThreadViewPost(thread))
|
|
150
|
+
expect(
|
|
151
|
+
thread.replies?.some((reply) => {
|
|
152
|
+
return (
|
|
153
|
+
isThreadViewPost(reply) && reply.post.author.did === sc.dids.geoff
|
|
154
|
+
)
|
|
155
|
+
}),
|
|
156
|
+
).toBe(true)
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('does not apply to followers.', async () => {
|
|
160
|
+
const {
|
|
161
|
+
data: { thread },
|
|
162
|
+
} = await agent.app.bsky.feed.getPostThread(
|
|
163
|
+
{
|
|
164
|
+
uri: sc.posts[sc.dids.alice][0].ref.uriStr,
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
headers: await network.serviceHeaders(
|
|
168
|
+
sc.dids.bob, // follows geoff
|
|
169
|
+
ids.AppBskyFeedGetPostThread,
|
|
170
|
+
),
|
|
171
|
+
},
|
|
172
|
+
)
|
|
173
|
+
assert(isThreadViewPost(thread))
|
|
174
|
+
expect(
|
|
175
|
+
thread.replies?.some((reply) => {
|
|
176
|
+
return (
|
|
177
|
+
isThreadViewPost(reply) && reply.post.author.did === sc.dids.geoff
|
|
178
|
+
)
|
|
179
|
+
}),
|
|
180
|
+
).toBe(true)
|
|
181
|
+
})
|
|
143
182
|
})
|
|
144
183
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
isThreadViewPost(reply) && reply.post.author.did === sc.dids.geoff
|
|
184
|
+
describe('record-level', () => {
|
|
185
|
+
beforeAll(async () => {
|
|
186
|
+
const geoffPostUris = [
|
|
187
|
+
...sc.posts[sc.dids.geoff],
|
|
188
|
+
...sc.replies[sc.dids.geoff],
|
|
189
|
+
].map((post) => post.ref.uriStr)
|
|
190
|
+
await network.bsky.db.db
|
|
191
|
+
.insertInto('label')
|
|
192
|
+
.values(
|
|
193
|
+
geoffPostUris.map((uri) => ({
|
|
194
|
+
src: network.ozone.ctx.cfg.service.did,
|
|
195
|
+
uri,
|
|
196
|
+
cid: '',
|
|
197
|
+
val: 'needs-review',
|
|
198
|
+
exp: null,
|
|
199
|
+
neg: false,
|
|
200
|
+
cts: new Date().toISOString(),
|
|
201
|
+
})),
|
|
164
202
|
)
|
|
165
|
-
|
|
166
|
-
)
|
|
203
|
+
.execute()
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
it('applies to thread replies.', async () => {
|
|
207
|
+
const {
|
|
208
|
+
data: { thread },
|
|
209
|
+
} = await agent.app.bsky.feed.getPostThread({
|
|
210
|
+
uri: sc.posts[sc.dids.alice][0].ref.uriStr,
|
|
211
|
+
})
|
|
212
|
+
assert(isThreadViewPost(thread))
|
|
213
|
+
expect(
|
|
214
|
+
thread.replies?.some((reply) => {
|
|
215
|
+
return (
|
|
216
|
+
isThreadViewPost(reply) && reply.post.author.did === sc.dids.geoff
|
|
217
|
+
)
|
|
218
|
+
}),
|
|
219
|
+
).toBe(false)
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
it('applies to quote lists.', async () => {
|
|
223
|
+
const {
|
|
224
|
+
data: { posts },
|
|
225
|
+
} = await agent.app.bsky.feed.getQuotes({
|
|
226
|
+
uri: sc.posts[sc.dids.alice][0].ref.uriStr,
|
|
227
|
+
})
|
|
228
|
+
expect(
|
|
229
|
+
posts.some((post) => {
|
|
230
|
+
return post.author.did === sc.dids.geoff
|
|
231
|
+
}),
|
|
232
|
+
).toBe(false)
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
it('applies to reply, quote, and mention notifications.', async () => {
|
|
236
|
+
const {
|
|
237
|
+
data: { notifications },
|
|
238
|
+
} = await agent.app.bsky.notification.listNotifications(
|
|
239
|
+
{},
|
|
240
|
+
{
|
|
241
|
+
headers: await network.serviceHeaders(
|
|
242
|
+
sc.dids.alice,
|
|
243
|
+
ids.AppBskyNotificationListNotifications,
|
|
244
|
+
),
|
|
245
|
+
},
|
|
246
|
+
)
|
|
247
|
+
expect(
|
|
248
|
+
notifications.some((notif) => {
|
|
249
|
+
return notif.reason === 'reply' && notif.author.did === sc.dids.geoff
|
|
250
|
+
}),
|
|
251
|
+
).toBe(false)
|
|
252
|
+
expect(
|
|
253
|
+
notifications.some((notif) => {
|
|
254
|
+
return notif.reason === 'quote' && notif.author.did === sc.dids.geoff
|
|
255
|
+
}),
|
|
256
|
+
).toBe(false)
|
|
257
|
+
expect(
|
|
258
|
+
notifications.some((notif) => {
|
|
259
|
+
return (
|
|
260
|
+
notif.reason === 'mention' && notif.author.did === sc.dids.geoff
|
|
261
|
+
)
|
|
262
|
+
}),
|
|
263
|
+
).toBe(false)
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
it('does not apply to self.', async () => {
|
|
267
|
+
const {
|
|
268
|
+
data: { thread },
|
|
269
|
+
} = await agent.app.bsky.feed.getPostThread(
|
|
270
|
+
{
|
|
271
|
+
uri: sc.posts[sc.dids.alice][0].ref.uriStr,
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
headers: await network.serviceHeaders(
|
|
275
|
+
sc.dids.geoff,
|
|
276
|
+
ids.AppBskyFeedGetPostThread,
|
|
277
|
+
),
|
|
278
|
+
},
|
|
279
|
+
)
|
|
280
|
+
assert(isThreadViewPost(thread))
|
|
281
|
+
expect(
|
|
282
|
+
thread.replies?.some((reply) => {
|
|
283
|
+
return (
|
|
284
|
+
isThreadViewPost(reply) && reply.post.author.did === sc.dids.geoff
|
|
285
|
+
)
|
|
286
|
+
}),
|
|
287
|
+
).toBe(true)
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
it('does not apply to followers.', async () => {
|
|
291
|
+
const {
|
|
292
|
+
data: { thread },
|
|
293
|
+
} = await agent.app.bsky.feed.getPostThread(
|
|
294
|
+
{
|
|
295
|
+
uri: sc.posts[sc.dids.alice][0].ref.uriStr,
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
headers: await network.serviceHeaders(
|
|
299
|
+
sc.dids.bob, // follows geoff
|
|
300
|
+
ids.AppBskyFeedGetPostThread,
|
|
301
|
+
),
|
|
302
|
+
},
|
|
303
|
+
)
|
|
304
|
+
assert(isThreadViewPost(thread))
|
|
305
|
+
expect(
|
|
306
|
+
thread.replies?.some((reply) => {
|
|
307
|
+
return (
|
|
308
|
+
isThreadViewPost(reply) && reply.post.author.did === sc.dids.geoff
|
|
309
|
+
)
|
|
310
|
+
}),
|
|
311
|
+
).toBe(true)
|
|
312
|
+
})
|
|
167
313
|
})
|
|
168
314
|
})
|