@atproto/api 0.20.22 → 0.20.25

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.
Files changed (64) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/client/lexicons.d.ts +28 -12
  3. package/dist/client/lexicons.d.ts.map +1 -1
  4. package/dist/client/lexicons.js +14 -6
  5. package/dist/client/lexicons.js.map +1 -1
  6. package/dist/client/types/app/bsky/ageassurance/defs.d.ts +2 -0
  7. package/dist/client/types/app/bsky/ageassurance/defs.d.ts.map +1 -1
  8. package/dist/client/types/app/bsky/ageassurance/defs.js.map +1 -1
  9. package/package.json +22 -16
  10. package/definitions/labels.json +0 -170
  11. package/docs/moderation.md +0 -260
  12. package/jest.config.cjs +0 -23
  13. package/jest.d.ts +0 -20
  14. package/jest.setup.ts +0 -97
  15. package/scripts/code/labels.mjs +0 -77
  16. package/scripts/generate-code.mjs +0 -4
  17. package/src/age-assurance.test.ts +0 -218
  18. package/src/age-assurance.ts +0 -137
  19. package/src/agent.ts +0 -1645
  20. package/src/atp-agent.ts +0 -570
  21. package/src/bsky-agent.ts +0 -14
  22. package/src/const.ts +0 -1
  23. package/src/index.ts +0 -44
  24. package/src/mocker.ts +0 -220
  25. package/src/moderation/decision.ts +0 -389
  26. package/src/moderation/index.ts +0 -69
  27. package/src/moderation/mutewords.ts +0 -170
  28. package/src/moderation/subjects/account.ts +0 -44
  29. package/src/moderation/subjects/feed-generator.ts +0 -24
  30. package/src/moderation/subjects/notification.ts +0 -25
  31. package/src/moderation/subjects/post.ts +0 -433
  32. package/src/moderation/subjects/profile.ts +0 -26
  33. package/src/moderation/subjects/status.ts +0 -27
  34. package/src/moderation/subjects/user-list.ts +0 -48
  35. package/src/moderation/types.ts +0 -191
  36. package/src/moderation/ui.ts +0 -21
  37. package/src/moderation/util.ts +0 -111
  38. package/src/predicate.ts +0 -52
  39. package/src/rich-text/detection.ts +0 -146
  40. package/src/rich-text/rich-text.ts +0 -418
  41. package/src/rich-text/sanitization.ts +0 -42
  42. package/src/rich-text/unicode.ts +0 -47
  43. package/src/rich-text/util.ts +0 -15
  44. package/src/session-manager.ts +0 -5
  45. package/src/types.ts +0 -165
  46. package/src/util.ts +0 -96
  47. package/tests/atp-agent.test.ts +0 -3863
  48. package/tests/dispatcher.test.ts +0 -527
  49. package/tests/errors.test.ts +0 -29
  50. package/tests/moderation-behaviors.test.ts +0 -951
  51. package/tests/moderation-custom-labels.test.ts +0 -334
  52. package/tests/moderation-mutewords.test.ts +0 -1299
  53. package/tests/moderation-prefs.test.ts +0 -402
  54. package/tests/moderation-quoteposts.test.ts +0 -277
  55. package/tests/moderation.test.ts +0 -739
  56. package/tests/rich-text-detection.test.ts +0 -456
  57. package/tests/rich-text-sanitization.test.ts +0 -216
  58. package/tests/rich-text.test.ts +0 -705
  59. package/tests/util/echo-server.ts +0 -37
  60. package/tests/util/moderation-behavior.ts +0 -268
  61. package/tsconfig.build.json +0 -9
  62. package/tsconfig.build.tsbuildinfo +0 -1
  63. package/tsconfig.json +0 -7
  64. package/tsconfig.tests.json +0 -10
