@atproto/bsky 0.0.114 → 0.0.116

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/src/views/util.ts CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  } from '../lexicon/types/app/bsky/feed/postgate'
8
8
  import {
9
9
  Record as GateRecord,
10
+ isFollowerRule,
10
11
  isFollowingRule,
11
12
  isListRule,
12
13
  isMentionRule,
@@ -28,6 +29,7 @@ export const parseThreadGate = (
28
29
  }
29
30
 
30
31
  const allowMentions = gate.allow.some(isMentionRule)
32
+ const allowFollower = gate.allow.some(isFollowerRule)
31
33
  const allowFollowing = gate.allow.some(isFollowingRule)
32
34
  const allowListUris = gate.allow?.filter(isListRule).map((item) => item.list)
33
35
 
@@ -39,15 +41,22 @@ export const parseThreadGate = (
39
41
  )
40
42
  })
41
43
  if (isMentioned) {
42
- return { canReply: true, allowMentions, allowFollowing, allowListUris }
44
+ return {
45
+ canReply: true,
46
+ allowMentions,
47
+ allowFollower,
48
+ allowFollowing,
49
+ allowListUris,
50
+ }
43
51
  }
44
52
  }
45
- return { allowMentions, allowFollowing, allowListUris }
53
+ return { allowMentions, allowFollower, allowFollowing, allowListUris }
46
54
  }
47
55
 
48
56
  type ParsedThreadGate = {
49
57
  canReply?: boolean
50
58
  allowMentions?: boolean
59
+ allowFollower?: boolean
51
60
  allowFollowing?: boolean
52
61
  allowListUris?: string[]
53
62
  }
@@ -16,6 +16,24 @@ Object {
16
16
  }
17
17
  `;
18
18
 
19
+ exports[`views with thread gating applies gate for follower rule. 1`] = `
20
+ Object {
21
+ "cid": "cids(0)",
22
+ "lists": Array [],
23
+ "record": Object {
24
+ "$type": "app.bsky.feed.threadgate",
25
+ "allow": Array [
26
+ Object {
27
+ "$type": "app.bsky.feed.threadgate#followerRule",
28
+ },
29
+ ],
30
+ "createdAt": "1970-01-01T00:00:00.000Z",
31
+ "post": "record(1)",
32
+ },
33
+ "uri": "record(0)",
34
+ }
35
+ `;
36
+
19
37
  exports[`views with thread gating applies gate for following rule. 1`] = `
20
38
  Object {
21
39
  "cid": "cids(0)",
@@ -123,6 +141,9 @@ Object {
123
141
  Object {
124
142
  "$type": "app.bsky.feed.threadgate#mentionRule",
125
143
  },
144
+ Object {
145
+ "$type": "app.bsky.feed.threadgate#followerRule",
146
+ },
126
147
  Object {
127
148
  "$type": "app.bsky.feed.threadgate#followingRule",
128
149
  },
@@ -22,6 +22,11 @@ describe('views with thread gating', () => {
22
22
  pdsAgent = network.pds.getClient()
23
23
  sc = network.getSeedClient()
24
24
  await basicSeed(sc)
25
+ await sc.createAccount('eve', {
26
+ handle: 'eve.test',
27
+ email: 'eve@eve.com',
28
+ password: 'hunter2',
29
+ })
25
30
  await network.processAll()
26
31
  })
27
32
 
@@ -242,8 +247,72 @@ describe('views with thread gating', () => {
242
247
  expect(reply.post.uri).toEqual(aliceReply.ref.uriStr)
243
248
  })
244
249
 
250
+ it('applies gate for follower rule.', async () => {
251
+ const post = await sc.post(sc.dids.carol, 'follower rule')
252
+ await pdsAgent.api.app.bsky.feed.threadgate.create(
253
+ { repo: sc.dids.carol, rkey: post.ref.uri.rkey },
254
+ {
255
+ post: post.ref.uriStr,
256
+ createdAt: iso(),
257
+ allow: [{ $type: 'app.bsky.feed.threadgate#followerRule' }],
258
+ },
259
+ sc.getHeaders(sc.dids.carol),
260
+ )
261
+ await network.processAll()
262
+
263
+ // dan does not follow carol, can't reply
264
+ await sc.reply(
265
+ sc.dids.dan,
266
+ post.ref,
267
+ post.ref,
268
+ 'follower rule reply disallow',
269
+ )
270
+
271
+ // alice follows carol, can reply
272
+ const aliceReply = await sc.reply(
273
+ sc.dids.alice,
274
+ post.ref,
275
+ post.ref,
276
+ 'follower rule reply allow',
277
+ )
278
+ await network.processAll()
279
+ const {
280
+ data: { thread: danThread },
281
+ } = await agent.api.app.bsky.feed.getPostThread(
282
+ { uri: post.ref.uriStr },
283
+ {
284
+ headers: await network.serviceHeaders(
285
+ sc.dids.dan,
286
+ ids.AppBskyFeedGetPostThread,
287
+ ),
288
+ },
289
+ )
290
+ assert(isThreadViewPost(danThread))
291
+ expect(danThread.post.viewer?.replyDisabled).toBe(true)
292
+ await checkReplyDisabled(post.ref.uriStr, sc.dids.dan, true)
293
+ const {
294
+ data: { thread: aliceThread },
295
+ } = await agent.api.app.bsky.feed.getPostThread(
296
+ { uri: post.ref.uriStr },
297
+ {
298
+ headers: await network.serviceHeaders(
299
+ sc.dids.alice,
300
+ ids.AppBskyFeedGetPostThread,
301
+ ),
302
+ },
303
+ )
304
+ assert(isThreadViewPost(aliceThread))
305
+ expect(forSnapshot(aliceThread.post.threadgate)).toMatchSnapshot()
306
+ expect(aliceThread.post.viewer?.replyDisabled).toBe(false)
307
+ await checkReplyDisabled(post.ref.uriStr, sc.dids.alice, false)
308
+ const [reply, ...otherReplies] = aliceThread.replies ?? []
309
+ assert(isThreadViewPost(reply))
310
+ expect(otherReplies.length).toEqual(0)
311
+ expect(reply.post.uri).toEqual(aliceReply.ref.uriStr)
312
+ })
313
+
245
314
  it('applies gate for list rule.', async () => {
246
- const post = await sc.post(sc.dids.carol, 'following rule')
315
+ const post = await sc.post(sc.dids.carol, 'list rule')
247
316
  // setup lists to allow alice and dan
248
317
  const listA = await pdsAgent.api.app.bsky.graph.list.create(
249
318
  { repo: sc.dids.carol },
@@ -419,14 +488,21 @@ describe('views with thread gating', () => {
419
488
  createdAt: iso(),
420
489
  allow: [
421
490
  { $type: 'app.bsky.feed.threadgate#mentionRule' },
491
+ { $type: 'app.bsky.feed.threadgate#followerRule' },
422
492
  { $type: 'app.bsky.feed.threadgate#followingRule' },
423
493
  ],
424
494
  },
425
495
  sc.getHeaders(sc.dids.carol),
426
496
  )
427
497
  await network.processAll()
428
- // carol only follows alice, and the post mentions dan.
429
- await sc.reply(sc.dids.bob, post.ref, post.ref, 'multi rule reply disallow')
498
+
499
+ await sc.reply(sc.dids.eve, post.ref, post.ref, 'multi rule reply disallow')
500
+ const bobReply = await sc.reply(
501
+ sc.dids.bob,
502
+ post.ref,
503
+ post.ref,
504
+ 'multi rule reply allow (follower)',
505
+ )
430
506
  const aliceReply = await sc.reply(
431
507
  sc.dids.alice,
432
508
  post.ref,
@@ -440,6 +516,23 @@ describe('views with thread gating', () => {
440
516
  'multi rule reply allow (mention)',
441
517
  )
442
518
  await network.processAll()
519
+
520
+ const {
521
+ data: { thread: eveThread },
522
+ } = await agent.api.app.bsky.feed.getPostThread(
523
+ { uri: post.ref.uriStr },
524
+ {
525
+ headers: await network.serviceHeaders(
526
+ sc.dids.eve,
527
+ ids.AppBskyFeedGetPostThread,
528
+ ),
529
+ },
530
+ )
531
+ assert(isThreadViewPost(eveThread))
532
+ // eve cannot interact
533
+ expect(eveThread.post.viewer?.replyDisabled).toBe(true)
534
+ await checkReplyDisabled(post.ref.uriStr, sc.dids.eve, true)
535
+
443
536
  const {
444
537
  data: { thread: bobThread },
445
538
  } = await agent.api.app.bsky.feed.getPostThread(
@@ -452,8 +545,10 @@ describe('views with thread gating', () => {
452
545
  },
453
546
  )
454
547
  assert(isThreadViewPost(bobThread))
455
- expect(bobThread.post.viewer?.replyDisabled).toBe(true)
456
- await checkReplyDisabled(post.ref.uriStr, sc.dids.bob, true)
548
+ // bob follows carol, followers can reply
549
+ expect(bobThread.post.viewer?.replyDisabled).toBe(false)
550
+ await checkReplyDisabled(post.ref.uriStr, sc.dids.bob, false)
551
+
457
552
  const {
458
553
  data: { thread: aliceThread },
459
554
  } = await agent.api.app.bsky.feed.getPostThread(
@@ -466,8 +561,10 @@ describe('views with thread gating', () => {
466
561
  },
467
562
  )
468
563
  assert(isThreadViewPost(aliceThread))
564
+ // carol follows alice, followed users can reply
469
565
  expect(aliceThread.post.viewer?.replyDisabled).toBe(false)
470
566
  await checkReplyDisabled(post.ref.uriStr, sc.dids.alice, false)
567
+
471
568
  const {
472
569
  data: { thread: danThread },
473
570
  } = await agent.api.app.bsky.feed.getPostThread(
@@ -481,14 +578,17 @@ describe('views with thread gating', () => {
481
578
  )
482
579
  assert(isThreadViewPost(danThread))
483
580
  expect(forSnapshot(danThread.post.threadgate)).toMatchSnapshot()
581
+ // dan was mentioned, mentioned users can reply
484
582
  expect(danThread.post.viewer?.replyDisabled).toBe(false)
485
583
  await checkReplyDisabled(post.ref.uriStr, sc.dids.dan, false)
486
- const [reply1, reply2, ...otherReplies] = aliceThread.replies ?? []
584
+
585
+ const [reply1, reply2, reply3, ...otherReplies] = aliceThread.replies ?? []
487
586
  assert(isThreadViewPost(reply1))
488
587
  assert(isThreadViewPost(reply2))
588
+ assert(isThreadViewPost(reply3))
489
589
  expect(otherReplies.length).toEqual(0)
490
- expect([reply1.post.uri, reply2.post.uri].sort()).toEqual(
491
- [aliceReply.ref.uriStr, danReply.ref.uriStr].sort(),
590
+ expect([reply1.post.uri, reply2.post.uri, reply3.post.uri].sort()).toEqual(
591
+ [aliceReply.ref.uriStr, danReply.ref.uriStr, bobReply.ref.uriStr].sort(),
492
592
  )
493
593
  })
494
594