@@ -1,3863 +0,0 @@
1
- import { TID } from '@atproto/common-web'
2
- import { TestNetworkNoAppView } from '@atproto/dev-env'
3
- import { asPredicate } from '../src/client/util.js'
4
- import {
5
- AppBskyActorDefs,
6
- AppBskyActorProfile,
7
- AtpAgent,
8
- ComAtprotoRepoPutRecord,
9
- DEFAULT_LABEL_SETTINGS,
10
- } from '../src/index.js'
11
- import {
12
- getSavedFeedType,
13
- savedFeedsToUriArrays,
14
- validateSavedFeed,
15
- } from '../src/util.js'
16
-
17
- describe('agent', () => {
18
- let network: TestNetworkNoAppView
19
-
20
- beforeAll(async () => {
21
- network = await TestNetworkNoAppView.create({
22
- dbPostgresSchema: 'api_atp_agent',
23
- })
24
- })
25
-
26
- afterAll(async () => {
27
- await network?.close()
28
- })
29
-
30
- const getProfileDisplayName = async (
31
- agent: AtpAgent,
32
- ): Promise<string | undefined> => {
33
- try {
34
- const res = await agent.app.bsky.actor.profile.get({
35
- repo: agent.accountDid,
36
- rkey: 'self',
37
- })
38
- return res.value.displayName ?? ''
39
- } catch (err) {
40
- return undefined
41
- }
42
- }
43
-
44
- it('clones correctly', () => {
45
- const agent = new AtpAgent({ service: network.pds.url })
46
- const agent2 = agent.clone()
47
- expect(agent2 instanceof AtpAgent).toBeTruthy()
48
- expect(agent.service).toEqual(agent2.service)
49
- })
50
-
51
- it('upsertProfile correctly creates and updates profiles.', async () => {
52
- const agent = new AtpAgent({ service: network.pds.url })
53
-
54
- await agent.createAccount({
55
- handle: 'user1.test',
56
- email: 'user1@test.com',
57
- password: 'password',
58
- })
59
- const displayName1 = await getProfileDisplayName(agent)
60
- expect(displayName1).toBeFalsy()
61
-
62
- await agent.upsertProfile((existing) => {
63
- expect(existing).toBeFalsy()
64
- return {
65
- displayName: 'Bob',
66
- }
67
- })
68
-
69
- const displayName2 = await getProfileDisplayName(agent)
70
- expect(displayName2).toBe('Bob')
71
-
72
- await agent.upsertProfile((existing) => {
73
- expect(existing).toBeTruthy()
74
- return {
75
- displayName: existing?.displayName?.toUpperCase(),
76
- }
77
- })
78
-
79
- const displayName3 = await getProfileDisplayName(agent)
80
- expect(displayName3).toBe('BOB')
81
- })
82
-
83
- it('upsertProfile correctly handles CAS failures.', async () => {
84
- const agent = new AtpAgent({ service: network.pds.url })
85
- await agent.createAccount({
86
- handle: 'user2.test',
87
- email: 'user2@test.com',
88
- password: 'password',
89
- })
90
-
91
- const displayName1 = await getProfileDisplayName(agent)
92
- expect(displayName1).toBeFalsy()
93
-
94
- let hasConflicted = false
95
- let ranTwice = false
96
- await agent.upsertProfile(async (_existing) => {
97
- if (!hasConflicted) {
98
- await agent.com.atproto.repo.putRecord({
99
- repo: agent.accountDid,
100
- collection: 'app.bsky.actor.profile',
101
- rkey: 'self',
102
- record: {
103
- $type: 'app.bsky.actor.profile',
104
- displayName: String(Math.random()),
105
- },
106
- })
107
- hasConflicted = true
108
- } else {
109
- ranTwice = true
110
- }
111
- return {
112
- displayName: 'Bob',
113
- }
114
- })
115
- expect(ranTwice).toBe(true)
116
-
117
- const displayName2 = await getProfileDisplayName(agent)
118
- expect(displayName2).toBe('Bob')
119
- })
120
-
121
- it('upsertProfile wont endlessly retry CAS failures.', async () => {
122
- const agent = new AtpAgent({ service: network.pds.url })
123
- await agent.createAccount({
124
- handle: 'user3.test',
125
- email: 'user3@test.com',
126
- password: 'password',
127
- })
128
-
129
- const displayName1 = await getProfileDisplayName(agent)
130
- expect(displayName1).toBeFalsy()
131
-
132
- const p = agent.upsertProfile(async (_existing) => {
133
- await agent.com.atproto.repo.putRecord({
134
- repo: agent.accountDid,
135
- collection: 'app.bsky.actor.profile',
136
- rkey: 'self',
137
- record: {
138
- $type: 'app.bsky.actor.profile',
139
- displayName: String(Math.random()),
140
- },
141
- })
142
- return {
143
- displayName: 'Bob',
144
- }
145
- })
146
- await expect(p).rejects.toThrow(ComAtprotoRepoPutRecord.InvalidSwapError)
147
- })
148
-
149
- it('upsertProfile validates the record.', async () => {
150
- const agent = new AtpAgent({ service: network.pds.url })
151
- await agent.createAccount({
152
- handle: 'user4.test',
153
- email: 'user4@test.com',
154
- password: 'password',
155
- })
156
-
157
- const p = agent.upsertProfile((_existing) => {
158
- return {
159
- displayName: { string: 'Bob' },
160
- } as unknown as AppBskyActorProfile.Record
161
- })
162
- await expect(p).rejects.toThrow('Record/displayName must be a string')
163
- })
164
-
165
- describe('app', () => {
166
- it('should retrieve the api app', () => {
167
- const agent = new AtpAgent({ service: network.pds.url })
168
- expect(agent.api).toBe(agent)
169
- expect(agent.app).toBeDefined()
170
- })
171
- })
172
-
173
- describe('post', () => {
174
- it('should throw if no session', async () => {
175
- const agent = new AtpAgent({ service: network.pds.url })
176
- await expect(agent.post({ text: 'foo' })).rejects.toThrow('Not logged in')
177
- })
178
- })
179
-
180
- describe('deletePost', () => {
181
- it('should throw if no session', async () => {
182
- const agent = new AtpAgent({ service: network.pds.url })
183
- await expect(agent.deletePost('foo')).rejects.toThrow('Not logged in')
184
- })
185
- })
186
-
187
- describe('like', () => {
188
- it('should throw if no session', async () => {
189
- const agent = new AtpAgent({ service: network.pds.url })
190
- await expect(agent.like('foo', 'bar')).rejects.toThrow('Not logged in')
191
- })
192
- })
193
-
194
- describe('deleteLike', () => {
195
- it('should throw if no session', async () => {
196
- const agent = new AtpAgent({ service: network.pds.url })
197
- await expect(agent.deleteLike('foo')).rejects.toThrow('Not logged in')
198
- })
199
- })
200
-
201
- describe('repost', () => {
202
- it('should throw if no session', async () => {
203
- const agent = new AtpAgent({ service: network.pds.url })
204
- await expect(agent.repost('foo', 'bar')).rejects.toThrow('Not logged in')
205
- })
206
- })
207
-
208
- describe('deleteRepost', () => {
209
- it('should throw if no session', async () => {
210
- const agent = new AtpAgent({ service: network.pds.url })
211
- await expect(agent.deleteRepost('foo')).rejects.toThrow('Not logged in')
212
- })
213
- })
214
-
215
- describe('follow', () => {
216
- it('should throw if no session', async () => {
217
- const agent = new AtpAgent({ service: network.pds.url })
218
- await expect(agent.follow('foo')).rejects.toThrow('Not logged in')
219
- })
220
- })
221
-
222
- describe('deleteFollow', () => {
223
- it('should throw if no session', async () => {
224
- const agent = new AtpAgent({ service: network.pds.url })
225
- await expect(agent.deleteFollow('foo')).rejects.toThrow('Not logged in')
226
- })
227
- })
228
-
229
- describe('preferences methods', () => {
230
- it('gets and sets preferences correctly', async () => {
231
- const agent = new AtpAgent({ service: network.pds.url })
232
- await agent.createAccount({
233
- handle: 'user5.test',
234
- email: 'user5@test.com',
235
- password: 'password',
236
- })
237
-
238
- const DEFAULT_LABELERS = AtpAgent.appLabelers.map((did) => ({
239
- did,
240
- labels: {},
241
- }))
242
-
243
- await expect(agent.getPreferences()).resolves.toStrictEqual({
244
- feeds: { pinned: undefined, saved: undefined },
245
- savedFeeds: [
246
- {
247
- id: expect.any(String),
248
- pinned: true,
249
- type: 'timeline',
250
- value: 'following',
251
- },
252
- ],
253
- moderationPrefs: {
254
- adultContentEnabled: false,
255
- labels: DEFAULT_LABEL_SETTINGS,
256
- labelers: DEFAULT_LABELERS,
257
- mutedWords: [],
258
- hiddenPosts: [],
259
- },
260
- birthDate: undefined,
261
- feedViewPrefs: {
262
- home: {
263
- hideReplies: false,
264
- hideRepliesByUnfollowed: true,
265
- hideRepliesByLikeCount: 0,
266
- hideReposts: false,
267
- hideQuotePosts: false,
268
- },
269
- },
270
- threadViewPrefs: {
271
- sort: 'hotness',
272
- },
273
- interests: {
274
- tags: [],
275
- },
276
- bskyAppState: {
277
- activeProgressGuide: undefined,
278
- queuedNudges: [],
279
- nuxs: [],
280
- },
281
- postInteractionSettings: {
282
- threadgateAllowRules: undefined,
283
- postgateEmbeddingRules: undefined,
284
- },
285
- verificationPrefs: {
286
- hideBadges: false,
287
- },
288
- liveEventPreferences: {
289
- hiddenFeedIds: [],
290
- hideAllFeeds: false,
291
- },
292
- })
293
-
294
- await agent.setAdultContentEnabled(true)
295
- await expect(agent.getPreferences()).resolves.toStrictEqual({
296
- feeds: { pinned: undefined, saved: undefined },
297
- savedFeeds: [
298
- {
299
- id: expect.any(String),
300
- pinned: true,
301
- type: 'timeline',
302
- value: 'following',
303
- },
304
- ],
305
- moderationPrefs: {
306
- adultContentEnabled: true,
307
- labels: DEFAULT_LABEL_SETTINGS,
308
- labelers: DEFAULT_LABELERS,
309
- mutedWords: [],
310
- hiddenPosts: [],
311
- },
312
- birthDate: undefined,
313
- feedViewPrefs: {
314
- home: {
315
- hideReplies: false,
316
- hideRepliesByUnfollowed: true,
317
- hideRepliesByLikeCount: 0,
318
- hideReposts: false,
319
- hideQuotePosts: false,
320
- },
321
- },
322
- threadViewPrefs: {
323
- sort: 'hotness',
324
- },
325
- interests: {
326
- tags: [],
327
- },
328
- bskyAppState: {
329
- activeProgressGuide: undefined,
330
- queuedNudges: [],
331
- nuxs: [],
332
- },
333
- postInteractionSettings: {
334
- threadgateAllowRules: undefined,
335
- postgateEmbeddingRules: undefined,
336
- },
337
- verificationPrefs: {
338
- hideBadges: false,
339
- },
340
- liveEventPreferences: {
341
- hiddenFeedIds: [],
342
- hideAllFeeds: false,
343
- },
344
- })
345
-
346
- await agent.setAdultContentEnabled(false)
347
- await expect(agent.getPreferences()).resolves.toStrictEqual({
348
- feeds: { pinned: undefined, saved: undefined },
349
- savedFeeds: [
350
- {
351
- id: expect.any(String),
352
- pinned: true,
353
- type: 'timeline',
354
- value: 'following',
355
- },
356
- ],
357
- moderationPrefs: {
358
- adultContentEnabled: false,
359
- labels: DEFAULT_LABEL_SETTINGS,
360
- labelers: DEFAULT_LABELERS,
361
- mutedWords: [],
362
- hiddenPosts: [],
363
- },
364
- birthDate: undefined,
365
- feedViewPrefs: {
366
- home: {
367
- hideReplies: false,
368
- hideRepliesByUnfollowed: true,
369
- hideRepliesByLikeCount: 0,
370
- hideReposts: false,
371
- hideQuotePosts: false,
372
- },
373
- },
374
- threadViewPrefs: {
375
- sort: 'hotness',
376
- },
377
- interests: {
378
- tags: [],
379
- },
380
- bskyAppState: {
381
- activeProgressGuide: undefined,
382
- queuedNudges: [],
383
- nuxs: [],
384
- },
385
- postInteractionSettings: {
386
- threadgateAllowRules: undefined,
387
- postgateEmbeddingRules: undefined,
388
- },
389
- verificationPrefs: {
390
- hideBadges: false,
391
- },
392
- liveEventPreferences: {
393
- hiddenFeedIds: [],
394
- hideAllFeeds: false,
395
- },
396
- })
397
-
398
- await agent.setContentLabelPref('misinfo', 'hide')
399
- await expect(agent.getPreferences()).resolves.toStrictEqual({
400
- feeds: { pinned: undefined, saved: undefined },
401
- savedFeeds: [
402
- {
403
- id: expect.any(String),
404
- pinned: true,
405
- type: 'timeline',
406
- value: 'following',
407
- },
408
- ],
409
- moderationPrefs: {
410
- adultContentEnabled: false,
411
- labels: { ...DEFAULT_LABEL_SETTINGS, misinfo: 'hide' },
412
- labelers: DEFAULT_LABELERS,
413
- mutedWords: [],
414
- hiddenPosts: [],
415
- },
416
- birthDate: undefined,
417
- feedViewPrefs: {
418
- home: {
419
- hideReplies: false,
420
- hideRepliesByUnfollowed: true,
421
- hideRepliesByLikeCount: 0,
422
- hideReposts: false,
423
- hideQuotePosts: false,
424
- },
425
- },
426
- threadViewPrefs: {
427
- sort: 'hotness',
428
- },
429
- interests: {
430
- tags: [],
431
- },
432
- bskyAppState: {
433
- activeProgressGuide: undefined,
434
- queuedNudges: [],
435
- nuxs: [],
436
- },
437
- postInteractionSettings: {
438
- threadgateAllowRules: undefined,
439
- postgateEmbeddingRules: undefined,
440
- },
441
- verificationPrefs: {
442
- hideBadges: false,
443
- },
444
- liveEventPreferences: {
445
- hiddenFeedIds: [],
446
- hideAllFeeds: false,
447
- },
448
- })
449
-
450
- await agent.setContentLabelPref('spam', 'ignore')
451
- await expect(agent.getPreferences()).resolves.toStrictEqual({
452
- feeds: { pinned: undefined, saved: undefined },
453
- savedFeeds: [
454
- {
455
- id: expect.any(String),
456
- pinned: true,
457
- type: 'timeline',
458
- value: 'following',
459
- },
460
- ],
461
- moderationPrefs: {
462
- adultContentEnabled: false,
463
- labels: {
464
- ...DEFAULT_LABEL_SETTINGS,
465
- misinfo: 'hide',
466
- spam: 'ignore',
467
- },
468
- labelers: DEFAULT_LABELERS,
469
- mutedWords: [],
470
- hiddenPosts: [],
471
- },
472
- birthDate: undefined,
473
- feedViewPrefs: {
474
- home: {
475
- hideReplies: false,
476
- hideRepliesByUnfollowed: true,
477
- hideRepliesByLikeCount: 0,
478
- hideReposts: false,
479
- hideQuotePosts: false,
480
- },
481
- },
482
- threadViewPrefs: {
483
- sort: 'hotness',
484
- },
485
- interests: {
486
- tags: [],
487
- },
488
- bskyAppState: {
489
- activeProgressGuide: undefined,
490
- queuedNudges: [],
491
- nuxs: [],
492
- },
493
- postInteractionSettings: {
494
- threadgateAllowRules: undefined,
495
- postgateEmbeddingRules: undefined,
496
- },
497
- verificationPrefs: {
498
- hideBadges: false,
499
- },
500
- liveEventPreferences: {
501
- hiddenFeedIds: [],
502
- hideAllFeeds: false,
503
- },
504
- })
505
-
506
- await agent.addSavedFeed('at://bob.com/app.bsky.feed.generator/fake')
507
- await expect(agent.getPreferences()).resolves.toStrictEqual({
508
- savedFeeds: [
509
- {
510
- id: expect.any(String),
511
- pinned: true,
512
- type: 'timeline',
513
- value: 'following',
514
- },
515
- ],
516
- feeds: {
517
- pinned: [],
518
- saved: ['at://bob.com/app.bsky.feed.generator/fake'],
519
- },
520
- moderationPrefs: {
521
- adultContentEnabled: false,
522
- labels: {
523
- ...DEFAULT_LABEL_SETTINGS,
524
- misinfo: 'hide',
525
- spam: 'ignore',
526
- },
527
- labelers: DEFAULT_LABELERS,
528
- mutedWords: [],
529
- hiddenPosts: [],
530
- },
531
- birthDate: undefined,
532
- feedViewPrefs: {
533
- home: {
534
- hideReplies: false,
535
- hideRepliesByUnfollowed: true,
536
- hideRepliesByLikeCount: 0,
537
- hideReposts: false,
538
- hideQuotePosts: false,
539
- },
540
- },
541
- threadViewPrefs: {
542
- sort: 'hotness',
543
- },
544
- interests: {
545
- tags: [],
546
- },
547
- bskyAppState: {
548
- activeProgressGuide: undefined,
549
- queuedNudges: [],
550
- nuxs: [],
551
- },
552
- postInteractionSettings: {
553
- threadgateAllowRules: undefined,
554
- postgateEmbeddingRules: undefined,
555
- },
556
- verificationPrefs: {
557
- hideBadges: false,
558
- },
559
- liveEventPreferences: {
560
- hiddenFeedIds: [],
561
- hideAllFeeds: false,
562
- },
563
- })
564
-
565
- await agent.addPinnedFeed('at://bob.com/app.bsky.feed.generator/fake')
566
- await expect(agent.getPreferences()).resolves.toStrictEqual({
567
- savedFeeds: [
568
- {
569
- id: expect.any(String),
570
- pinned: true,
571
- type: 'timeline',
572
- value: 'following',
573
- },
574
- ],
575
- feeds: {
576
- pinned: ['at://bob.com/app.bsky.feed.generator/fake'],
577
- saved: ['at://bob.com/app.bsky.feed.generator/fake'],
578
- },
579
- moderationPrefs: {
580
- adultContentEnabled: false,
581
- labels: {
582
- ...DEFAULT_LABEL_SETTINGS,
583
- misinfo: 'hide',
584
- spam: 'ignore',
585
- },
586
- labelers: DEFAULT_LABELERS,
587
- mutedWords: [],
588
- hiddenPosts: [],
589
- },
590
- birthDate: undefined,
591
- feedViewPrefs: {
592
- home: {
593
- hideReplies: false,
594
- hideRepliesByUnfollowed: true,
595
- hideRepliesByLikeCount: 0,
596
- hideReposts: false,
597
- hideQuotePosts: false,
598
- },
599
- },
600
- threadViewPrefs: {
601
- sort: 'hotness',
602
- },
603
- interests: {
604
- tags: [],
605
- },
606
- bskyAppState: {
607
- activeProgressGuide: undefined,
608
- queuedNudges: [],
609
- nuxs: [],
610
- },
611
- postInteractionSettings: {
612
- threadgateAllowRules: undefined,
613
- postgateEmbeddingRules: undefined,
614
- },
615
- verificationPrefs: {
616
- hideBadges: false,
617
- },
618
- liveEventPreferences: {
619
- hiddenFeedIds: [],
620
- hideAllFeeds: false,
621
- },
622
- })
623
-
624
- await agent.removePinnedFeed('at://bob.com/app.bsky.feed.generator/fake')
625
- await expect(agent.getPreferences()).resolves.toStrictEqual({
626
- savedFeeds: [
627
- {
628
- id: expect.any(String),
629
- pinned: true,
630
- type: 'timeline',
631
- value: 'following',
632
- },
633
- ],
634
- feeds: {
635
- pinned: [],
636
- saved: ['at://bob.com/app.bsky.feed.generator/fake'],
637
- },
638
- moderationPrefs: {
639
- adultContentEnabled: false,
640
- labels: {
641
- ...DEFAULT_LABEL_SETTINGS,
642
- misinfo: 'hide',
643
- spam: 'ignore',
644
- },
645
- labelers: DEFAULT_LABELERS,
646
- mutedWords: [],
647
- hiddenPosts: [],
648
- },
649
- birthDate: undefined,
650
- feedViewPrefs: {
651
- home: {
652
- hideReplies: false,
653
- hideRepliesByUnfollowed: true,
654
- hideRepliesByLikeCount: 0,
655
- hideReposts: false,
656
- hideQuotePosts: false,
657
- },
658
- },
659
- threadViewPrefs: {
660
- sort: 'hotness',
661
- },
662
- interests: {
663
- tags: [],
664
- },
665
- bskyAppState: {
666
- activeProgressGuide: undefined,
667
- queuedNudges: [],
668
- nuxs: [],
669
- },
670
- postInteractionSettings: {
671
- threadgateAllowRules: undefined,
672
- postgateEmbeddingRules: undefined,
673
- },
674
- verificationPrefs: {
675
- hideBadges: false,
676
- },
677
- liveEventPreferences: {
678
- hiddenFeedIds: [],
679
- hideAllFeeds: false,
680
- },
681
- })
682
-
683
- await agent.removeSavedFeed('at://bob.com/app.bsky.feed.generator/fake')
684
- await expect(agent.getPreferences()).resolves.toStrictEqual({
685
- savedFeeds: [
686
- {
687
- id: expect.any(String),
688
- pinned: true,
689
- type: 'timeline',
690
- value: 'following',
691
- },
692
- ],
693
- feeds: {
694
- pinned: [],
695
- saved: [],
696
- },
697
- moderationPrefs: {
698
- adultContentEnabled: false,
699
- labels: {
700
- ...DEFAULT_LABEL_SETTINGS,
701
- misinfo: 'hide',
702
- spam: 'ignore',
703
- },
704
- labelers: DEFAULT_LABELERS,
705
- mutedWords: [],
706
- hiddenPosts: [],
707
- },
708
- birthDate: undefined,
709
- feedViewPrefs: {
710
- home: {
711
- hideReplies: false,
712
- hideRepliesByUnfollowed: true,
713
- hideRepliesByLikeCount: 0,
714
- hideReposts: false,
715
- hideQuotePosts: false,
716
- },
717
- },
718
- threadViewPrefs: {
719
- sort: 'hotness',
720
- },
721
- interests: {
722
- tags: [],
723
- },
724
- bskyAppState: {
725
- activeProgressGuide: undefined,
726
- queuedNudges: [],
727
- nuxs: [],
728
- },
729
- postInteractionSettings: {
730
- threadgateAllowRules: undefined,
731
- postgateEmbeddingRules: undefined,
732
- },
733
- verificationPrefs: {
734
- hideBadges: false,
735
- },
736
- liveEventPreferences: {
737
- hiddenFeedIds: [],
738
- hideAllFeeds: false,
739
- },
740
- })
741
-
742
- await agent.addPinnedFeed('at://bob.com/app.bsky.feed.generator/fake')
743
- await expect(agent.getPreferences()).resolves.toStrictEqual({
744
- savedFeeds: [
745
- {
746
- id: expect.any(String),
747
- pinned: true,
748
- type: 'timeline',
749
- value: 'following',
750
- },
751
- ],
752
- feeds: {
753
- pinned: ['at://bob.com/app.bsky.feed.generator/fake'],
754
- saved: ['at://bob.com/app.bsky.feed.generator/fake'],
755
- },
756
- moderationPrefs: {
757
- adultContentEnabled: false,
758
- labels: {
759
- ...DEFAULT_LABEL_SETTINGS,
760
- misinfo: 'hide',
761
- spam: 'ignore',
762
- },
763
- labelers: DEFAULT_LABELERS,
764
- mutedWords: [],
765
- hiddenPosts: [],
766
- },
767
- birthDate: undefined,
768
- feedViewPrefs: {
769
- home: {
770
- hideReplies: false,
771
- hideRepliesByUnfollowed: true,
772
- hideRepliesByLikeCount: 0,
773
- hideReposts: false,
774
- hideQuotePosts: false,
775
- },
776
- },
777
- threadViewPrefs: {
778
- sort: 'hotness',
779
- },
780
- interests: {
781
- tags: [],
782
- },
783
- bskyAppState: {
784
- activeProgressGuide: undefined,
785
- queuedNudges: [],
786
- nuxs: [],
787
- },
788
- postInteractionSettings: {
789
- threadgateAllowRules: undefined,
790
- postgateEmbeddingRules: undefined,
791
- },
792
- verificationPrefs: {
793
- hideBadges: false,
794
- },
795
- liveEventPreferences: {
796
- hiddenFeedIds: [],
797
- hideAllFeeds: false,
798
- },
799
- })
800
-
801
- await agent.addPinnedFeed('at://bob.com/app.bsky.feed.generator/fake2')
802
- await expect(agent.getPreferences()).resolves.toStrictEqual({
803
- savedFeeds: [
804
- {
805
- id: expect.any(String),
806
- pinned: true,
807
- type: 'timeline',
808
- value: 'following',
809
- },
810
- ],
811
- feeds: {
812
- pinned: [
813
- 'at://bob.com/app.bsky.feed.generator/fake',
814
- 'at://bob.com/app.bsky.feed.generator/fake2',
815
- ],
816
- saved: [
817
- 'at://bob.com/app.bsky.feed.generator/fake',
818
- 'at://bob.com/app.bsky.feed.generator/fake2',
819
- ],
820
- },
821
- moderationPrefs: {
822
- adultContentEnabled: false,
823
- labels: {
824
- ...DEFAULT_LABEL_SETTINGS,
825
- misinfo: 'hide',
826
- spam: 'ignore',
827
- },
828
- labelers: DEFAULT_LABELERS,
829
- mutedWords: [],
830
- hiddenPosts: [],
831
- },
832
- birthDate: undefined,
833
- feedViewPrefs: {
834
- home: {
835
- hideReplies: false,
836
- hideRepliesByUnfollowed: true,
837
- hideRepliesByLikeCount: 0,
838
- hideReposts: false,
839
- hideQuotePosts: false,
840
- },
841
- },
842
- threadViewPrefs: {
843
- sort: 'hotness',
844
- },
845
- interests: {
846
- tags: [],
847
- },
848
- bskyAppState: {
849
- activeProgressGuide: undefined,
850
- queuedNudges: [],
851
- nuxs: [],
852
- },
853
- postInteractionSettings: {
854
- threadgateAllowRules: undefined,
855
- postgateEmbeddingRules: undefined,
856
- },
857
- verificationPrefs: {
858
- hideBadges: false,
859
- },
860
- liveEventPreferences: {
861
- hiddenFeedIds: [],
862
- hideAllFeeds: false,
863
- },
864
- })
865
-
866
- await agent.removeSavedFeed('at://bob.com/app.bsky.feed.generator/fake')
867
- await expect(agent.getPreferences()).resolves.toStrictEqual({
868
- savedFeeds: [
869
- {
870
- id: expect.any(String),
871
- pinned: true,
872
- type: 'timeline',
873
- value: 'following',
874
- },
875
- ],
876
- feeds: {
877
- pinned: ['at://bob.com/app.bsky.feed.generator/fake2'],
878
- saved: ['at://bob.com/app.bsky.feed.generator/fake2'],
879
- },
880
- moderationPrefs: {
881
- adultContentEnabled: false,
882
- labels: {
883
- ...DEFAULT_LABEL_SETTINGS,
884
- misinfo: 'hide',
885
- spam: 'ignore',
886
- },
887
- labelers: DEFAULT_LABELERS,
888
- mutedWords: [],
889
- hiddenPosts: [],
890
- },
891
- birthDate: undefined,
892
- feedViewPrefs: {
893
- home: {
894
- hideReplies: false,
895
- hideRepliesByUnfollowed: true,
896
- hideRepliesByLikeCount: 0,
897
- hideReposts: false,
898
- hideQuotePosts: false,
899
- },
900
- },
901
- threadViewPrefs: {
902
- sort: 'hotness',
903
- },
904
- interests: {
905
- tags: [],
906
- },
907
- bskyAppState: {
908
- activeProgressGuide: undefined,
909
- queuedNudges: [],
910
- nuxs: [],
911
- },
912
- postInteractionSettings: {
913
- threadgateAllowRules: undefined,
914
- postgateEmbeddingRules: undefined,
915
- },
916
- verificationPrefs: {
917
- hideBadges: false,
918
- },
919
- liveEventPreferences: {
920
- hiddenFeedIds: [],
921
- hideAllFeeds: false,
922
- },
923
- })
924
-
925
- await agent.setPersonalDetails({ birthDate: '2023-09-11T18:05:42.556Z' })
926
- await expect(agent.getPreferences()).resolves.toStrictEqual({
927
- savedFeeds: [
928
- {
929
- id: expect.any(String),
930
- pinned: true,
931
- type: 'timeline',
932
- value: 'following',
933
- },
934
- ],
935
- feeds: {
936
- pinned: ['at://bob.com/app.bsky.feed.generator/fake2'],
937
- saved: ['at://bob.com/app.bsky.feed.generator/fake2'],
938
- },
939
- moderationPrefs: {
940
- adultContentEnabled: false,
941
- labels: {
942
- ...DEFAULT_LABEL_SETTINGS,
943
- misinfo: 'hide',
944
- spam: 'ignore',
945
- },
946
- labelers: DEFAULT_LABELERS,
947
- mutedWords: [],
948
- hiddenPosts: [],
949
- },
950
- birthDate: new Date('2023-09-11T18:05:42.556Z'),
951
- declaredAge: {
952
- isOverAge13: false,
953
- isOverAge16: false,
954
- isOverAge18: false,
955
- },
956
- feedViewPrefs: {
957
- home: {
958
- hideReplies: false,
959
- hideRepliesByUnfollowed: true,
960
- hideRepliesByLikeCount: 0,
961
- hideReposts: false,
962
- hideQuotePosts: false,
963
- },
964
- },
965
- threadViewPrefs: {
966
- sort: 'hotness',
967
- },
968
- interests: {
969
- tags: [],
970
- },
971
- bskyAppState: {
972
- activeProgressGuide: undefined,
973
- queuedNudges: [],
974
- nuxs: [],
975
- },
976
- postInteractionSettings: {
977
- threadgateAllowRules: undefined,
978
- postgateEmbeddingRules: undefined,
979
- },
980
- verificationPrefs: {
981
- hideBadges: false,
982
- },
983
- liveEventPreferences: {
984
- hiddenFeedIds: [],
985
- hideAllFeeds: false,
986
- },
987
- })
988
-
989
- await agent.setFeedViewPrefs('home', { hideReplies: true })
990
- await expect(agent.getPreferences()).resolves.toStrictEqual({
991
- savedFeeds: [
992
- {
993
- id: expect.any(String),
994
- pinned: true,
995
- type: 'timeline',
996
- value: 'following',
997
- },
998
- ],
999
- feeds: {
1000
- pinned: ['at://bob.com/app.bsky.feed.generator/fake2'],
1001
- saved: ['at://bob.com/app.bsky.feed.generator/fake2'],
1002
- },
1003
- moderationPrefs: {
1004
- adultContentEnabled: false,
1005
- labels: {
1006
- ...DEFAULT_LABEL_SETTINGS,
1007
- misinfo: 'hide',
1008
- spam: 'ignore',
1009
- },
1010
- labelers: DEFAULT_LABELERS,
1011
- mutedWords: [],
1012
- hiddenPosts: [],
1013
- },
1014
- birthDate: new Date('2023-09-11T18:05:42.556Z'),
1015
- declaredAge: {
1016
- isOverAge13: false,
1017
- isOverAge16: false,
1018
- isOverAge18: false,
1019
- },
1020
- feedViewPrefs: {
1021
- home: {
1022
- hideReplies: true,
1023
- hideRepliesByUnfollowed: true,
1024
- hideRepliesByLikeCount: 0,
1025
- hideReposts: false,
1026
- hideQuotePosts: false,
1027
- },
1028
- },
1029
- threadViewPrefs: {
1030
- sort: 'hotness',
1031
- },
1032
- interests: {
1033
- tags: [],
1034
- },
1035
- bskyAppState: {
1036
- activeProgressGuide: undefined,
1037
- queuedNudges: [],
1038
- nuxs: [],
1039
- },
1040
- postInteractionSettings: {
1041
- threadgateAllowRules: undefined,
1042
- postgateEmbeddingRules: undefined,
1043
- },
1044
- verificationPrefs: {
1045
- hideBadges: false,
1046
- },
1047
- liveEventPreferences: {
1048
- hiddenFeedIds: [],
1049
- hideAllFeeds: false,
1050
- },
1051
- })
1052
-
1053
- await agent.setFeedViewPrefs('home', { hideReplies: false })
1054
- await expect(agent.getPreferences()).resolves.toStrictEqual({
1055
- savedFeeds: [
1056
- {
1057
- id: expect.any(String),
1058
- pinned: true,
1059
- type: 'timeline',
1060
- value: 'following',
1061
- },
1062
- ],
1063
- feeds: {
1064
- pinned: ['at://bob.com/app.bsky.feed.generator/fake2'],
1065
- saved: ['at://bob.com/app.bsky.feed.generator/fake2'],
1066
- },
1067
- moderationPrefs: {
1068
- adultContentEnabled: false,
1069
- labels: {
1070
- ...DEFAULT_LABEL_SETTINGS,
1071
- misinfo: 'hide',
1072
- spam: 'ignore',
1073
- },
1074
- labelers: DEFAULT_LABELERS,
1075
- mutedWords: [],
1076
- hiddenPosts: [],
1077
- },
1078
- birthDate: new Date('2023-09-11T18:05:42.556Z'),
1079
- declaredAge: {
1080
- isOverAge13: false,
1081
- isOverAge16: false,
1082
- isOverAge18: false,
1083
- },
1084
- feedViewPrefs: {
1085
- home: {
1086
- hideReplies: false,
1087
- hideRepliesByUnfollowed: true,
1088
- hideRepliesByLikeCount: 0,
1089
- hideReposts: false,
1090
- hideQuotePosts: false,
1091
- },
1092
- },
1093
- threadViewPrefs: {
1094
- sort: 'hotness',
1095
- },
1096
- interests: {
1097
- tags: [],
1098
- },
1099
- bskyAppState: {
1100
- activeProgressGuide: undefined,
1101
- queuedNudges: [],
1102
- nuxs: [],
1103
- },
1104
- postInteractionSettings: {
1105
- threadgateAllowRules: undefined,
1106
- postgateEmbeddingRules: undefined,
1107
- },
1108
- verificationPrefs: {
1109
- hideBadges: false,
1110
- },
1111
- liveEventPreferences: {
1112
- hiddenFeedIds: [],
1113
- hideAllFeeds: false,
1114
- },
1115
- })
1116
-
1117
- await agent.setFeedViewPrefs('other', { hideReplies: true })
1118
- await expect(agent.getPreferences()).resolves.toStrictEqual({
1119
- savedFeeds: [
1120
- {
1121
- id: expect.any(String),
1122
- pinned: true,
1123
- type: 'timeline',
1124
- value: 'following',
1125
- },
1126
- ],
1127
- feeds: {
1128
- pinned: ['at://bob.com/app.bsky.feed.generator/fake2'],
1129
- saved: ['at://bob.com/app.bsky.feed.generator/fake2'],
1130
- },
1131
- moderationPrefs: {
1132
- adultContentEnabled: false,
1133
- labels: {
1134
- ...DEFAULT_LABEL_SETTINGS,
1135
- misinfo: 'hide',
1136
- spam: 'ignore',
1137
- },
1138
- labelers: DEFAULT_LABELERS,
1139
- mutedWords: [],
1140
- hiddenPosts: [],
1141
- },
1142
- birthDate: new Date('2023-09-11T18:05:42.556Z'),
1143
- declaredAge: {
1144
- isOverAge13: false,
1145
- isOverAge16: false,
1146
- isOverAge18: false,
1147
- },
1148
- feedViewPrefs: {
1149
- home: {
1150
- hideReplies: false,
1151
- hideRepliesByUnfollowed: true,
1152
- hideRepliesByLikeCount: 0,
1153
- hideReposts: false,
1154
- hideQuotePosts: false,
1155
- },
1156
- other: {
1157
- hideReplies: true,
1158
- hideRepliesByUnfollowed: true,
1159
- hideRepliesByLikeCount: 0,
1160
- hideReposts: false,
1161
- hideQuotePosts: false,
1162
- },
1163
- },
1164
- threadViewPrefs: {
1165
- sort: 'hotness',
1166
- },
1167
- interests: {
1168
- tags: [],
1169
- },
1170
- bskyAppState: {
1171
- activeProgressGuide: undefined,
1172
- queuedNudges: [],
1173
- nuxs: [],
1174
- },
1175
- postInteractionSettings: {
1176
- threadgateAllowRules: undefined,
1177
- postgateEmbeddingRules: undefined,
1178
- },
1179
- verificationPrefs: {
1180
- hideBadges: false,
1181
- },
1182
- liveEventPreferences: {
1183
- hiddenFeedIds: [],
1184
- hideAllFeeds: false,
1185
- },
1186
- })
1187
-
1188
- await agent.setThreadViewPrefs({ sort: 'random' })
1189
- await expect(agent.getPreferences()).resolves.toStrictEqual({
1190
- savedFeeds: [
1191
- {
1192
- id: expect.any(String),
1193
- pinned: true,
1194
- type: 'timeline',
1195
- value: 'following',
1196
- },
1197
- ],
1198
- feeds: {
1199
- pinned: ['at://bob.com/app.bsky.feed.generator/fake2'],
1200
- saved: ['at://bob.com/app.bsky.feed.generator/fake2'],
1201
- },
1202
- moderationPrefs: {
1203
- adultContentEnabled: false,
1204
- labels: {
1205
- ...DEFAULT_LABEL_SETTINGS,
1206
- misinfo: 'hide',
1207
- spam: 'ignore',
1208
- },
1209
- labelers: DEFAULT_LABELERS,
1210
- mutedWords: [],
1211
- hiddenPosts: [],
1212
- },
1213
- birthDate: new Date('2023-09-11T18:05:42.556Z'),
1214
- declaredAge: {
1215
- isOverAge13: false,
1216
- isOverAge16: false,
1217
- isOverAge18: false,
1218
- },
1219
- feedViewPrefs: {
1220
- home: {
1221
- hideReplies: false,
1222
- hideRepliesByUnfollowed: true,
1223
- hideRepliesByLikeCount: 0,
1224
- hideReposts: false,
1225
- hideQuotePosts: false,
1226
- },
1227
- other: {
1228
- hideReplies: true,
1229
- hideRepliesByUnfollowed: true,
1230
- hideRepliesByLikeCount: 0,
1231
- hideReposts: false,
1232
- hideQuotePosts: false,
1233
- },
1234
- },
1235
- threadViewPrefs: {
1236
- sort: 'random',
1237
- },
1238
- interests: {
1239
- tags: [],
1240
- },
1241
- bskyAppState: {
1242
- activeProgressGuide: undefined,
1243
- queuedNudges: [],
1244
- nuxs: [],
1245
- },
1246
- postInteractionSettings: {
1247
- threadgateAllowRules: undefined,
1248
- postgateEmbeddingRules: undefined,
1249
- },
1250
- verificationPrefs: {
1251
- hideBadges: false,
1252
- },
1253
- liveEventPreferences: {
1254
- hiddenFeedIds: [],
1255
- hideAllFeeds: false,
1256
- },
1257
- })
1258
-
1259
- await agent.setThreadViewPrefs({ sort: 'oldest' })
1260
- await expect(agent.getPreferences()).resolves.toStrictEqual({
1261
- savedFeeds: [
1262
- {
1263
- id: expect.any(String),
1264
- pinned: true,
1265
- type: 'timeline',
1266
- value: 'following',
1267
- },
1268
- ],
1269
- feeds: {
1270
- pinned: ['at://bob.com/app.bsky.feed.generator/fake2'],
1271
- saved: ['at://bob.com/app.bsky.feed.generator/fake2'],
1272
- },
1273
- moderationPrefs: {
1274
- adultContentEnabled: false,
1275
- labels: {
1276
- ...DEFAULT_LABEL_SETTINGS,
1277
- misinfo: 'hide',
1278
- spam: 'ignore',
1279
- },
1280
- labelers: DEFAULT_LABELERS,
1281
- mutedWords: [],
1282
- hiddenPosts: [],
1283
- },
1284
- birthDate: new Date('2023-09-11T18:05:42.556Z'),
1285
- declaredAge: {
1286
- isOverAge13: false,
1287
- isOverAge16: false,
1288
- isOverAge18: false,
1289
- },
1290
- feedViewPrefs: {
1291
- home: {
1292
- hideReplies: false,
1293
- hideRepliesByUnfollowed: true,
1294
- hideRepliesByLikeCount: 0,
1295
- hideReposts: false,
1296
- hideQuotePosts: false,
1297
- },
1298
- other: {
1299
- hideReplies: true,
1300
- hideRepliesByUnfollowed: true,
1301
- hideRepliesByLikeCount: 0,
1302
- hideReposts: false,
1303
- hideQuotePosts: false,
1304
- },
1305
- },
1306
- threadViewPrefs: {
1307
- sort: 'oldest',
1308
- },
1309
- interests: {
1310
- tags: [],
1311
- },
1312
- bskyAppState: {
1313
- activeProgressGuide: undefined,
1314
- queuedNudges: [],
1315
- nuxs: [],
1316
- },
1317
- postInteractionSettings: {
1318
- threadgateAllowRules: undefined,
1319
- postgateEmbeddingRules: undefined,
1320
- },
1321
- verificationPrefs: {
1322
- hideBadges: false,
1323
- },
1324
- liveEventPreferences: {
1325
- hiddenFeedIds: [],
1326
- hideAllFeeds: false,
1327
- },
1328
- })
1329
-
1330
- await agent.setInterestsPref({ tags: ['foo', 'bar'] })
1331
- await expect(agent.getPreferences()).resolves.toStrictEqual({
1332
- savedFeeds: [
1333
- {
1334
- id: expect.any(String),
1335
- pinned: true,
1336
- type: 'timeline',
1337
- value: 'following',
1338
- },
1339
- ],
1340
- feeds: {
1341
- pinned: ['at://bob.com/app.bsky.feed.generator/fake2'],
1342
- saved: ['at://bob.com/app.bsky.feed.generator/fake2'],
1343
- },
1344
- moderationPrefs: {
1345
- adultContentEnabled: false,
1346
- labels: {
1347
- ...DEFAULT_LABEL_SETTINGS,
1348
- misinfo: 'hide',
1349
- spam: 'ignore',
1350
- },
1351
- labelers: DEFAULT_LABELERS,
1352
- mutedWords: [],
1353
- hiddenPosts: [],
1354
- },
1355
- birthDate: new Date('2023-09-11T18:05:42.556Z'),
1356
- declaredAge: {
1357
- isOverAge13: false,
1358
- isOverAge16: false,
1359
- isOverAge18: false,
1360
- },
1361
- feedViewPrefs: {
1362
- home: {
1363
- hideReplies: false,
1364
- hideRepliesByUnfollowed: true,
1365
- hideRepliesByLikeCount: 0,
1366
- hideReposts: false,
1367
- hideQuotePosts: false,
1368
- },
1369
- other: {
1370
- hideReplies: true,
1371
- hideRepliesByUnfollowed: true,
1372
- hideRepliesByLikeCount: 0,
1373
- hideReposts: false,
1374
- hideQuotePosts: false,
1375
- },
1376
- },
1377
- threadViewPrefs: {
1378
- sort: 'oldest',
1379
- },
1380
- interests: {
1381
- tags: ['foo', 'bar'],
1382
- },
1383
- bskyAppState: {
1384
- activeProgressGuide: undefined,
1385
- queuedNudges: [],
1386
- nuxs: [],
1387
- },
1388
- postInteractionSettings: {
1389
- threadgateAllowRules: undefined,
1390
- postgateEmbeddingRules: undefined,
1391
- },
1392
- verificationPrefs: {
1393
- hideBadges: false,
1394
- },
1395
- liveEventPreferences: {
1396
- hiddenFeedIds: [],
1397
- hideAllFeeds: false,
1398
- },
1399
- })
1400
- })
1401
-
1402
- it('resolves duplicates correctly', async () => {
1403
- const agent = new AtpAgent({ service: network.pds.url })
1404
-
1405
- await agent.createAccount({
1406
- handle: 'user6.test',
1407
- email: 'user6@test.com',
1408
- password: 'password',
1409
- })
1410
-
1411
- await agent.app.bsky.actor.putPreferences({
1412
- preferences: [
1413
- {
1414
- $type: 'app.bsky.actor.defs#contentLabelPref',
1415
- label: 'porn',
1416
- visibility: 'show',
1417
- },
1418
- {
1419
- $type: 'app.bsky.actor.defs#contentLabelPref',
1420
- label: 'porn',
1421
- visibility: 'hide',
1422
- },
1423
- {
1424
- $type: 'app.bsky.actor.defs#contentLabelPref',
1425
- label: 'porn',
1426
- visibility: 'show',
1427
- },
1428
- {
1429
- $type: 'app.bsky.actor.defs#contentLabelPref',
1430
- label: 'porn',
1431
- visibility: 'warn',
1432
- },
1433
- {
1434
- $type: 'app.bsky.actor.defs#labelersPref',
1435
- labelers: [
1436
- {
1437
- did: 'did:plc:first-labeler',
1438
- },
1439
- ],
1440
- },
1441
- {
1442
- $type: 'app.bsky.actor.defs#labelersPref',
1443
- labelers: [
1444
- {
1445
- did: 'did:plc:first-labeler',
1446
- },
1447
- {
1448
- did: 'did:plc:other',
1449
- },
1450
- ],
1451
- },
1452
- {
1453
- $type: 'app.bsky.actor.defs#adultContentPref',
1454
- enabled: true,
1455
- },
1456
- {
1457
- $type: 'app.bsky.actor.defs#adultContentPref',
1458
- enabled: false,
1459
- },
1460
- {
1461
- $type: 'app.bsky.actor.defs#adultContentPref',
1462
- enabled: true,
1463
- },
1464
- {
1465
- $type: 'app.bsky.actor.defs#savedFeedsPref',
1466
- pinned: [
1467
- 'at://bob.com/app.bsky.feed.generator/fake',
1468
- 'at://bob.com/app.bsky.feed.generator/fake2',
1469
- ],
1470
- saved: [
1471
- 'at://bob.com/app.bsky.feed.generator/fake',
1472
- 'at://bob.com/app.bsky.feed.generator/fake2',
1473
- ],
1474
- },
1475
- {
1476
- $type: 'app.bsky.actor.defs#savedFeedsPref',
1477
- pinned: [],
1478
- saved: [],
1479
- },
1480
- {
1481
- $type: 'app.bsky.actor.defs#personalDetailsPref',
1482
- birthDate: '2023-09-11T18:05:42.556Z',
1483
- },
1484
- {
1485
- $type: 'app.bsky.actor.defs#personalDetailsPref',
1486
- birthDate: '2021-09-11T18:05:42.556Z',
1487
- },
1488
- {
1489
- $type: 'app.bsky.actor.defs#declaredAgePref',
1490
- isOverAge13: false,
1491
- isOverAge16: false,
1492
- isOverAge18: false,
1493
- },
1494
- {
1495
- $type: 'app.bsky.actor.defs#feedViewPref',
1496
- feed: 'home',
1497
- hideReplies: false,
1498
- hideRepliesByUnfollowed: true,
1499
- hideRepliesByLikeCount: 0,
1500
- hideReposts: false,
1501
- hideQuotePosts: false,
1502
- },
1503
- {
1504
- $type: 'app.bsky.actor.defs#feedViewPref',
1505
- feed: 'home',
1506
- hideReplies: true,
1507
- hideRepliesByUnfollowed: false,
1508
- hideRepliesByLikeCount: 10,
1509
- hideReposts: true,
1510
- hideQuotePosts: true,
1511
- },
1512
- {
1513
- $type: 'app.bsky.actor.defs#threadViewPref',
1514
- sort: 'oldest',
1515
- },
1516
- {
1517
- $type: 'app.bsky.actor.defs#threadViewPref',
1518
- sort: 'newest',
1519
- },
1520
- {
1521
- $type: 'app.bsky.actor.defs#bskyAppStatePref',
1522
- queuedNudges: ['one'],
1523
- },
1524
- {
1525
- $type: 'app.bsky.actor.defs#bskyAppStatePref',
1526
- activeProgressGuide: undefined,
1527
- queuedNudges: ['two'],
1528
- },
1529
- ],
1530
- })
1531
- await expect(agent.getPreferences()).resolves.toStrictEqual({
1532
- savedFeeds: [
1533
- {
1534
- id: expect.any(String),
1535
- type: 'timeline',
1536
- value: 'following',
1537
- pinned: true,
1538
- },
1539
- ],
1540
- feeds: {
1541
- pinned: [],
1542
- saved: [],
1543
- },
1544
- moderationPrefs: {
1545
- adultContentEnabled: true,
1546
- labels: {
1547
- ...DEFAULT_LABEL_SETTINGS,
1548
- porn: 'warn',
1549
- },
1550
- labelers: [
1551
- ...AtpAgent.appLabelers.map((did) => ({ did, labels: {} })),
1552
- {
1553
- did: 'did:plc:first-labeler',
1554
- labels: {},
1555
- },
1556
- {
1557
- did: 'did:plc:other',
1558
- labels: {},
1559
- },
1560
- ],
1561
- mutedWords: [],
1562
- hiddenPosts: [],
1563
- },
1564
- birthDate: new Date('2021-09-11T18:05:42.556Z'),
1565
- declaredAge: {
1566
- isOverAge13: false,
1567
- isOverAge16: false,
1568
- isOverAge18: false,
1569
- },
1570
- feedViewPrefs: {
1571
- home: {
1572
- hideReplies: true,
1573
- hideRepliesByUnfollowed: false,
1574
- hideRepliesByLikeCount: 10,
1575
- hideReposts: true,
1576
- hideQuotePosts: true,
1577
- },
1578
- },
1579
- threadViewPrefs: {
1580
- sort: 'newest',
1581
- },
1582
- interests: {
1583
- tags: [],
1584
- },
1585
- bskyAppState: {
1586
- activeProgressGuide: undefined,
1587
- queuedNudges: ['two'],
1588
- nuxs: [],
1589
- },
1590
- postInteractionSettings: {
1591
- threadgateAllowRules: undefined,
1592
- postgateEmbeddingRules: undefined,
1593
- },
1594
- verificationPrefs: {
1595
- hideBadges: false,
1596
- },
1597
- liveEventPreferences: {
1598
- hiddenFeedIds: [],
1599
- hideAllFeeds: false,
1600
- },
1601
- })
1602
-
1603
- await agent.setAdultContentEnabled(false)
1604
- await expect(agent.getPreferences()).resolves.toStrictEqual({
1605
- savedFeeds: [
1606
- {
1607
- id: expect.any(String),
1608
- type: 'timeline',
1609
- value: 'following',
1610
- pinned: true,
1611
- },
1612
- ],
1613
- feeds: {
1614
- pinned: [],
1615
- saved: [],
1616
- },
1617
- moderationPrefs: {
1618
- adultContentEnabled: false,
1619
- labels: {
1620
- ...DEFAULT_LABEL_SETTINGS,
1621
- porn: 'warn',
1622
- },
1623
- labelers: [
1624
- ...AtpAgent.appLabelers.map((did) => ({ did, labels: {} })),
1625
- {
1626
- did: 'did:plc:first-labeler',
1627
- labels: {},
1628
- },
1629
- {
1630
- did: 'did:plc:other',
1631
- labels: {},
1632
- },
1633
- ],
1634
- mutedWords: [],
1635
- hiddenPosts: [],
1636
- },
1637
- birthDate: new Date('2021-09-11T18:05:42.556Z'),
1638
- declaredAge: {
1639
- isOverAge13: false,
1640
- isOverAge16: false,
1641
- isOverAge18: false,
1642
- },
1643
- feedViewPrefs: {
1644
- home: {
1645
- hideReplies: true,
1646
- hideRepliesByUnfollowed: false,
1647
- hideRepliesByLikeCount: 10,
1648
- hideReposts: true,
1649
- hideQuotePosts: true,
1650
- },
1651
- },
1652
- threadViewPrefs: {
1653
- sort: 'newest',
1654
- },
1655
- interests: {
1656
- tags: [],
1657
- },
1658
- bskyAppState: {
1659
- activeProgressGuide: undefined,
1660
- queuedNudges: ['two'],
1661
- nuxs: [],
1662
- },
1663
- postInteractionSettings: {
1664
- threadgateAllowRules: undefined,
1665
- postgateEmbeddingRules: undefined,
1666
- },
1667
- verificationPrefs: {
1668
- hideBadges: false,
1669
- },
1670
- liveEventPreferences: {
1671
- hiddenFeedIds: [],
1672
- hideAllFeeds: false,
1673
- },
1674
- })
1675
-
1676
- await agent.setContentLabelPref('porn', 'ignore')
1677
- await expect(agent.getPreferences()).resolves.toStrictEqual({
1678
- savedFeeds: [
1679
- {
1680
- id: expect.any(String),
1681
- type: 'timeline',
1682
- value: 'following',
1683
- pinned: true,
1684
- },
1685
- ],
1686
- feeds: {
1687
- pinned: [],
1688
- saved: [],
1689
- },
1690
- moderationPrefs: {
1691
- adultContentEnabled: false,
1692
- labels: {
1693
- ...DEFAULT_LABEL_SETTINGS,
1694
- nsfw: 'ignore',
1695
- porn: 'ignore',
1696
- },
1697
- labelers: [
1698
- ...AtpAgent.appLabelers.map((did) => ({ did, labels: {} })),
1699
- {
1700
- did: 'did:plc:first-labeler',
1701
- labels: {},
1702
- },
1703
- {
1704
- did: 'did:plc:other',
1705
- labels: {},
1706
- },
1707
- ],
1708
- mutedWords: [],
1709
- hiddenPosts: [],
1710
- },
1711
- birthDate: new Date('2021-09-11T18:05:42.556Z'),
1712
- declaredAge: {
1713
- isOverAge13: false,
1714
- isOverAge16: false,
1715
- isOverAge18: false,
1716
- },
1717
- feedViewPrefs: {
1718
- home: {
1719
- hideReplies: true,
1720
- hideRepliesByUnfollowed: false,
1721
- hideRepliesByLikeCount: 10,
1722
- hideReposts: true,
1723
- hideQuotePosts: true,
1724
- },
1725
- },
1726
- threadViewPrefs: {
1727
- sort: 'newest',
1728
- },
1729
- interests: {
1730
- tags: [],
1731
- },
1732
- bskyAppState: {
1733
- activeProgressGuide: undefined,
1734
- queuedNudges: ['two'],
1735
- nuxs: [],
1736
- },
1737
- postInteractionSettings: {
1738
- threadgateAllowRules: undefined,
1739
- postgateEmbeddingRules: undefined,
1740
- },
1741
- verificationPrefs: {
1742
- hideBadges: false,
1743
- },
1744
- liveEventPreferences: {
1745
- hiddenFeedIds: [],
1746
- hideAllFeeds: false,
1747
- },
1748
- })
1749
-
1750
- await agent.removeLabeler('did:plc:other')
1751
- await expect(agent.getPreferences()).resolves.toStrictEqual({
1752
- savedFeeds: [
1753
- {
1754
- id: expect.any(String),
1755
- type: 'timeline',
1756
- value: 'following',
1757
- pinned: true,
1758
- },
1759
- ],
1760
- feeds: {
1761
- pinned: [],
1762
- saved: [],
1763
- },
1764
- moderationPrefs: {
1765
- adultContentEnabled: false,
1766
- labels: {
1767
- ...DEFAULT_LABEL_SETTINGS,
1768
- nsfw: 'ignore',
1769
- porn: 'ignore',
1770
- },
1771
- labelers: [
1772
- ...AtpAgent.appLabelers.map((did) => ({ did, labels: {} })),
1773
- {
1774
- did: 'did:plc:first-labeler',
1775
- labels: {},
1776
- },
1777
- ],
1778
- mutedWords: [],
1779
- hiddenPosts: [],
1780
- },
1781
- birthDate: new Date('2021-09-11T18:05:42.556Z'),
1782
- declaredAge: {
1783
- isOverAge13: false,
1784
- isOverAge16: false,
1785
- isOverAge18: false,
1786
- },
1787
- feedViewPrefs: {
1788
- home: {
1789
- hideReplies: true,
1790
- hideRepliesByUnfollowed: false,
1791
- hideRepliesByLikeCount: 10,
1792
- hideReposts: true,
1793
- hideQuotePosts: true,
1794
- },
1795
- },
1796
- threadViewPrefs: {
1797
- sort: 'newest',
1798
- },
1799
- interests: {
1800
- tags: [],
1801
- },
1802
- bskyAppState: {
1803
- activeProgressGuide: undefined,
1804
- queuedNudges: ['two'],
1805
- nuxs: [],
1806
- },
1807
- postInteractionSettings: {
1808
- threadgateAllowRules: undefined,
1809
- postgateEmbeddingRules: undefined,
1810
- },
1811
- verificationPrefs: {
1812
- hideBadges: false,
1813
- },
1814
- liveEventPreferences: {
1815
- hiddenFeedIds: [],
1816
- hideAllFeeds: false,
1817
- },
1818
- })
1819
-
1820
- await agent.addPinnedFeed('at://bob.com/app.bsky.feed.generator/fake')
1821
- await expect(agent.getPreferences()).resolves.toStrictEqual({
1822
- feeds: {
1823
- pinned: ['at://bob.com/app.bsky.feed.generator/fake'],
1824
- saved: ['at://bob.com/app.bsky.feed.generator/fake'],
1825
- },
1826
- savedFeeds: [
1827
- {
1828
- id: expect.any(String),
1829
- pinned: true,
1830
- type: 'timeline',
1831
- value: 'following',
1832
- },
1833
- ],
1834
- moderationPrefs: {
1835
- adultContentEnabled: false,
1836
- labels: {
1837
- ...DEFAULT_LABEL_SETTINGS,
1838
- nsfw: 'ignore',
1839
- porn: 'ignore',
1840
- },
1841
- labelers: [
1842
- ...AtpAgent.appLabelers.map((did) => ({ did, labels: {} })),
1843
- {
1844
- did: 'did:plc:first-labeler',
1845
- labels: {},
1846
- },
1847
- ],
1848
- mutedWords: [],
1849
- hiddenPosts: [],
1850
- },
1851
- birthDate: new Date('2021-09-11T18:05:42.556Z'),
1852
- declaredAge: {
1853
- isOverAge13: false,
1854
- isOverAge16: false,
1855
- isOverAge18: false,
1856
- },
1857
- feedViewPrefs: {
1858
- home: {
1859
- hideReplies: true,
1860
- hideRepliesByUnfollowed: false,
1861
- hideRepliesByLikeCount: 10,
1862
- hideReposts: true,
1863
- hideQuotePosts: true,
1864
- },
1865
- },
1866
- threadViewPrefs: {
1867
- sort: 'newest',
1868
- },
1869
- interests: {
1870
- tags: [],
1871
- },
1872
- bskyAppState: {
1873
- activeProgressGuide: undefined,
1874
- queuedNudges: ['two'],
1875
- nuxs: [],
1876
- },
1877
- postInteractionSettings: {
1878
- threadgateAllowRules: undefined,
1879
- postgateEmbeddingRules: undefined,
1880
- },
1881
- verificationPrefs: {
1882
- hideBadges: false,
1883
- },
1884
- liveEventPreferences: {
1885
- hiddenFeedIds: [],
1886
- hideAllFeeds: false,
1887
- },
1888
- })
1889
-
1890
- await agent.setPersonalDetails({ birthDate: '2023-09-11T18:05:42.556Z' })
1891
- await expect(agent.getPreferences()).resolves.toStrictEqual({
1892
- feeds: {
1893
- pinned: ['at://bob.com/app.bsky.feed.generator/fake'],
1894
- saved: ['at://bob.com/app.bsky.feed.generator/fake'],
1895
- },
1896
- savedFeeds: [
1897
- {
1898
- id: expect.any(String),
1899
- pinned: true,
1900
- type: 'timeline',
1901
- value: 'following',
1902
- },
1903
- ],
1904
- moderationPrefs: {
1905
- adultContentEnabled: false,
1906
- labels: {
1907
- ...DEFAULT_LABEL_SETTINGS,
1908
- nsfw: 'ignore',
1909
- porn: 'ignore',
1910
- },
1911
- labelers: [
1912
- ...AtpAgent.appLabelers.map((did) => ({ did, labels: {} })),
1913
- {
1914
- did: 'did:plc:first-labeler',
1915
- labels: {},
1916
- },
1917
- ],
1918
- mutedWords: [],
1919
- hiddenPosts: [],
1920
- },
1921
- birthDate: new Date('2023-09-11T18:05:42.556Z'),
1922
- declaredAge: {
1923
- isOverAge13: false,
1924
- isOverAge16: false,
1925
- isOverAge18: false,
1926
- },
1927
- feedViewPrefs: {
1928
- home: {
1929
- hideReplies: true,
1930
- hideRepliesByUnfollowed: false,
1931
- hideRepliesByLikeCount: 10,
1932
- hideReposts: true,
1933
- hideQuotePosts: true,
1934
- },
1935
- },
1936
- threadViewPrefs: {
1937
- sort: 'newest',
1938
- },
1939
- interests: {
1940
- tags: [],
1941
- },
1942
- bskyAppState: {
1943
- activeProgressGuide: undefined,
1944
- queuedNudges: ['two'],
1945
- nuxs: [],
1946
- },
1947
- postInteractionSettings: {
1948
- threadgateAllowRules: undefined,
1949
- postgateEmbeddingRules: undefined,
1950
- },
1951
- verificationPrefs: {
1952
- hideBadges: false,
1953
- },
1954
- liveEventPreferences: {
1955
- hiddenFeedIds: [],
1956
- hideAllFeeds: false,
1957
- },
1958
- })
1959
-
1960
- await agent.setFeedViewPrefs('home', {
1961
- hideReplies: false,
1962
- hideRepliesByUnfollowed: true,
1963
- hideRepliesByLikeCount: 0,
1964
- hideReposts: false,
1965
- hideQuotePosts: false,
1966
- })
1967
- await agent.setThreadViewPrefs({
1968
- sort: 'oldest',
1969
- })
1970
- await agent.setPersonalDetails({ birthDate: '2023-09-11T18:05:42.556Z' })
1971
- await agent.bskyAppQueueNudges('three')
1972
- await expect(agent.getPreferences()).resolves.toStrictEqual({
1973
- feeds: {
1974
- pinned: ['at://bob.com/app.bsky.feed.generator/fake'],
1975
- saved: ['at://bob.com/app.bsky.feed.generator/fake'],
1976
- },
1977
- savedFeeds: [
1978
- {
1979
- id: expect.any(String),
1980
- pinned: true,
1981
- type: 'timeline',
1982
- value: 'following',
1983
- },
1984
- ],
1985
- moderationPrefs: {
1986
- adultContentEnabled: false,
1987
- labels: {
1988
- ...DEFAULT_LABEL_SETTINGS,
1989
- nsfw: 'ignore',
1990
- porn: 'ignore',
1991
- },
1992
- labelers: [
1993
- ...AtpAgent.appLabelers.map((did) => ({ did, labels: {} })),
1994
- {
1995
- did: 'did:plc:first-labeler',
1996
- labels: {},
1997
- },
1998
- ],
1999
- mutedWords: [],
2000
- hiddenPosts: [],
2001
- },
2002
- birthDate: new Date('2023-09-11T18:05:42.556Z'),
2003
- declaredAge: {
2004
- isOverAge13: false,
2005
- isOverAge16: false,
2006
- isOverAge18: false,
2007
- },
2008
- feedViewPrefs: {
2009
- home: {
2010
- hideReplies: false,
2011
- hideRepliesByUnfollowed: true,
2012
- hideRepliesByLikeCount: 0,
2013
- hideReposts: false,
2014
- hideQuotePosts: false,
2015
- },
2016
- },
2017
- threadViewPrefs: {
2018
- sort: 'oldest',
2019
- },
2020
- interests: {
2021
- tags: [],
2022
- },
2023
- bskyAppState: {
2024
- activeProgressGuide: undefined,
2025
- queuedNudges: ['two', 'three'],
2026
- nuxs: [],
2027
- },
2028
- postInteractionSettings: {
2029
- threadgateAllowRules: undefined,
2030
- postgateEmbeddingRules: undefined,
2031
- },
2032
- verificationPrefs: {
2033
- hideBadges: false,
2034
- },
2035
- liveEventPreferences: {
2036
- hiddenFeedIds: [],
2037
- hideAllFeeds: false,
2038
- },
2039
- })
2040
-
2041
- const res = await agent.app.bsky.actor.getPreferences()
2042
- expect(res.data.preferences.sort(byType)).toStrictEqual(
2043
- [
2044
- {
2045
- $type: 'app.bsky.actor.defs#bskyAppStatePref',
2046
- queuedNudges: ['two', 'three'],
2047
- },
2048
- {
2049
- $type: 'app.bsky.actor.defs#adultContentPref',
2050
- enabled: false,
2051
- },
2052
- {
2053
- $type: 'app.bsky.actor.defs#contentLabelPref',
2054
- label: 'porn',
2055
- visibility: 'ignore',
2056
- },
2057
- {
2058
- $type: 'app.bsky.actor.defs#contentLabelPref',
2059
- label: 'nsfw',
2060
- visibility: 'ignore',
2061
- },
2062
- {
2063
- $type: 'app.bsky.actor.defs#labelersPref',
2064
- labelers: [
2065
- {
2066
- did: 'did:plc:first-labeler',
2067
- },
2068
- ],
2069
- },
2070
- {
2071
- $type: 'app.bsky.actor.defs#savedFeedsPref',
2072
- pinned: ['at://bob.com/app.bsky.feed.generator/fake'],
2073
- saved: ['at://bob.com/app.bsky.feed.generator/fake'],
2074
- },
2075
- {
2076
- $type: 'app.bsky.actor.defs#savedFeedsPrefV2',
2077
- items: [
2078
- {
2079
- id: expect.any(String),
2080
- pinned: true,
2081
- type: 'timeline',
2082
- value: 'following',
2083
- },
2084
- ],
2085
- },
2086
- {
2087
- $type: 'app.bsky.actor.defs#personalDetailsPref',
2088
- birthDate: '2023-09-11T18:05:42.556Z',
2089
- },
2090
- {
2091
- $type: 'app.bsky.actor.defs#declaredAgePref',
2092
- isOverAge13: false,
2093
- isOverAge16: false,
2094
- isOverAge18: false,
2095
- },
2096
- {
2097
- $type: 'app.bsky.actor.defs#feedViewPref',
2098
- feed: 'home',
2099
- hideReplies: false,
2100
- hideRepliesByUnfollowed: true,
2101
- hideRepliesByLikeCount: 0,
2102
- hideReposts: false,
2103
- hideQuotePosts: false,
2104
- },
2105
- {
2106
- $type: 'app.bsky.actor.defs#threadViewPref',
2107
- sort: 'oldest',
2108
- },
2109
- ].sort(byType),
2110
- )
2111
- })
2112
-
2113
- describe('muted words', () => {
2114
- let agent: AtpAgent
2115
-
2116
- beforeAll(async () => {
2117
- agent = new AtpAgent({ service: network.pds.url })
2118
- await agent.createAccount({
2119
- handle: 'user7.test',
2120
- email: 'user7@test.com',
2121
- password: 'password',
2122
- })
2123
- })
2124
-
2125
- afterEach(async () => {
2126
- const { moderationPrefs } = await agent.getPreferences()
2127
- await agent.removeMutedWords(moderationPrefs.mutedWords)
2128
- })
2129
-
2130
- describe('addMutedWord', () => {
2131
- it('inserts', async () => {
2132
- const expiresAt = new Date(Date.now() + 6e3).toISOString()
2133
- await agent.addMutedWord({
2134
- value: 'word',
2135
- targets: ['content'],
2136
- actorTarget: 'all',
2137
- expiresAt,
2138
- })
2139
-
2140
- const { moderationPrefs } = await agent.getPreferences()
2141
- const word = moderationPrefs.mutedWords.find(
2142
- (m) => m.value === 'word',
2143
- )
2144
-
2145
- expect(word!.id).toBeTruthy()
2146
- expect(word!.targets).toEqual(['content'])
2147
- expect(word!.actorTarget).toEqual('all')
2148
- expect(word!.expiresAt).toEqual(expiresAt)
2149
- })
2150
-
2151
- it('single-hash #, no insert', async () => {
2152
- await agent.addMutedWord({
2153
- value: '#',
2154
- targets: [],
2155
- actorTarget: 'all',
2156
- })
2157
- const { moderationPrefs } = await agent.getPreferences()
2158
-
2159
- // sanitized to empty string, not inserted
2160
- expect(moderationPrefs.mutedWords.length).toEqual(0)
2161
- })
2162
-
2163
- it('multi-hash ##, inserts #', async () => {
2164
- await agent.addMutedWord({
2165
- value: '##',
2166
- targets: [],
2167
- actorTarget: 'all',
2168
- })
2169
- const { moderationPrefs } = await agent.getPreferences()
2170
- expect(
2171
- moderationPrefs.mutedWords.find((m) => m.value === '#'),
2172
- ).toBeTruthy()
2173
- })
2174
-
2175
- it('multi-hash ##hashtag, inserts #hashtag', async () => {
2176
- await agent.addMutedWord({
2177
- value: '##hashtag',
2178
- targets: [],
2179
- actorTarget: 'all',
2180
- })
2181
- const { moderationPrefs } = await agent.getPreferences()
2182
- expect(
2183
- moderationPrefs.mutedWords.find((w) => w.value === '#hashtag'),
2184
- ).toBeTruthy()
2185
- })
2186
-
2187
- it('hash emoji #️⃣, inserts #️⃣', async () => {
2188
- await agent.addMutedWord({
2189
- value: '#️⃣',
2190
- targets: [],
2191
- actorTarget: 'all',
2192
- })
2193
- const { moderationPrefs } = await agent.getPreferences()
2194
- expect(
2195
- moderationPrefs.mutedWords.find((m) => m.value === '#️⃣'),
2196
- ).toBeTruthy()
2197
- })
2198
-
2199
- it('hash emoji w/leading hash ##️⃣, inserts #️⃣', async () => {
2200
- await agent.addMutedWord({
2201
- value: '##️⃣',
2202
- targets: [],
2203
- actorTarget: 'all',
2204
- })
2205
- const { moderationPrefs } = await agent.getPreferences()
2206
- expect(
2207
- moderationPrefs.mutedWords.find((m) => m.value === '#️⃣'),
2208
- ).toBeTruthy()
2209
- })
2210
-
2211
- it('hash emoji with double leading hash ###️⃣, inserts ##️⃣', async () => {
2212
- await agent.addMutedWord({
2213
- value: '###️⃣',
2214
- targets: [],
2215
- actorTarget: 'all',
2216
- })
2217
- const { moderationPrefs } = await agent.getPreferences()
2218
- expect(
2219
- moderationPrefs.mutedWords.find((m) => m.value === '##️⃣'),
2220
- ).toBeTruthy()
2221
- })
2222
-
2223
- it(`includes apostrophes e.g. Bluesky's`, async () => {
2224
- await agent.addMutedWord({
2225
- value: `Bluesky's`,
2226
- targets: [],
2227
- actorTarget: 'all',
2228
- })
2229
- const { mutedWords } = (await agent.getPreferences()).moderationPrefs
2230
-
2231
- expect(mutedWords.find((m) => m.value === `Bluesky's`)).toBeTruthy()
2232
- })
2233
-
2234
- describe(`invalid characters`, () => {
2235
- it('#<zws>, no insert', async () => {
2236
- await agent.addMutedWord({
2237
- value: '#​',
2238
- targets: [],
2239
- actorTarget: 'all',
2240
- })
2241
- const { moderationPrefs } = await agent.getPreferences()
2242
- expect(moderationPrefs.mutedWords.length).toEqual(0)
2243
- })
2244
-
2245
- it('#<zws>ab, inserts ab', async () => {
2246
- await agent.addMutedWord({
2247
- value: '#​ab',
2248
- targets: [],
2249
- actorTarget: 'all',
2250
- })
2251
- const { moderationPrefs } = await agent.getPreferences()
2252
- expect(moderationPrefs.mutedWords.length).toEqual(1)
2253
- })
2254
-
2255
- it('phrase with newline, inserts phrase without newline', async () => {
2256
- await agent.addMutedWord({
2257
- value: 'test value\n with newline',
2258
- targets: [],
2259
- actorTarget: 'all',
2260
- })
2261
- const { moderationPrefs } = await agent.getPreferences()
2262
- expect(
2263
- moderationPrefs.mutedWords.find(
2264
- (m) => m.value === 'test value with newline',
2265
- ),
2266
- ).toBeTruthy()
2267
- })
2268
-
2269
- it('phrase with newlines, inserts phrase without newlines', async () => {
2270
- await agent.addMutedWord({
2271
- value: 'test value\n\r with newline',
2272
- targets: [],
2273
- actorTarget: 'all',
2274
- })
2275
- const { moderationPrefs } = await agent.getPreferences()
2276
- expect(
2277
- moderationPrefs.mutedWords.find(
2278
- (m) => m.value === 'test value with newline',
2279
- ),
2280
- ).toBeTruthy()
2281
- })
2282
-
2283
- it('empty space, no insert', async () => {
2284
- await agent.addMutedWord({
2285
- value: ' ',
2286
- targets: [],
2287
- actorTarget: 'all',
2288
- })
2289
- const { moderationPrefs } = await agent.getPreferences()
2290
- expect(moderationPrefs.mutedWords.length).toEqual(0)
2291
- })
2292
-
2293
- it(`' trim ', inserts 'trim'`, async () => {
2294
- await agent.addMutedWord({
2295
- value: ' trim ',
2296
- targets: [],
2297
- actorTarget: 'all',
2298
- })
2299
- const { moderationPrefs } = await agent.getPreferences()
2300
- expect(
2301
- moderationPrefs.mutedWords.find((m) => m.value === 'trim'),
2302
- ).toBeTruthy()
2303
- })
2304
- })
2305
- })
2306
-
2307
- describe('addMutedWords', () => {
2308
- it('inserts happen sequentially, no clobbering', async () => {
2309
- await agent.addMutedWords([
2310
- { value: 'a', targets: ['content'], actorTarget: 'all' },
2311
- { value: 'b', targets: ['content'], actorTarget: 'all' },
2312
- { value: 'c', targets: ['content'], actorTarget: 'all' },
2313
- ])
2314
-
2315
- const { moderationPrefs } = await agent.getPreferences()
2316
-
2317
- expect(moderationPrefs.mutedWords.length).toEqual(3)
2318
- })
2319
- })
2320
-
2321
- describe('upsertMutedWords (deprecated)', () => {
2322
- it('no longer upserts, calls addMutedWords', async () => {
2323
- await agent.upsertMutedWords([
2324
- { value: 'both', targets: ['content'], actorTarget: 'all' },
2325
- ])
2326
- await agent.upsertMutedWords([
2327
- { value: 'both', targets: ['tag'], actorTarget: 'all' },
2328
- ])
2329
-
2330
- const { moderationPrefs } = await agent.getPreferences()
2331
-
2332
- expect(moderationPrefs.mutedWords.length).toEqual(2)
2333
- })
2334
- })
2335
-
2336
- describe('updateMutedWord', () => {
2337
- it(`word doesn't exist, no update or insert`, async () => {
2338
- await agent.updateMutedWord({
2339
- value: 'word',
2340
- targets: ['tag', 'content'],
2341
- actorTarget: 'all',
2342
- })
2343
- const { moderationPrefs } = await agent.getPreferences()
2344
- expect(moderationPrefs.mutedWords.length).toEqual(0)
2345
- })
2346
-
2347
- it('updates and sanitizes new value', async () => {
2348
- await agent.addMutedWord({
2349
- value: 'value',
2350
- targets: ['content'],
2351
- actorTarget: 'all',
2352
- })
2353
-
2354
- const a = await agent.getPreferences()
2355
- const word = a.moderationPrefs.mutedWords.find(
2356
- (m) => m.value === 'value',
2357
- )
2358
-
2359
- await agent.updateMutedWord({
2360
- ...word!,
2361
- value: '#new value',
2362
- })
2363
-
2364
- const b = await agent.getPreferences()
2365
- const updatedWord = b.moderationPrefs.mutedWords.find(
2366
- (m) => m.id === word!.id,
2367
- )
2368
-
2369
- expect(updatedWord!.value).toEqual('new value')
2370
- expect(updatedWord).toHaveProperty('targets', ['content'])
2371
- })
2372
-
2373
- it('updates targets', async () => {
2374
- await agent.addMutedWord({
2375
- value: 'word',
2376
- targets: ['tag'],
2377
- actorTarget: 'all',
2378
- })
2379
-
2380
- const a = await agent.getPreferences()
2381
- const word = a.moderationPrefs.mutedWords.find(
2382
- (m) => m.value === 'word',
2383
- )
2384
-
2385
- await agent.updateMutedWord({
2386
- ...word!,
2387
- targets: ['content'],
2388
- })
2389
-
2390
- const b = await agent.getPreferences()
2391
-
2392
- expect(
2393
- b.moderationPrefs.mutedWords.find((m) => m.id === word!.id),
2394
- ).toHaveProperty('targets', ['content'])
2395
- })
2396
-
2397
- it('updates actorTarget', async () => {
2398
- await agent.addMutedWord({
2399
- value: 'value',
2400
- targets: ['content'],
2401
- actorTarget: 'all',
2402
- })
2403
-
2404
- const a = await agent.getPreferences()
2405
- const word = a.moderationPrefs.mutedWords.find(
2406
- (m) => m.value === 'value',
2407
- )
2408
-
2409
- await agent.updateMutedWord({
2410
- ...word!,
2411
- actorTarget: 'exclude-following',
2412
- })
2413
-
2414
- const b = await agent.getPreferences()
2415
-
2416
- expect(
2417
- b.moderationPrefs.mutedWords.find((m) => m.id === word!.id),
2418
- ).toHaveProperty('actorTarget', 'exclude-following')
2419
- })
2420
-
2421
- it('updates expiresAt', async () => {
2422
- const expiresAt = new Date(Date.now() + 6e3).toISOString()
2423
- const expiresAt2 = new Date(Date.now() + 10e3).toISOString()
2424
- await agent.addMutedWord({
2425
- value: 'value',
2426
- targets: ['content'],
2427
- expiresAt,
2428
- actorTarget: 'all',
2429
- })
2430
-
2431
- const a = await agent.getPreferences()
2432
- const word = a.moderationPrefs.mutedWords.find(
2433
- (m) => m.value === 'value',
2434
- )
2435
-
2436
- await agent.updateMutedWord({
2437
- ...word!,
2438
- expiresAt: expiresAt2,
2439
- })
2440
-
2441
- const b = await agent.getPreferences()
2442
-
2443
- expect(
2444
- b.moderationPrefs.mutedWords.find((m) => m.id === word!.id),
2445
- ).toHaveProperty('expiresAt', expiresAt2)
2446
- })
2447
-
2448
- it(`doesn't update if value is sanitized to be falsy`, async () => {
2449
- await agent.addMutedWord({
2450
- value: 'rug',
2451
- targets: ['content'],
2452
- actorTarget: 'all',
2453
- })
2454
-
2455
- const a = await agent.getPreferences()
2456
- const word = a.moderationPrefs.mutedWords.find(
2457
- (m) => m.value === 'rug',
2458
- )
2459
-
2460
- await agent.updateMutedWord({
2461
- ...word!,
2462
- value: '',
2463
- })
2464
-
2465
- const b = await agent.getPreferences()
2466
-
2467
- expect(
2468
- b.moderationPrefs.mutedWords.find((m) => m.id === word!.id),
2469
- ).toHaveProperty('value', 'rug')
2470
- })
2471
- })
2472
-
2473
- describe('removeMutedWord', () => {
2474
- it('removes word', async () => {
2475
- await agent.addMutedWord({
2476
- value: 'word',
2477
- targets: ['tag'],
2478
- actorTarget: 'all',
2479
- })
2480
- const a = await agent.getPreferences()
2481
- const word = a.moderationPrefs.mutedWords.find(
2482
- (m) => m.value === 'word',
2483
- )
2484
-
2485
- await agent.removeMutedWord(word!)
2486
-
2487
- const b = await agent.getPreferences()
2488
-
2489
- expect(
2490
- b.moderationPrefs.mutedWords.find((m) => m.id === word!.id),
2491
- ).toBeFalsy()
2492
- })
2493
-
2494
- it(`word doesn't exist, no action`, async () => {
2495
- await agent.addMutedWord({
2496
- value: 'word',
2497
- targets: ['tag'],
2498
- actorTarget: 'all',
2499
- })
2500
- const a = await agent.getPreferences()
2501
- const word = a.moderationPrefs.mutedWords.find(
2502
- (m) => m.value === 'word',
2503
- )
2504
-
2505
- await agent.removeMutedWord({
2506
- value: 'another',
2507
- targets: [],
2508
- actorTarget: 'all',
2509
- })
2510
-
2511
- const b = await agent.getPreferences()
2512
-
2513
- expect(
2514
- b.moderationPrefs.mutedWords.find((m) => m.id === word!.id),
2515
- ).toBeTruthy()
2516
- })
2517
- })
2518
-
2519
- describe('removeMutedWords', () => {
2520
- it(`removes sequentially, no clobbering`, async () => {
2521
- await agent.addMutedWords([
2522
- { value: 'a', targets: ['content'], actorTarget: 'all ' },
2523
- { value: 'b', targets: ['content'], actorTarget: 'all ' },
2524
- { value: 'c', targets: ['content'], actorTarget: 'all ' },
2525
- ])
2526
-
2527
- const a = await agent.getPreferences()
2528
- await agent.removeMutedWords(a.moderationPrefs.mutedWords)
2529
- const b = await agent.getPreferences()
2530
-
2531
- expect(b.moderationPrefs.mutedWords.length).toEqual(0)
2532
- })
2533
- })
2534
- })
2535
-
2536
- describe('legacy muted words', () => {
2537
- let agent: AtpAgent
2538
-
2539
- async function updatePreferences(
2540
- agent: AtpAgent,
2541
- cb: (
2542
- prefs: AppBskyActorDefs.Preferences,
2543
- ) => AppBskyActorDefs.Preferences | false,
2544
- ) {
2545
- const res = await agent.app.bsky.actor.getPreferences({})
2546
- const newPrefs = cb(res.data.preferences)
2547
- if (newPrefs === false) {
2548
- return
2549
- }
2550
- await agent.app.bsky.actor.putPreferences({
2551
- preferences: newPrefs,
2552
- })
2553
- }
2554
-
2555
- async function addLegacyMutedWord(mutedWord: AppBskyActorDefs.MutedWord) {
2556
- await updatePreferences(agent, (prefs) => {
2557
- const mutedWordsPref = prefs.findLast(
2558
- asPredicate(AppBskyActorDefs.validateMutedWordsPref),
2559
- ) || {
2560
- $type: 'app.bsky.actor.defs#mutedWordsPref',
2561
- items: [],
2562
- }
2563
-
2564
- mutedWordsPref.items.push({
2565
- value: mutedWord.value,
2566
- targets: mutedWord.targets,
2567
- actorTarget: 'all',
2568
- })
2569
-
2570
- return prefs
2571
- .filter((p) => !AppBskyActorDefs.isMutedWordsPref(p))
2572
- .concat([mutedWordsPref])
2573
- })
2574
- }
2575
-
2576
- beforeAll(async () => {
2577
- agent = new AtpAgent({ service: network.pds.url })
2578
- await agent.createAccount({
2579
- handle: 'user7-1.test',
2580
- email: 'user7-1@test.com',
2581
- password: 'password',
2582
- })
2583
- })
2584
-
2585
- afterEach(async () => {
2586
- const { moderationPrefs } = await agent.getPreferences()
2587
- await agent.removeMutedWords(moderationPrefs.mutedWords)
2588
- })
2589
-
2590
- describe(`upsertMutedWords (and addMutedWord)`, () => {
2591
- it(`adds new word, migrates old words`, async () => {
2592
- await addLegacyMutedWord({
2593
- value: 'word',
2594
- targets: ['content'],
2595
- actorTarget: 'all',
2596
- })
2597
-
2598
- {
2599
- const { moderationPrefs } = await agent.getPreferences()
2600
- const word = moderationPrefs.mutedWords.find(
2601
- (w) => w.value === 'word',
2602
- )
2603
- expect(word).toBeTruthy()
2604
- expect(word!.id).toBeFalsy()
2605
- }
2606
-
2607
- await agent.upsertMutedWords([
2608
- { value: 'word2', targets: ['tag'], actorTarget: 'all' },
2609
- ])
2610
-
2611
- {
2612
- const { moderationPrefs } = await agent.getPreferences()
2613
- const word = moderationPrefs.mutedWords.find(
2614
- (w) => w.value === 'word',
2615
- )
2616
- const word2 = moderationPrefs.mutedWords.find(
2617
- (w) => w.value === 'word2',
2618
- )
2619
-
2620
- expect(word!.id).toBeTruthy()
2621
- expect(word2!.id).toBeTruthy()
2622
- }
2623
- })
2624
- })
2625
-
2626
- describe(`updateMutedWord`, () => {
2627
- it(`updates legacy word, migrates old words`, async () => {
2628
- await addLegacyMutedWord({
2629
- value: 'word',
2630
- targets: ['content'],
2631
- actorTarget: 'all',
2632
- })
2633
- await addLegacyMutedWord({
2634
- value: 'word2',
2635
- targets: ['tag'],
2636
- actorTarget: 'all',
2637
- })
2638
-
2639
- await agent.updateMutedWord({
2640
- value: 'word',
2641
- targets: ['tag'],
2642
- actorTarget: 'all',
2643
- })
2644
-
2645
- {
2646
- const { moderationPrefs } = await agent.getPreferences()
2647
- const word = moderationPrefs.mutedWords.find(
2648
- (w) => w.value === 'word',
2649
- )
2650
- const word2 = moderationPrefs.mutedWords.find(
2651
- (w) => w.value === 'word2',
2652
- )
2653
-
2654
- expect(moderationPrefs.mutedWords.length).toEqual(2)
2655
- expect(word!.id).toBeTruthy()
2656
- expect(word!.targets).toEqual(['tag'])
2657
- expect(word2!.id).toBeTruthy()
2658
- }
2659
- })
2660
- })
2661
-
2662
- describe(`removeMutedWord`, () => {
2663
- it(`removes legacy word, migrates old words`, async () => {
2664
- await addLegacyMutedWord({
2665
- value: 'word',
2666
- targets: ['content'],
2667
- actorTarget: 'all',
2668
- })
2669
- await addLegacyMutedWord({
2670
- value: 'word2',
2671
- targets: ['tag'],
2672
- actorTarget: 'all',
2673
- })
2674
-
2675
- await agent.removeMutedWord({
2676
- value: 'word',
2677
- targets: ['tag'],
2678
- actorTarget: 'all',
2679
- })
2680
-
2681
- {
2682
- const { moderationPrefs } = await agent.getPreferences()
2683
- const word = moderationPrefs.mutedWords.find(
2684
- (w) => w.value === 'word',
2685
- )
2686
- const word2 = moderationPrefs.mutedWords.find(
2687
- (w) => w.value === 'word2',
2688
- )
2689
-
2690
- expect(moderationPrefs.mutedWords.length).toEqual(1)
2691
- expect(word).toBeFalsy()
2692
- expect(word2!.id).toBeTruthy()
2693
- }
2694
- })
2695
- })
2696
- })
2697
-
2698
- describe('hidden posts', () => {
2699
- let agent: AtpAgent
2700
- const postUri = 'at://did:plc:fake/app.bsky.feed.post/fake'
2701
-
2702
- beforeAll(async () => {
2703
- agent = new AtpAgent({ service: network.pds.url })
2704
- await agent.createAccount({
2705
- handle: 'user8.test',
2706
- email: 'user8@test.com',
2707
- password: 'password',
2708
- })
2709
- })
2710
-
2711
- it('hidePost', async () => {
2712
- await agent.hidePost(postUri)
2713
- await agent.hidePost(postUri) // double, should dedupe
2714
- await expect(agent.getPreferences()).resolves.toHaveProperty(
2715
- 'moderationPrefs.hiddenPosts',
2716
- [postUri],
2717
- )
2718
- })
2719
-
2720
- it('unhidePost', async () => {
2721
- await agent.unhidePost(postUri)
2722
- await expect(agent.getPreferences()).resolves.toHaveProperty(
2723
- 'moderationPrefs.hiddenPosts',
2724
- [],
2725
- )
2726
- // no issues calling a second time
2727
- await agent.unhidePost(postUri)
2728
- await expect(agent.getPreferences()).resolves.toHaveProperty(
2729
- 'moderationPrefs.hiddenPosts',
2730
- [],
2731
- )
2732
- })
2733
- })
2734
-
2735
- describe(`saved feeds v2`, () => {
2736
- let agent: AtpAgent
2737
- let i = 0
2738
- const feedUri = () => `at://bob.com/app.bsky.feed.generator/${i++}`
2739
- const listUri = () => `at://bob.com/app.bsky.graph.list/${i++}`
2740
-
2741
- beforeAll(async () => {
2742
- agent = new AtpAgent({ service: network.pds.url })
2743
- await agent.createAccount({
2744
- handle: 'user9.test',
2745
- email: 'user9@test.com',
2746
- password: 'password',
2747
- })
2748
- })
2749
-
2750
- beforeEach(async () => {
2751
- await agent.app.bsky.actor.putPreferences({
2752
- preferences: [],
2753
- })
2754
- })
2755
-
2756
- describe(`addSavedFeeds`, () => {
2757
- it('works', async () => {
2758
- const feed = {
2759
- type: 'feed',
2760
- value: feedUri(),
2761
- pinned: false,
2762
- }
2763
- await agent.addSavedFeeds([feed])
2764
- const prefs = await agent.getPreferences()
2765
- expect(prefs.savedFeeds).toStrictEqual([
2766
- {
2767
- ...feed,
2768
- id: expect.any(String),
2769
- },
2770
- ])
2771
- })
2772
-
2773
- it('throws if feed is specified and list provided', async () => {
2774
- const list = listUri()
2775
- await expect(() =>
2776
- agent.addSavedFeeds([
2777
- {
2778
- type: 'feed',
2779
- value: list,
2780
- pinned: true,
2781
- },
2782
- ]),
2783
- ).rejects.toThrow()
2784
- })
2785
-
2786
- it('throws if list is specified and feed provided', async () => {
2787
- const feed = feedUri()
2788
- await expect(() =>
2789
- agent.addSavedFeeds([
2790
- {
2791
- type: 'list',
2792
- value: feed,
2793
- pinned: true,
2794
- },
2795
- ]),
2796
- ).rejects.toThrow()
2797
- })
2798
-
2799
- it(`timeline`, async () => {
2800
- const feeds = await agent.addSavedFeeds([
2801
- {
2802
- type: 'timeline',
2803
- value: 'following',
2804
- pinned: true,
2805
- },
2806
- ])
2807
- const prefs = await agent.getPreferences()
2808
- expect(
2809
- prefs.savedFeeds.filter((f) => f.type === 'timeline'),
2810
- ).toStrictEqual(feeds)
2811
- })
2812
-
2813
- it(`allows duplicates`, async () => {
2814
- const feed = {
2815
- type: 'feed',
2816
- value: feedUri(),
2817
- pinned: false,
2818
- }
2819
- await agent.addSavedFeeds([feed])
2820
- await agent.addSavedFeeds([feed])
2821
- const prefs = await agent.getPreferences()
2822
- expect(prefs.savedFeeds).toStrictEqual([
2823
- {
2824
- ...feed,
2825
- id: expect.any(String),
2826
- },
2827
- {
2828
- ...feed,
2829
- id: expect.any(String),
2830
- },
2831
- ])
2832
- })
2833
-
2834
- it(`adds multiple`, async () => {
2835
- const a = {
2836
- type: 'feed',
2837
- value: feedUri(),
2838
- pinned: true,
2839
- }
2840
- const b = {
2841
- type: 'feed',
2842
- value: feedUri(),
2843
- pinned: false,
2844
- }
2845
- await agent.addSavedFeeds([a, b])
2846
- const prefs = await agent.getPreferences()
2847
- expect(prefs.savedFeeds).toStrictEqual([
2848
- {
2849
- ...a,
2850
- id: expect.any(String),
2851
- },
2852
- {
2853
- ...b,
2854
- id: expect.any(String),
2855
- },
2856
- ])
2857
- })
2858
-
2859
- it(`appends multiple`, async () => {
2860
- const a = {
2861
- type: 'feed',
2862
- value: feedUri(),
2863
- pinned: true,
2864
- }
2865
- const b = {
2866
- type: 'feed',
2867
- value: feedUri(),
2868
- pinned: false,
2869
- }
2870
- const c = {
2871
- type: 'feed',
2872
- value: feedUri(),
2873
- pinned: true,
2874
- }
2875
- const d = {
2876
- type: 'feed',
2877
- value: feedUri(),
2878
- pinned: false,
2879
- }
2880
- await agent.addSavedFeeds([a, b])
2881
- await agent.addSavedFeeds([c, d])
2882
- const prefs = await agent.getPreferences()
2883
- expect(prefs.savedFeeds).toStrictEqual([
2884
- {
2885
- ...a,
2886
- id: expect.any(String),
2887
- },
2888
- {
2889
- ...c,
2890
- id: expect.any(String),
2891
- },
2892
- {
2893
- ...b,
2894
- id: expect.any(String),
2895
- },
2896
- {
2897
- ...d,
2898
- id: expect.any(String),
2899
- },
2900
- ])
2901
- })
2902
- })
2903
-
2904
- describe(`removeSavedFeeds`, () => {
2905
- it('works', async () => {
2906
- const feed = {
2907
- type: 'feed',
2908
- value: feedUri(),
2909
- pinned: true,
2910
- }
2911
- const savedFeeds = await agent.addSavedFeeds([feed])
2912
- await agent.removeSavedFeeds([savedFeeds[0].id])
2913
- const prefs = await agent.getPreferences()
2914
- expect(prefs.savedFeeds).toStrictEqual([])
2915
- })
2916
- })
2917
-
2918
- describe(`overwriteSavedFeeds`, () => {
2919
- it(`dedupes by id, takes last, preserves order based on last found`, async () => {
2920
- const a = {
2921
- id: TID.nextStr(),
2922
- type: 'feed',
2923
- value: feedUri(),
2924
- pinned: true,
2925
- }
2926
- const b = {
2927
- id: TID.nextStr(),
2928
- type: 'feed',
2929
- value: feedUri(),
2930
- pinned: true,
2931
- }
2932
- await agent.overwriteSavedFeeds([a, b, a])
2933
- const prefs = await agent.getPreferences()
2934
- expect(prefs.savedFeeds).toStrictEqual([b, a])
2935
- })
2936
-
2937
- it(`preserves order`, async () => {
2938
- const a = feedUri()
2939
- const b = feedUri()
2940
- const c = feedUri()
2941
- const d = feedUri()
2942
-
2943
- await agent.overwriteSavedFeeds([
2944
- {
2945
- id: TID.nextStr(),
2946
- type: 'timeline',
2947
- value: a,
2948
- pinned: true,
2949
- },
2950
- {
2951
- id: TID.nextStr(),
2952
- type: 'feed',
2953
- value: b,
2954
- pinned: false,
2955
- },
2956
- {
2957
- id: TID.nextStr(),
2958
- type: 'feed',
2959
- value: c,
2960
- pinned: true,
2961
- },
2962
- {
2963
- id: TID.nextStr(),
2964
- type: 'feed',
2965
- value: d,
2966
- pinned: false,
2967
- },
2968
- ])
2969
-
2970
- const { savedFeeds } = await agent.getPreferences()
2971
- expect(savedFeeds.filter((f) => f.pinned)).toStrictEqual([
2972
- {
2973
- id: expect.any(String),
2974
- type: 'timeline',
2975
- value: a,
2976
- pinned: true,
2977
- },
2978
- {
2979
- id: expect.any(String),
2980
- type: 'feed',
2981
- value: c,
2982
- pinned: true,
2983
- },
2984
- ])
2985
- expect(savedFeeds.filter((f) => !f.pinned)).toEqual([
2986
- {
2987
- id: expect.any(String),
2988
- type: 'feed',
2989
- value: b,
2990
- pinned: false,
2991
- },
2992
- {
2993
- id: expect.any(String),
2994
- type: 'feed',
2995
- value: d,
2996
- pinned: false,
2997
- },
2998
- ])
2999
- })
3000
- })
3001
-
3002
- describe(`updateSavedFeeds`, () => {
3003
- it(`updates affect order, saved last, new pins last`, async () => {
3004
- const a = {
3005
- id: TID.nextStr(),
3006
- type: 'feed',
3007
- value: feedUri(),
3008
- pinned: true,
3009
- }
3010
- const b = {
3011
- id: TID.nextStr(),
3012
- type: 'feed',
3013
- value: feedUri(),
3014
- pinned: true,
3015
- }
3016
- const c = {
3017
- id: TID.nextStr(),
3018
- type: 'feed',
3019
- value: feedUri(),
3020
- pinned: true,
3021
- }
3022
-
3023
- await agent.overwriteSavedFeeds([a, b, c])
3024
- await agent.updateSavedFeeds([
3025
- {
3026
- ...b,
3027
- pinned: false,
3028
- },
3029
- ])
3030
-
3031
- const prefs1 = await agent.getPreferences()
3032
- expect(prefs1.savedFeeds).toStrictEqual([
3033
- a,
3034
- c,
3035
- {
3036
- ...b,
3037
- pinned: false,
3038
- },
3039
- ])
3040
-
3041
- await agent.updateSavedFeeds([
3042
- {
3043
- ...b,
3044
- pinned: true,
3045
- },
3046
- ])
3047
-
3048
- const prefs2 = await agent.getPreferences()
3049
- expect(prefs2.savedFeeds).toStrictEqual([a, c, b])
3050
- })
3051
-
3052
- it(`cannot override original id`, async () => {
3053
- const a = {
3054
- id: TID.nextStr(),
3055
- type: 'feed',
3056
- value: feedUri(),
3057
- pinned: true,
3058
- }
3059
- await agent.overwriteSavedFeeds([a])
3060
- await agent.updateSavedFeeds([
3061
- {
3062
- ...a,
3063
- pinned: false,
3064
- id: TID.nextStr(),
3065
- },
3066
- ])
3067
- const prefs = await agent.getPreferences()
3068
- expect(prefs.savedFeeds).toStrictEqual([a])
3069
- })
3070
-
3071
- it(`updates multiple`, async () => {
3072
- const a = {
3073
- id: TID.nextStr(),
3074
- type: 'feed',
3075
- value: feedUri(),
3076
- pinned: false,
3077
- }
3078
- const b = {
3079
- id: TID.nextStr(),
3080
- type: 'feed',
3081
- value: feedUri(),
3082
- pinned: false,
3083
- }
3084
- const c = {
3085
- id: TID.nextStr(),
3086
- type: 'feed',
3087
- value: feedUri(),
3088
- pinned: false,
3089
- }
3090
-
3091
- await agent.overwriteSavedFeeds([a, b, c])
3092
- await agent.updateSavedFeeds([
3093
- {
3094
- ...b,
3095
- pinned: true,
3096
- },
3097
- {
3098
- ...c,
3099
- pinned: true,
3100
- },
3101
- ])
3102
-
3103
- const prefs1 = await agent.getPreferences()
3104
- expect(prefs1.savedFeeds).toStrictEqual([
3105
- {
3106
- ...b,
3107
- pinned: true,
3108
- },
3109
- {
3110
- ...c,
3111
- pinned: true,
3112
- },
3113
- a,
3114
- ])
3115
- })
3116
- })
3117
-
3118
- describe(`utils`, () => {
3119
- describe(`savedFeedsToUriArrays`, () => {
3120
- const { saved, pinned } = savedFeedsToUriArrays([
3121
- {
3122
- id: '',
3123
- type: 'feed',
3124
- value: 'a',
3125
- pinned: true,
3126
- },
3127
- {
3128
- id: '',
3129
- type: 'feed',
3130
- value: 'b',
3131
- pinned: false,
3132
- },
3133
- {
3134
- id: '',
3135
- type: 'feed',
3136
- value: 'c',
3137
- pinned: true,
3138
- },
3139
- ])
3140
- expect(saved).toStrictEqual(['a', 'b', 'c'])
3141
- expect(pinned).toStrictEqual(['a', 'c'])
3142
- })
3143
-
3144
- describe(`getSavedFeedType`, () => {
3145
- it(`works`, () => {
3146
- expect(getSavedFeedType('at://foo.com')).toBe('unknown')
3147
- expect(getSavedFeedType(feedUri())).toBe('feed')
3148
- expect(getSavedFeedType(listUri())).toBe('list')
3149
- expect(
3150
- getSavedFeedType('at://did:plc:fake/app.bsky.graph.follow/fake'),
3151
- ).toBe('unknown')
3152
- })
3153
- })
3154
-
3155
- describe(`validateSavedFeed`, () => {
3156
- it(`throws if missing id`, () => {
3157
- // really only checks length at time of writing
3158
- expect(() =>
3159
- validateSavedFeed({
3160
- id: '',
3161
- type: 'feed',
3162
- value: feedUri(),
3163
- pinned: false,
3164
- }),
3165
- ).toThrow()
3166
- })
3167
-
3168
- it(`does not throw if a UUID is used as id`, () => {
3169
- // really only checks length at time of writing
3170
- expect(() =>
3171
- validateSavedFeed({
3172
- id: '497dcba3-ecbf-4587-a2dd-5eb0665e6880',
3173
- type: 'feed',
3174
- value: feedUri(),
3175
- pinned: false,
3176
- }),
3177
- ).not.toThrow()
3178
- })
3179
-
3180
- it(`throws if mismatched types`, () => {
3181
- expect(() =>
3182
- validateSavedFeed({
3183
- id: TID.nextStr(),
3184
- type: 'list',
3185
- value: feedUri(),
3186
- pinned: false,
3187
- }),
3188
- ).toThrow()
3189
- expect(() =>
3190
- validateSavedFeed({
3191
- id: TID.nextStr(),
3192
- type: 'feed',
3193
- value: listUri(),
3194
- pinned: false,
3195
- }),
3196
- ).toThrow()
3197
- })
3198
-
3199
- it(`ignores values it can't validate`, () => {
3200
- expect(() =>
3201
- validateSavedFeed({
3202
- id: TID.nextStr(),
3203
- type: 'timeline',
3204
- value: 'following',
3205
- pinned: false,
3206
- }),
3207
- ).not.toThrow()
3208
- expect(() =>
3209
- validateSavedFeed({
3210
- id: TID.nextStr(),
3211
- type: 'unknown',
3212
- value: 'could be @nyt4!ng',
3213
- pinned: false,
3214
- }),
3215
- ).not.toThrow()
3216
- })
3217
- })
3218
- })
3219
- })
3220
-
3221
- describe(`saved feeds v2: migration scenarios`, () => {
3222
- let agent: AtpAgent
3223
- let i = 0
3224
- const feedUri = () => `at://bob.com/app.bsky.feed.generator/${i++}`
3225
-
3226
- beforeAll(async () => {
3227
- agent = new AtpAgent({ service: network.pds.url })
3228
- await agent.createAccount({
3229
- handle: 'user10.test',
3230
- email: 'user10@test.com',
3231
- password: 'password',
3232
- })
3233
- })
3234
-
3235
- beforeEach(async () => {
3236
- await agent.app.bsky.actor.putPreferences({
3237
- preferences: [],
3238
- })
3239
- })
3240
-
3241
- it('CRUD action before migration, no timeline inserted', async () => {
3242
- const feed = {
3243
- type: 'feed',
3244
- value: feedUri(),
3245
- pinned: false,
3246
- }
3247
- await agent.addSavedFeeds([feed])
3248
- const prefs = await agent.getPreferences()
3249
- expect(prefs.savedFeeds).toStrictEqual([
3250
- {
3251
- ...feed,
3252
- id: expect.any(String),
3253
- },
3254
- ])
3255
- })
3256
-
3257
- it('CRUD action AFTER migration, timeline was inserted', async () => {
3258
- await agent.getPreferences()
3259
- const feed = {
3260
- type: 'feed',
3261
- value: feedUri(),
3262
- pinned: false,
3263
- }
3264
- await agent.addSavedFeeds([feed])
3265
- const prefs = await agent.getPreferences()
3266
- expect(prefs.savedFeeds).toStrictEqual([
3267
- {
3268
- id: expect.any(String),
3269
- type: 'timeline',
3270
- value: 'following',
3271
- pinned: true,
3272
- },
3273
- {
3274
- ...feed,
3275
- id: expect.any(String),
3276
- },
3277
- ])
3278
- })
3279
-
3280
- // fresh account OR an old account with no v1 prefs to migrate from
3281
- it(`brand new user, v1 remains undefined`, async () => {
3282
- const prefs = await agent.getPreferences()
3283
- expect(prefs.savedFeeds).toStrictEqual([
3284
- {
3285
- id: expect.any(String),
3286
- type: 'timeline',
3287
- value: 'following',
3288
- pinned: true,
3289
- },
3290
- ])
3291
- // no v1 prefs to populate from
3292
- expect(prefs.feeds).toStrictEqual({
3293
- saved: undefined,
3294
- pinned: undefined,
3295
- })
3296
- })
3297
-
3298
- it(`brand new user, v2 does not write to v1`, async () => {
3299
- const a = feedUri()
3300
- // migration happens
3301
- await agent.getPreferences()
3302
- await agent.addSavedFeeds([
3303
- {
3304
- type: 'feed',
3305
- value: a,
3306
- pinned: false,
3307
- },
3308
- ])
3309
- const prefs = await agent.getPreferences()
3310
- expect(prefs.savedFeeds).toStrictEqual([
3311
- {
3312
- id: expect.any(String),
3313
- type: 'timeline',
3314
- value: 'following',
3315
- pinned: true,
3316
- },
3317
- {
3318
- id: expect.any(String),
3319
- type: 'feed',
3320
- value: a,
3321
- pinned: false,
3322
- },
3323
- ])
3324
- // no v1 prefs to populate from
3325
- expect(prefs.feeds).toStrictEqual({
3326
- saved: undefined,
3327
- pinned: undefined,
3328
- })
3329
- })
3330
-
3331
- it(`existing user with v1 prefs, migrates`, async () => {
3332
- const one = feedUri()
3333
- const two = feedUri()
3334
- await agent.app.bsky.actor.putPreferences({
3335
- preferences: [
3336
- {
3337
- $type: 'app.bsky.actor.defs#savedFeedsPref',
3338
- pinned: [one],
3339
- saved: [one, two],
3340
- },
3341
- ],
3342
- })
3343
- const prefs = await agent.getPreferences()
3344
-
3345
- // deprecated interface receives what it normally would
3346
- expect(prefs.feeds).toStrictEqual({
3347
- pinned: [one],
3348
- saved: [one, two],
3349
- })
3350
- // new interface gets new timeline + old pinned feed
3351
- expect(prefs.savedFeeds).toStrictEqual([
3352
- {
3353
- id: expect.any(String),
3354
- type: 'timeline',
3355
- value: 'following',
3356
- pinned: true,
3357
- },
3358
- {
3359
- id: expect.any(String),
3360
- type: 'feed',
3361
- value: one,
3362
- pinned: true,
3363
- },
3364
- {
3365
- id: expect.any(String),
3366
- type: 'feed',
3367
- value: two,
3368
- pinned: false,
3369
- },
3370
- ])
3371
- })
3372
-
3373
- it('squashes duplicates during migration', async () => {
3374
- const one = feedUri()
3375
- const two = feedUri()
3376
- await agent.app.bsky.actor.putPreferences({
3377
- preferences: [
3378
- {
3379
- $type: 'app.bsky.actor.defs#savedFeedsPref',
3380
- pinned: [one, two],
3381
- saved: [one, two],
3382
- },
3383
- {
3384
- $type: 'app.bsky.actor.defs#savedFeedsPref',
3385
- pinned: [],
3386
- saved: [],
3387
- },
3388
- ],
3389
- })
3390
-
3391
- // performs migration
3392
- const prefs = await agent.getPreferences()
3393
- expect(prefs.feeds).toStrictEqual({
3394
- pinned: [],
3395
- saved: [],
3396
- })
3397
- expect(prefs.savedFeeds).toStrictEqual([
3398
- {
3399
- id: expect.any(String),
3400
- type: 'timeline',
3401
- value: 'following',
3402
- pinned: true,
3403
- },
3404
- ])
3405
-
3406
- const res = await agent.app.bsky.actor.getPreferences()
3407
- expect(res.data.preferences).toStrictEqual([
3408
- {
3409
- $type: 'app.bsky.actor.defs#savedFeedsPrefV2',
3410
- items: [
3411
- {
3412
- id: expect.any(String),
3413
- type: 'timeline',
3414
- value: 'following',
3415
- pinned: true,
3416
- },
3417
- ],
3418
- },
3419
- {
3420
- $type: 'app.bsky.actor.defs#savedFeedsPref',
3421
- pinned: [],
3422
- saved: [],
3423
- },
3424
- ])
3425
- })
3426
-
3427
- it('v2 writes persist to v1, not the inverse', async () => {
3428
- const a = feedUri()
3429
- const b = feedUri()
3430
- const c = feedUri()
3431
- const d = feedUri()
3432
- const e = feedUri()
3433
-
3434
- await agent.app.bsky.actor.putPreferences({
3435
- preferences: [
3436
- {
3437
- $type: 'app.bsky.actor.defs#savedFeedsPref',
3438
- pinned: [a, b],
3439
- saved: [a, b],
3440
- },
3441
- ],
3442
- })
3443
-
3444
- // client updates, migrates to v2
3445
- // a and b are both pinned
3446
- await agent.getPreferences()
3447
-
3448
- // new write to v2, c is saved
3449
- await agent.addSavedFeeds([
3450
- {
3451
- type: 'feed',
3452
- value: c,
3453
- pinned: false,
3454
- },
3455
- ])
3456
-
3457
- // v2 write wrote to v1 also
3458
- const res1 = await agent.app.bsky.actor.getPreferences()
3459
- const v1Pref = res1.data.preferences.find((p) =>
3460
- AppBskyActorDefs.isSavedFeedsPref(p),
3461
- )
3462
- expect(v1Pref).toStrictEqual({
3463
- $type: 'app.bsky.actor.defs#savedFeedsPref',
3464
- pinned: [a, b],
3465
- saved: [a, b, c],
3466
- })
3467
-
3468
- // v1 write occurs, d is added but not to v2
3469
- await agent.addSavedFeed(d)
3470
-
3471
- const res3 = await agent.app.bsky.actor.getPreferences()
3472
- const v1Pref3 = res3.data.preferences.find((p) =>
3473
- AppBskyActorDefs.isSavedFeedsPref(p),
3474
- )
3475
- expect(v1Pref3).toStrictEqual({
3476
- $type: 'app.bsky.actor.defs#savedFeedsPref',
3477
- pinned: [a, b],
3478
- saved: [a, b, c, d],
3479
- })
3480
-
3481
- // another new write to v2, pins e
3482
- await agent.addSavedFeeds([
3483
- {
3484
- type: 'feed',
3485
- value: e,
3486
- pinned: true,
3487
- },
3488
- ])
3489
-
3490
- const res4 = await agent.app.bsky.actor.getPreferences()
3491
- const v1Pref4 = res4.data.preferences.find((p) =>
3492
- AppBskyActorDefs.isSavedFeedsPref(p),
3493
- )
3494
- // v1 pref got v2 write
3495
- expect(v1Pref4).toStrictEqual({
3496
- $type: 'app.bsky.actor.defs#savedFeedsPref',
3497
- pinned: [a, b, e],
3498
- saved: [a, b, c, d, e],
3499
- })
3500
-
3501
- const final = await agent.getPreferences()
3502
- // d not here bc it was written with v1
3503
- expect(final.savedFeeds).toStrictEqual([
3504
- {
3505
- id: expect.any(String),
3506
- type: 'timeline',
3507
- value: 'following',
3508
- pinned: true,
3509
- },
3510
- { id: expect.any(String), type: 'feed', value: a, pinned: true },
3511
- { id: expect.any(String), type: 'feed', value: b, pinned: true },
3512
- { id: expect.any(String), type: 'feed', value: e, pinned: true },
3513
- { id: expect.any(String), type: 'feed', value: c, pinned: false },
3514
- ])
3515
- })
3516
-
3517
- it(`filters out invalid values in v1 prefs`, async () => {
3518
- // v1 prefs must be valid AtUris, but they could be any type in theory
3519
- await agent.app.bsky.actor.putPreferences({
3520
- preferences: [
3521
- {
3522
- $type: 'app.bsky.actor.defs#savedFeedsPref',
3523
- pinned: ['at://did:plc:fake/app.bsky.graph.follow/fake'],
3524
- saved: ['at://did:plc:fake/app.bsky.graph.follow/fake'],
3525
- },
3526
- ],
3527
- })
3528
- const prefs = await agent.getPreferences()
3529
- expect(prefs.savedFeeds).toStrictEqual([
3530
- {
3531
- id: expect.any(String),
3532
- type: 'timeline',
3533
- value: 'following',
3534
- pinned: true,
3535
- },
3536
- ])
3537
- })
3538
- })
3539
-
3540
- describe('queued nudges', () => {
3541
- it('queueNudges & dismissNudges', async () => {
3542
- const agent = new AtpAgent({ service: network.pds.url })
3543
- await agent.createAccount({
3544
- handle: 'user11.test',
3545
- email: 'user11@test.com',
3546
- password: 'password',
3547
- })
3548
- await agent.bskyAppQueueNudges('first')
3549
- await expect(agent.getPreferences()).resolves.toHaveProperty(
3550
- 'bskyAppState.queuedNudges',
3551
- ['first'],
3552
- )
3553
- await agent.bskyAppQueueNudges(['second', 'third'])
3554
- await expect(agent.getPreferences()).resolves.toHaveProperty(
3555
- 'bskyAppState.queuedNudges',
3556
- ['first', 'second', 'third'],
3557
- )
3558
- await agent.bskyAppDismissNudges('second')
3559
- await expect(agent.getPreferences()).resolves.toHaveProperty(
3560
- 'bskyAppState.queuedNudges',
3561
- ['first', 'third'],
3562
- )
3563
- await agent.bskyAppDismissNudges(['first', 'third'])
3564
- await expect(agent.getPreferences()).resolves.toHaveProperty(
3565
- 'bskyAppState.queuedNudges',
3566
- [],
3567
- )
3568
- })
3569
- })
3570
-
3571
- describe('guided tours', () => {
3572
- it('setActiveProgressGuide', async () => {
3573
- const agent = new AtpAgent({ service: network.pds.url })
3574
-
3575
- await agent.createAccount({
3576
- handle: 'user12.test',
3577
- email: 'user12@test.com',
3578
- password: 'password',
3579
- })
3580
-
3581
- await agent.bskyAppSetActiveProgressGuide({
3582
- guide: 'test-guide',
3583
- // @ts-expect-error unspecced field
3584
- numThings: 0,
3585
- })
3586
- await expect(agent.getPreferences()).resolves.toHaveProperty(
3587
- 'bskyAppState.activeProgressGuide.guide',
3588
- 'test-guide',
3589
- )
3590
- await agent.bskyAppSetActiveProgressGuide({
3591
- guide: 'test-guide',
3592
- // @ts-expect-error unspecced field
3593
- numThings: 1,
3594
- })
3595
- await expect(agent.getPreferences()).resolves.toHaveProperty(
3596
- 'bskyAppState.activeProgressGuide.guide',
3597
- 'test-guide',
3598
- )
3599
- await expect(agent.getPreferences()).resolves.toHaveProperty(
3600
- 'bskyAppState.activeProgressGuide.numThings',
3601
- 1,
3602
- )
3603
- await agent.bskyAppSetActiveProgressGuide(undefined)
3604
- await expect(agent.getPreferences()).resolves.toHaveProperty(
3605
- 'bskyAppState.activeProgressGuide',
3606
- undefined,
3607
- )
3608
- })
3609
- })
3610
-
3611
- describe('nuxs', () => {
3612
- let agent: AtpAgent
3613
-
3614
- const nux = {
3615
- id: 'a',
3616
- completed: false,
3617
- data: '{}',
3618
- expiresAt: new Date(Date.now() + 6e3).toISOString(),
3619
- }
3620
-
3621
- beforeAll(async () => {
3622
- agent = new AtpAgent({ service: network.pds.url })
3623
-
3624
- await agent.createAccount({
3625
- handle: 'nuxs.test',
3626
- email: 'nuxs@test.com',
3627
- password: 'password',
3628
- })
3629
- })
3630
-
3631
- it('bskyAppUpsertNux', async () => {
3632
- // never duplicates
3633
- await agent.bskyAppUpsertNux(nux)
3634
- await agent.bskyAppUpsertNux(nux)
3635
- await agent.bskyAppUpsertNux(nux)
3636
-
3637
- const prefs = await agent.getPreferences()
3638
- const nuxs = prefs.bskyAppState.nuxs
3639
-
3640
- expect(nuxs.length).toEqual(1)
3641
- expect(nuxs.find((n) => n.id === nux.id)).toEqual(nux)
3642
- })
3643
-
3644
- it('bskyAppUpsertNux completed', async () => {
3645
- // never duplicates
3646
- await agent.bskyAppUpsertNux({
3647
- ...nux,
3648
- completed: true,
3649
- })
3650
-
3651
- const prefs = await agent.getPreferences()
3652
- const nuxs = prefs.bskyAppState.nuxs
3653
-
3654
- expect(nuxs.length).toEqual(1)
3655
- expect(nuxs.find((n) => n.id === nux.id)?.completed).toEqual(true)
3656
- })
3657
-
3658
- it('bskyAppRemoveNuxs', async () => {
3659
- await agent.bskyAppRemoveNuxs([nux.id])
3660
-
3661
- const prefs = await agent.getPreferences()
3662
- const nuxs = prefs.bskyAppState.nuxs
3663
-
3664
- expect(nuxs.length).toEqual(0)
3665
- })
3666
-
3667
- it('bskyAppUpsertNux validates nux', async () => {
3668
- // @ts-expect-error
3669
- expect(() => agent.bskyAppUpsertNux({ name: 'a' })).rejects.toThrow()
3670
- expect(() =>
3671
- // @ts-expect-error
3672
- agent.bskyAppUpsertNux({ id: 'a', completed: false, foo: 'bar' }),
3673
- ).rejects.toThrow()
3674
- })
3675
- })
3676
-
3677
- describe('setPostInteractionSettings', () => {
3678
- let agent: AtpAgent
3679
-
3680
- beforeAll(async () => {
3681
- agent = new AtpAgent({ service: network.pds.url })
3682
-
3683
- await agent.createAccount({
3684
- handle: 'pints.test',
3685
- email: 'pints@test.com',
3686
- password: 'password',
3687
- })
3688
- })
3689
-
3690
- it('works', async () => {
3691
- const next: AppBskyActorDefs.PostInteractionSettingsPref = {
3692
- threadgateAllowRules: [
3693
- { $type: 'app.bsky.feed.threadgate#mentionRule' },
3694
- ],
3695
- postgateEmbeddingRules: [],
3696
- }
3697
-
3698
- await agent.setPostInteractionSettings(next)
3699
-
3700
- const prefs = await agent.getPreferences()
3701
-
3702
- expect(prefs.postInteractionSettings).toEqual(next)
3703
- })
3704
-
3705
- it('clears', async () => {
3706
- const next: AppBskyActorDefs.PostInteractionSettingsPref = {
3707
- threadgateAllowRules: [],
3708
- postgateEmbeddingRules: [],
3709
- }
3710
-
3711
- await agent.setPostInteractionSettings(next)
3712
-
3713
- const prefs = await agent.getPreferences()
3714
-
3715
- expect(prefs.postInteractionSettings).toEqual(next)
3716
- })
3717
-
3718
- /**
3719
- * Logic matches threadgate `allow` logic, where `undefined` means "anyone
3720
- * can reply" and an empty array means "no one can reply".
3721
- *
3722
- * Postgate `embeddingRules` behaves differently, where `undefined` and
3723
- * an empty array both mean "no particular rules applied".
3724
- *
3725
- * Both props are optional though, so for easier sharing of types, we
3726
- * allow `undefined`.
3727
- */
3728
- it('clears using undefined', async () => {
3729
- const next: AppBskyActorDefs.PostInteractionSettingsPref = {
3730
- threadgateAllowRules: undefined,
3731
- postgateEmbeddingRules: undefined,
3732
- }
3733
-
3734
- await agent.setPostInteractionSettings(next)
3735
-
3736
- const prefs = await agent.getPreferences()
3737
-
3738
- expect(prefs.postInteractionSettings).toEqual(next)
3739
- })
3740
-
3741
- it('validates inputs', async () => {
3742
- expect(() =>
3743
- agent.setPostInteractionSettings({
3744
- // @ts-expect-error we are testing invalid inputs
3745
- threadgateAllowRules: [{ key: 'string' }],
3746
- postgateEmbeddingRules: [],
3747
- }),
3748
- ).rejects.toThrow()
3749
- })
3750
- })
3751
-
3752
- describe('setVerificationPrefs', () => {
3753
- let agent: AtpAgent
3754
-
3755
- beforeAll(async () => {
3756
- agent = new AtpAgent({ service: network.pds.url })
3757
-
3758
- await agent.createAccount({
3759
- handle: 'verification-prefs.test',
3760
- email: 'verification-prefs@test.com',
3761
- password: 'password',
3762
- })
3763
- })
3764
-
3765
- it('default state', async () => {
3766
- const next: AppBskyActorDefs.VerificationPrefs = {
3767
- hideBadges: false,
3768
- }
3769
- const prefs = await agent.getPreferences()
3770
- expect(prefs.verificationPrefs).toEqual(next)
3771
- })
3772
-
3773
- it('updates', async () => {
3774
- const next: AppBskyActorDefs.VerificationPrefs = {
3775
- hideBadges: true,
3776
- }
3777
- await agent.setVerificationPrefs(next)
3778
- const prefs = await agent.getPreferences()
3779
- expect(prefs.verificationPrefs).toEqual(next)
3780
- })
3781
- })
3782
-
3783
- describe('updateLiveEventPreferences', () => {
3784
- let agent: AtpAgent
3785
-
3786
- beforeAll(async () => {
3787
- agent = new AtpAgent({ service: network.pds.url })
3788
-
3789
- await agent.createAccount({
3790
- handle: 'live-event-prefs.test',
3791
- email: 'live-event-prefs@test.com',
3792
- password: 'password',
3793
- })
3794
- })
3795
-
3796
- it('default state', async () => {
3797
- const prefs = await agent.getPreferences()
3798
- expect(prefs.liveEventPreferences).toEqual({
3799
- hiddenFeedIds: [],
3800
- hideAllFeeds: false,
3801
- })
3802
- })
3803
-
3804
- it('hideFeed adds a feed id', async () => {
3805
- await agent.updateLiveEventPreferences({
3806
- type: 'hideFeed',
3807
- id: 'feed1',
3808
- })
3809
- const prefs = await agent.getPreferences()
3810
- expect(prefs.liveEventPreferences).toEqual({
3811
- hiddenFeedIds: ['feed1'],
3812
- hideAllFeeds: false,
3813
- })
3814
- })
3815
-
3816
- it('hideFeed adds another feed id', async () => {
3817
- await agent.updateLiveEventPreferences({
3818
- type: 'hideFeed',
3819
- id: 'feed2',
3820
- })
3821
- const prefs = await agent.getPreferences()
3822
- expect(prefs.liveEventPreferences).toEqual({
3823
- hiddenFeedIds: ['feed1', 'feed2'],
3824
- hideAllFeeds: false,
3825
- })
3826
- })
3827
-
3828
- it('unhideFeed removes a feed id', async () => {
3829
- await agent.updateLiveEventPreferences({
3830
- type: 'unhideFeed',
3831
- id: 'feed1',
3832
- })
3833
- const prefs = await agent.getPreferences()
3834
- expect(prefs.liveEventPreferences).toEqual({
3835
- hiddenFeedIds: ['feed2'],
3836
- hideAllFeeds: false,
3837
- })
3838
- })
3839
-
3840
- it('toggleHideAllFeeds toggles the flag', async () => {
3841
- await agent.updateLiveEventPreferences({ type: 'toggleHideAllFeeds' })
3842
- const prefs = await agent.getPreferences()
3843
- expect(prefs.liveEventPreferences).toEqual({
3844
- hiddenFeedIds: ['feed2'],
3845
- hideAllFeeds: true,
3846
- })
3847
- })
3848
-
3849
- it('toggleHideAllFeeds toggles back', async () => {
3850
- await agent.updateLiveEventPreferences({ type: 'toggleHideAllFeeds' })
3851
- const prefs = await agent.getPreferences()
3852
- expect(prefs.liveEventPreferences).toEqual({
3853
- hiddenFeedIds: ['feed2'],
3854
- hideAllFeeds: false,
3855
- })
3856
- })
3857
- })
3858
-
3859
- // end
3860
- })
3861
- })
3862
-
3863
- const byType = (a, b) => a.$type.localeCompare(b.$type)