tangled
alpha
login
or
join now
robinwobin.dev
/
witchsky.app
forked from
jollywhoppers.com/witchsky.app
0
fork
atom
Bluesky app fork with some witchin' additions 💫
0
fork
atom
overview
issues
pulls
pipelines
demo mode
authored by
samuel.fm
and committed by
Eric Bailey
10 months ago
91f9bc7c
c34c67e4
+616
-21
5 changed files
expand all
collapse all
unified
split
src
lib
api
feed
demo.ts
demo.ts
state
queries
post-feed.ts
view
screens
Home.tsx
shell
bottom-bar
BottomBar.tsx
+20
src/lib/api/feed/demo.ts
···
1
1
+
import {type AppBskyFeedDefs, type BskyAgent} from '@atproto/api'
2
2
+
3
3
+
import {DEMO_FEED} from '#/lib/demo'
4
4
+
import {type FeedAPI, type FeedAPIResponse} from './types'
5
5
+
6
6
+
export class DemoFeedAPI implements FeedAPI {
7
7
+
agent: BskyAgent
8
8
+
9
9
+
constructor({agent}: {agent: BskyAgent}) {
10
10
+
this.agent = agent
11
11
+
}
12
12
+
13
13
+
async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> {
14
14
+
return DEMO_FEED.feed[0]
15
15
+
}
16
16
+
17
17
+
async fetch(): Promise<FeedAPIResponse> {
18
18
+
return DEMO_FEED
19
19
+
}
20
20
+
}
+516
src/lib/demo.ts
···
1
1
+
import {type AppBskyFeedGetFeed} from '@atproto/api'
2
2
+
3
3
+
export const DEMO_FEED = {
4
4
+
feed: [
5
5
+
{
6
6
+
post: {
7
7
+
uri: 'at://did:plc:vwzwgnygau7ed7b7wt5ux7y2/app.bsky.feed.post/3lng6kvb6uc2a',
8
8
+
cid: 'bafyreifqyej7jivzucaagu22f7jj7rvjcpbzv2kxo27wt47ktduwwdpdae',
9
9
+
author: {
10
10
+
did: 'did:plc:vwzwgnygau7ed7b7wt5ux7y2',
11
11
+
handle: 'someoneelse.bsky.social',
12
12
+
displayName: 'Not David',
13
13
+
avatar:
14
14
+
'https://cdn.bsky.app/img/avatar/plain/did:plc:vwzwgnygau7ed7b7wt5ux7y2/bafkreifrtz3ngpzz5qmhjliv5toj4nvyjovijxs5e2la67wddhmdhro5he@jpeg',
15
15
+
associated: {
16
16
+
chat: {
17
17
+
allowIncoming: 'following',
18
18
+
},
19
19
+
},
20
20
+
viewer: {
21
21
+
muted: false,
22
22
+
blockedBy: false,
23
23
+
following:
24
24
+
'at://did:plc:p2cp5gopk7mgjegy6wadk3ep/app.bsky.graph.follow/3kcvvfzq6o32a',
25
25
+
followedBy:
26
26
+
'at://did:plc:vwzwgnygau7ed7b7wt5ux7y2/app.bsky.graph.follow/3jwawchotz22h',
27
27
+
},
28
28
+
labels: [],
29
29
+
createdAt: '2023-04-27T09:23:54.423Z',
30
30
+
verification: {
31
31
+
verifications: [
32
32
+
{
33
33
+
issuer: 'did:plc:z72i7hdynmk6r22z27h6tvur',
34
34
+
uri: 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.graph.verification/3lndpxt7ur22f',
35
35
+
isValid: true,
36
36
+
createdAt: '2025-04-21T10:48:58.775Z',
37
37
+
},
38
38
+
],
39
39
+
verifiedStatus: 'valid',
40
40
+
trustedVerifierStatus: 'none',
41
41
+
},
42
42
+
},
43
43
+
record: {
44
44
+
$type: 'app.bsky.feed.post',
45
45
+
createdAt: '2025-04-22T17:15:30.525Z',
46
46
+
langs: ['en'],
47
47
+
reply: {
48
48
+
parent: {
49
49
+
cid: 'bafyreic7wmhywvu5fi4lupswxpmyydqn2gl5kwnt4n4jxvb3lpej2l72ku',
50
50
+
uri: 'at://did:plc:vc7f4oafdgxsihk4cry2xpze/app.bsky.feed.post/3lng66dkzu222',
51
51
+
},
52
52
+
root: {
53
53
+
cid: 'bafyreic7wmhywvu5fi4lupswxpmyydqn2gl5kwnt4n4jxvb3lpej2l72ku',
54
54
+
uri: 'at://did:plc:vc7f4oafdgxsihk4cry2xpze/app.bsky.feed.post/3lng66dkzu222',
55
55
+
},
56
56
+
},
57
57
+
text: 'sometimes I go to the apple store just to look at them',
58
58
+
},
59
59
+
replyCount: 0,
60
60
+
repostCount: 0,
61
61
+
likeCount: 6,
62
62
+
quoteCount: 0,
63
63
+
indexedAt: '2025-04-22T17:15:31.251Z',
64
64
+
viewer: {
65
65
+
threadMuted: false,
66
66
+
embeddingDisabled: false,
67
67
+
},
68
68
+
labels: [],
69
69
+
},
70
70
+
reply: {
71
71
+
root: {
72
72
+
uri: 'at://did:plc:vc7f4oafdgxsihk4cry2xpze/app.bsky.feed.post/3lng66dkzu222',
73
73
+
cid: 'bafyreic7wmhywvu5fi4lupswxpmyydqn2gl5kwnt4n4jxvb3lpej2l72ku',
74
74
+
author: {
75
75
+
did: 'did:plc:vc7f4oafdgxsihk4cry2xpze',
76
76
+
handle: 'jasalterego.bsky.social',
77
77
+
displayName: 'Jerry Appleseed',
78
78
+
avatar:
79
79
+
'https://cdn.bsky.app/img/avatar/plain/did:plc:vc7f4oafdgxsihk4cry2xpze/bafkreicwxwecqiko2rwwln5y3fqqb2zx6wfg5rxf5r7lukakkq2slqy5hy@jpeg',
80
80
+
associated: {
81
81
+
chat: {
82
82
+
allowIncoming: 'following',
83
83
+
},
84
84
+
},
85
85
+
viewer: {
86
86
+
muted: false,
87
87
+
blockedBy: false,
88
88
+
following:
89
89
+
'at://did:plc:p2cp5gopk7mgjegy6wadk3ep/app.bsky.graph.follow/3jx4rnvlnhl25',
90
90
+
followedBy:
91
91
+
'at://did:plc:vc7f4oafdgxsihk4cry2xpze/app.bsky.graph.follow/3jx4siiniuc2e',
92
92
+
},
93
93
+
labels: [
94
94
+
{
95
95
+
cts: '2024-05-17T21:53:59.049Z',
96
96
+
src: 'did:plc:skibpmllbhxvbvwgtjxl3uao',
97
97
+
uri: 'did:plc:vc7f4oafdgxsihk4cry2xpze',
98
98
+
val: 'cringe',
99
99
+
ver: 1,
100
100
+
},
101
101
+
{
102
102
+
cts: '2024-05-17T21:53:59.049Z',
103
103
+
src: 'did:plc:skibpmllbhxvbvwgtjxl3uao',
104
104
+
uri: 'did:plc:vc7f4oafdgxsihk4cry2xpze',
105
105
+
val: 'elder-watch',
106
106
+
ver: 1,
107
107
+
},
108
108
+
{
109
109
+
src: 'did:plc:vc7f4oafdgxsihk4cry2xpze',
110
110
+
uri: 'at://did:plc:vc7f4oafdgxsihk4cry2xpze/app.bsky.actor.profile/self',
111
111
+
cid: 'bafyreidfiuv3c22vliyu2onazf23zrp35rr7i3upsqa2dsn5cqimmlgugm',
112
112
+
val: '!no-unauthenticated',
113
113
+
cts: '1970-01-01T00:00:00.000Z',
114
114
+
},
115
115
+
],
116
116
+
createdAt: '2023-04-23T20:11:04.375Z',
117
117
+
},
118
118
+
record: {
119
119
+
$type: 'app.bsky.feed.post',
120
120
+
createdAt: '2025-04-22T17:08:29.321Z',
121
121
+
langs: ['en'],
122
122
+
text: "(studying the blade) ow that's the sharp side",
123
123
+
},
124
124
+
replyCount: 8,
125
125
+
repostCount: 37,
126
126
+
likeCount: 252,
127
127
+
quoteCount: 1,
128
128
+
indexedAt: '2025-04-22T17:08:29.458Z',
129
129
+
viewer: {
130
130
+
threadMuted: false,
131
131
+
embeddingDisabled: false,
132
132
+
},
133
133
+
labels: [],
134
134
+
$type: 'app.bsky.feed.defs#postView',
135
135
+
},
136
136
+
parent: {
137
137
+
uri: 'at://did:plc:vc7f4oafdgxsihk4cry2xpze/app.bsky.feed.post/3lng66dkzu222',
138
138
+
cid: 'bafyreic7wmhywvu5fi4lupswxpmyydqn2gl5kwnt4n4jxvb3lpej2l72ku',
139
139
+
author: {
140
140
+
did: 'did:plc:vc7f4oafdgxsihk4cry2xpze',
141
141
+
handle: 'jasalterego.bsky.social',
142
142
+
displayName: 'Jerry Appleseed',
143
143
+
avatar:
144
144
+
'https://cdn.bsky.app/img/avatar/plain/did:plc:vc7f4oafdgxsihk4cry2xpze/bafkreicwxwecqiko2rwwln5y3fqqb2zx6wfg5rxf5r7lukakkq2slqy5hy@jpeg',
145
145
+
associated: {
146
146
+
chat: {
147
147
+
allowIncoming: 'following',
148
148
+
},
149
149
+
},
150
150
+
viewer: {
151
151
+
muted: false,
152
152
+
blockedBy: false,
153
153
+
following:
154
154
+
'at://did:plc:p2cp5gopk7mgjegy6wadk3ep/app.bsky.graph.follow/3jx4rnvlnhl25',
155
155
+
followedBy:
156
156
+
'at://did:plc:vc7f4oafdgxsihk4cry2xpze/app.bsky.graph.follow/3jx4siiniuc2e',
157
157
+
},
158
158
+
labels: [
159
159
+
{
160
160
+
cts: '2024-05-17T21:53:59.049Z',
161
161
+
src: 'did:plc:skibpmllbhxvbvwgtjxl3uao',
162
162
+
uri: 'did:plc:vc7f4oafdgxsihk4cry2xpze',
163
163
+
val: 'cringe',
164
164
+
ver: 1,
165
165
+
},
166
166
+
{
167
167
+
cts: '2024-05-17T21:53:59.049Z',
168
168
+
src: 'did:plc:skibpmllbhxvbvwgtjxl3uao',
169
169
+
uri: 'did:plc:vc7f4oafdgxsihk4cry2xpze',
170
170
+
val: 'elder-watch',
171
171
+
ver: 1,
172
172
+
},
173
173
+
{
174
174
+
src: 'did:plc:vc7f4oafdgxsihk4cry2xpze',
175
175
+
uri: 'at://did:plc:vc7f4oafdgxsihk4cry2xpze/app.bsky.actor.profile/self',
176
176
+
cid: 'bafyreidfiuv3c22vliyu2onazf23zrp35rr7i3upsqa2dsn5cqimmlgugm',
177
177
+
val: '!no-unauthenticated',
178
178
+
cts: '1970-01-01T00:00:00.000Z',
179
179
+
},
180
180
+
],
181
181
+
createdAt: '2023-04-23T20:11:04.375Z',
182
182
+
},
183
183
+
record: {
184
184
+
$type: 'app.bsky.feed.post',
185
185
+
createdAt: '2025-04-22T17:08:29.321Z',
186
186
+
langs: ['en'],
187
187
+
text: '*sees the studio display* i think i hauve covid',
188
188
+
},
189
189
+
replyCount: 8,
190
190
+
repostCount: 37,
191
191
+
likeCount: 252,
192
192
+
quoteCount: 1,
193
193
+
indexedAt: '2025-04-22T17:08:29.458Z',
194
194
+
viewer: {
195
195
+
threadMuted: false,
196
196
+
embeddingDisabled: false,
197
197
+
},
198
198
+
labels: [],
199
199
+
$type: 'app.bsky.feed.defs#postView',
200
200
+
},
201
201
+
},
202
202
+
},
203
203
+
{
204
204
+
post: {
205
205
+
uri: 'at://did:plc:qvzn322kmcvd7xtnips5xaun/app.bsky.feed.post/3lnehbwkvzk2z',
206
206
+
cid: 'bafyreidshyla4xoolb7fexhtlqcjjbn6ts7z4xja7gnlinroms5cuqg3fq',
207
207
+
author: {
208
208
+
did: 'did:plc:qvzn322kmcvd7xtnips5xaun',
209
209
+
handle: 'scalzi.com',
210
210
+
displayName: 'John Scalzi',
211
211
+
avatar:
212
212
+
'https://cdn.bsky.app/img/avatar/plain/did:plc:qvzn322kmcvd7xtnips5xaun/bafkreih4dn5gllculyzb6wlqcqparkax35zloe3bzn2nufeqeilz4sutsu@jpeg',
213
213
+
associated: {
214
214
+
chat: {
215
215
+
allowIncoming: 'following',
216
216
+
},
217
217
+
},
218
218
+
219
219
+
viewer: {
220
220
+
muted: false,
221
221
+
blockedBy: false,
222
222
+
following:
223
223
+
'at://did:plc:p2cp5gopk7mgjegy6wadk3ep/app.bsky.graph.follow/3kcvvfzq6o32a',
224
224
+
followedBy:
225
225
+
'at://did:plc:vwzwgnygau7ed7b7wt5ux7y2/app.bsky.graph.follow/3jwawchotz22h',
226
226
+
},
227
227
+
labels: [],
228
228
+
createdAt: '2023-04-27T16:05:17.859Z',
229
229
+
},
230
230
+
record: {
231
231
+
$type: 'app.bsky.feed.post',
232
232
+
createdAt: '2025-04-22T00:46:14.095Z',
233
233
+
embed: {
234
234
+
$type: 'app.bsky.embed.images',
235
235
+
images: [
236
236
+
{
237
237
+
alt: 'Smudge napping on the Eames chair, which is covered in a blanket to avoid getting cat hair on it. ',
238
238
+
aspectRatio: {
239
239
+
height: 2000,
240
240
+
width: 1505,
241
241
+
},
242
242
+
image: {
243
243
+
$type: 'blob',
244
244
+
ref: {
245
245
+
$link:
246
246
+
'bafkreigkfbcmttc4r4avknp4y2mlcjlws3sdcat6jcyd2mjr7yvz6sje4q',
247
247
+
},
248
248
+
mimeType: 'image/jpeg',
249
249
+
size: 833834,
250
250
+
},
251
251
+
},
252
252
+
],
253
253
+
},
254
254
+
langs: ['en'],
255
255
+
text: 'Smudge found the Eames chair',
256
256
+
},
257
257
+
embed: {
258
258
+
$type: 'app.bsky.embed.images#view',
259
259
+
images: [
260
260
+
{
261
261
+
thumb:
262
262
+
'https://cdn.bsky.app/img/feed_thumbnail/plain/did:plc:qvzn322kmcvd7xtnips5xaun/bafkreigkfbcmttc4r4avknp4y2mlcjlws3sdcat6jcyd2mjr7yvz6sje4q@jpeg',
263
263
+
fullsize:
264
264
+
'https://cdn.bsky.app/img/feed_fullsize/plain/did:plc:qvzn322kmcvd7xtnips5xaun/bafkreigkfbcmttc4r4avknp4y2mlcjlws3sdcat6jcyd2mjr7yvz6sje4q@jpeg',
265
265
+
alt: 'Smudge napping on the Eames chair, which is covered in a blanket to avoid getting cat hair on it. ',
266
266
+
aspectRatio: {
267
267
+
height: 2000,
268
268
+
width: 1505,
269
269
+
},
270
270
+
},
271
271
+
],
272
272
+
},
273
273
+
replyCount: 47,
274
274
+
repostCount: 31,
275
275
+
likeCount: 1543,
276
276
+
quoteCount: 0,
277
277
+
indexedAt: '2025-04-22T00:46:17.457Z',
278
278
+
viewer: {
279
279
+
threadMuted: false,
280
280
+
embeddingDisabled: false,
281
281
+
},
282
282
+
labels: [],
283
283
+
},
284
284
+
},
285
285
+
{
286
286
+
post: {
287
287
+
uri: 'at://did:plc:jb2q4yqmgpmefxd4xx66gepm/app.bsky.feed.post/3lng4m2memc2k',
288
288
+
cid: 'bafyreicvgwkd5xkbtu3eh756yodnynqbxoegzzqhhees5ou45t3gq3j6um',
289
289
+
author: {
290
290
+
did: 'did:plc:jb2q4yqmgpmefxd4xx66gepm',
291
291
+
handle: 'visionprofan.bsky.social',
292
292
+
displayName: 'Visionary',
293
293
+
avatar:
294
294
+
'https://cdn.bsky.app/img/avatar/plain/did:plc:jb2q4yqmgpmefxd4xx66gepm/bafkreibqdpwilgj43m37ksjilzcnzqx2g3njcyiyifvazdar23rs5hlokm@jpeg',
295
295
+
associated: {
296
296
+
chat: {
297
297
+
allowIncoming: 'following',
298
298
+
},
299
299
+
},
300
300
+
301
301
+
viewer: {
302
302
+
muted: false,
303
303
+
blockedBy: false,
304
304
+
following:
305
305
+
'at://did:plc:p2cp5gopk7mgjegy6wadk3ep/app.bsky.graph.follow/3kcvvfzq6o32a',
306
306
+
followedBy:
307
307
+
'at://did:plc:vwzwgnygau7ed7b7wt5ux7y2/app.bsky.graph.follow/3jwawchotz22h',
308
308
+
},
309
309
+
labels: [
310
310
+
{
311
311
+
cts: '2024-07-14T04:31:55.599Z',
312
312
+
src: 'did:plc:skibpmllbhxvbvwgtjxl3uao',
313
313
+
uri: 'did:plc:jb2q4yqmgpmefxd4xx66gepm',
314
314
+
val: 'cringe',
315
315
+
ver: 1,
316
316
+
},
317
317
+
],
318
318
+
createdAt: '2023-04-13T05:48:48.827Z',
319
319
+
},
320
320
+
record: {
321
321
+
$type: 'app.bsky.feed.post',
322
322
+
createdAt: '2025-04-22T16:40:22.205Z',
323
323
+
embed: {
324
324
+
$type: 'app.bsky.embed.record',
325
325
+
record: {
326
326
+
cid: 'bafyreif6ks7dhjrgrio5guztdhf3byoygbwebbvehi4al2c5kmkevlqdky',
327
327
+
uri: 'at://did:plc:4llrhdclvdlmmynkwsmg5tdc/app.bsky.feed.post/3lnfspxzdd42p',
328
328
+
},
329
329
+
},
330
330
+
langs: ['en'],
331
331
+
text: 'Just got my Vision Pro! 🤯\n\nAny recommendations for apps to try out?',
332
332
+
},
333
333
+
embed: {
334
334
+
$type: 'app.bsky.embed.record#view',
335
335
+
record: {
336
336
+
$type: 'app.bsky.embed.record#viewRecord',
337
337
+
uri: 'at://did:plc:4llrhdclvdlmmynkwsmg5tdc/app.bsky.feed.post/3lnfspxzdd42p',
338
338
+
cid: 'bafyreif6ks7dhjrgrio5guztdhf3byoygbwebbvehi4al2c5kmkevlqdky',
339
339
+
author: {
340
340
+
did: 'did:plc:4llrhdclvdlmmynkwsmg5tdc',
341
341
+
handle: 'atrupar.com',
342
342
+
displayName: 'Aaron Rupar',
343
343
+
avatar:
344
344
+
'https://cdn.bsky.app/img/avatar/plain/did:plc:4llrhdclvdlmmynkwsmg5tdc/bafkreibmhm3h6ar52pogvolisrzjdhwa2myras5vkxzj67twxn2l6pogwu@jpeg',
345
345
+
associated: {
346
346
+
chat: {
347
347
+
allowIncoming: 'following',
348
348
+
},
349
349
+
},
350
350
+
viewer: {
351
351
+
muted: false,
352
352
+
blockedBy: false,
353
353
+
following:
354
354
+
'at://did:plc:p2cp5gopk7mgjegy6wadk3ep/app.bsky.graph.follow/3l7eals2t4g2k',
355
355
+
},
356
356
+
labels: [],
357
357
+
createdAt: '2023-04-28T00:47:57.437Z',
358
358
+
},
359
359
+
value: {
360
360
+
$type: 'app.bsky.feed.post',
361
361
+
createdAt: '2025-04-22T13:43:36.261Z',
362
362
+
embed: {
363
363
+
$type: 'app.bsky.embed.video',
364
364
+
alt: '',
365
365
+
aspectRatio: {
366
366
+
height: 720,
367
367
+
width: 1280,
368
368
+
},
369
369
+
captions: [
370
370
+
{
371
371
+
$type: 'app.bsky.embed.video#caption',
372
372
+
file: {
373
373
+
$type: 'blob',
374
374
+
ref: {
375
375
+
$link:
376
376
+
'bafkreihm7npnefqxqmn7d45lcbxzn4wnowc2abe5hxmd63qlob7fbszola',
377
377
+
},
378
378
+
mimeType: 'text/vtt',
379
379
+
size: 1339,
380
380
+
},
381
381
+
lang: 'en',
382
382
+
},
383
383
+
],
384
384
+
video: {
385
385
+
$type: 'blob',
386
386
+
ref: {
387
387
+
$link:
388
388
+
'bafkreihovdkyvql2yswuimm5m35lqyo7tgsfiujm7vrxtfmi4gwej4hkpa',
389
389
+
},
390
390
+
mimeType: 'video/mp4',
391
391
+
size: 6026932,
392
392
+
},
393
393
+
},
394
394
+
facets: [],
395
395
+
langs: ['en'],
396
396
+
text: 'Austin Scott previews how House Rs plan to cut Medicaid: "The federal govt is paying 90% of the Medicaid expansion. What we\'ve talked about is moving that 90% level of the expansion back... nobody would be kicked off Medicaid as long as governors decided they wanted to continue to fund the program"',
397
397
+
},
398
398
+
labels: [],
399
399
+
likeCount: 880,
400
400
+
replyCount: 230,
401
401
+
repostCount: 347,
402
402
+
quoteCount: 145,
403
403
+
indexedAt: '2025-04-22T13:43:37.350Z',
404
404
+
embeds: [
405
405
+
{
406
406
+
$type: 'app.bsky.embed.video#view',
407
407
+
cid: 'bafkreihovdkyvql2yswuimm5m35lqyo7tgsfiujm7vrxtfmi4gwej4hkpa',
408
408
+
playlist:
409
409
+
'https://video.bsky.app/watch/did%3Aplc%3A4llrhdclvdlmmynkwsmg5tdc/bafkreihovdkyvql2yswuimm5m35lqyo7tgsfiujm7vrxtfmi4gwej4hkpa/playlist.m3u8',
410
410
+
thumbnail:
411
411
+
'https://video.bsky.app/watch/did%3Aplc%3A4llrhdclvdlmmynkwsmg5tdc/bafkreihovdkyvql2yswuimm5m35lqyo7tgsfiujm7vrxtfmi4gwej4hkpa/thumbnail.jpg',
412
412
+
alt: '',
413
413
+
aspectRatio: {
414
414
+
height: 720,
415
415
+
width: 1280,
416
416
+
},
417
417
+
},
418
418
+
],
419
419
+
},
420
420
+
},
421
421
+
replyCount: 11,
422
422
+
repostCount: 97,
423
423
+
likeCount: 399,
424
424
+
quoteCount: 3,
425
425
+
indexedAt: '2025-04-22T16:40:22.648Z',
426
426
+
viewer: {
427
427
+
threadMuted: false,
428
428
+
embeddingDisabled: false,
429
429
+
},
430
430
+
labels: [],
431
431
+
},
432
432
+
},
433
433
+
{
434
434
+
post: {
435
435
+
uri: 'at://did:plc:5o6k7jvowuyaquloafzn3cfw/app.bsky.feed.post/3lng6lkuhxc2s',
436
436
+
cid: 'bafyreifwapmjx76kz5lkoeejfoes4ct2xhyycfyxwccmyr3mtxht5juyli',
437
437
+
author: {
438
438
+
did: 'did:plc:5o6k7jvowuyaquloafzn3cfw',
439
439
+
handle: 'johndoe.org',
440
440
+
displayName: 'John Doe',
441
441
+
avatar:
442
442
+
'https://cdn.bsky.app/img/avatar/plain/did:plc:5o6k7jvowuyaquloafzn3cfw/bafkreierrwtdsf5quwprs2xqmmh2lu2k7au2cibyegfpard6wqyjs7nd6i@jpeg',
443
443
+
444
444
+
viewer: {
445
445
+
muted: false,
446
446
+
blockedBy: false,
447
447
+
following:
448
448
+
'at://did:plc:p2cp5gopk7mgjegy6wadk3ep/app.bsky.graph.follow/3kcvvfzq6o32a',
449
449
+
followedBy:
450
450
+
'at://did:plc:vwzwgnygau7ed7b7wt5ux7y2/app.bsky.graph.follow/3jwawchotz22h',
451
451
+
},
452
452
+
labels: [],
453
453
+
createdAt: '2023-05-16T02:37:39.269Z',
454
454
+
},
455
455
+
record: {
456
456
+
$type: 'app.bsky.feed.post',
457
457
+
createdAt: '2025-04-22T17:15:53.178Z',
458
458
+
langs: ['en'],
459
459
+
text: "I'm running out of ideas for these fake posts. Alas, such is the demands of modern life. Do you like blueberries? Just remembered that blueberries are delicious! They're so tasty! I can't wait to try them!",
460
460
+
},
461
461
+
replyCount: 13,
462
462
+
repostCount: 121,
463
463
+
likeCount: 345,
464
464
+
quoteCount: 6,
465
465
+
indexedAt: '2025-04-22T17:15:53.351Z',
466
466
+
viewer: {
467
467
+
threadMuted: false,
468
468
+
embeddingDisabled: false,
469
469
+
},
470
470
+
labels: [],
471
471
+
},
472
472
+
},
473
473
+
{
474
474
+
post: {
475
475
+
uri: 'at://did:plc:5ywatwbfxoecxgb4xq6ods72/app.bsky.feed.post/3lng5w2fbvs2g',
476
476
+
cid: 'bafyreigi7d57fudzoybe4u6w7friw6ydz3pmzrdu3bvjwb5mvsvlprk23y',
477
477
+
author: {
478
478
+
did: 'did:plc:5ywatwbfxoecxgb4xq6ods72',
479
479
+
handle: 'cooking.bsky.social',
480
480
+
displayName: 'cooking tips',
481
481
+
avatar:
482
482
+
'https://cdn.bsky.app/img/avatar/plain/did:plc:5ywatwbfxoecxgb4xq6ods72/bafkreibbsuyy25elibbys5vx25cnkcs6g4ih6dozypa4bomwhkwsi6f5wa@jpeg',
483
483
+
484
484
+
viewer: {
485
485
+
muted: false,
486
486
+
blockedBy: false,
487
487
+
following:
488
488
+
'at://did:plc:p2cp5gopk7mgjegy6wadk3ep/app.bsky.graph.follow/3kcvvfzq6o32a',
489
489
+
followedBy:
490
490
+
'at://did:plc:vwzwgnygau7ed7b7wt5ux7y2/app.bsky.graph.follow/3jwawchotz22h',
491
491
+
},
492
492
+
labels: [],
493
493
+
createdAt: '2024-05-28T00:18:08.531Z',
494
494
+
},
495
495
+
record: {
496
496
+
$type: 'app.bsky.feed.post',
497
497
+
createdAt: '2025-04-22T17:03:51.259Z',
498
498
+
langs: ['en'],
499
499
+
text: 'and another thing. I have more things to say. I think. I forget :/',
500
500
+
},
501
501
+
replyCount: 1,
502
502
+
repostCount: 4,
503
503
+
likeCount: 20,
504
504
+
quoteCount: 0,
505
505
+
indexedAt: '2025-04-22T17:03:52.050Z',
506
506
+
viewer: {
507
507
+
threadMuted: false,
508
508
+
embeddingDisabled: false,
509
509
+
},
510
510
+
labels: [],
511
511
+
},
512
512
+
},
513
513
+
],
514
514
+
} satisfies AppBskyFeedGetFeed.OutputSchema
515
515
+
516
516
+
export const BOTTOM_BAR_AVI = 'https://bsky.social/about/hero-social-card.webp'
+13
-9
src/state/queries/post-feed.ts
···
1
1
import React, {useCallback, useEffect, useRef} from 'react'
2
2
import {AppState} from 'react-native'
3
3
import {
4
4
-
AppBskyActorDefs,
4
4
+
type AppBskyActorDefs,
5
5
AppBskyFeedDefs,
6
6
-
AppBskyFeedPost,
6
6
+
type AppBskyFeedPost,
7
7
AtUri,
8
8
-
BskyAgent,
8
8
+
type BskyAgent,
9
9
moderatePost,
10
10
-
ModerationDecision,
10
10
+
type ModerationDecision,
11
11
} from '@atproto/api'
12
12
import {
13
13
-
InfiniteData,
14
14
-
QueryClient,
15
15
-
QueryKey,
13
13
+
type InfiniteData,
14
14
+
type QueryClient,
15
15
+
type QueryKey,
16
16
useInfiniteQuery,
17
17
} from '@tanstack/react-query'
18
18
19
19
import {AuthorFeedAPI} from '#/lib/api/feed/author'
20
20
import {CustomFeedAPI} from '#/lib/api/feed/custom'
21
21
+
import {DemoFeedAPI} from '#/lib/api/feed/demo'
21
22
import {FollowingFeedAPI} from '#/lib/api/feed/following'
22
23
import {HomeFeedAPI} from '#/lib/api/feed/home'
23
24
import {LikesFeedAPI} from '#/lib/api/feed/likes'
24
25
import {ListFeedAPI} from '#/lib/api/feed/list'
25
26
import {MergeFeedAPI} from '#/lib/api/feed/merge'
26
26
-
import {FeedAPI, ReasonFeedSource} from '#/lib/api/feed/types'
27
27
+
import {type FeedAPI, type ReasonFeedSource} from '#/lib/api/feed/types'
27
28
import {aggregateUserInterests} from '#/lib/api/feed/utils'
28
28
-
import {FeedTuner, FeedTunerFn} from '#/lib/api/feed-manip'
29
29
+
import {FeedTuner, type FeedTunerFn} from '#/lib/api/feed-manip'
29
30
import {DISCOVER_FEED_URI} from '#/lib/constants'
30
31
import {BSKY_FEED_OWNER_DIDS} from '#/lib/constants'
31
32
import {logger} from '#/logger'
···
59
60
| `feedgen|${FeedUri}`
60
61
| `likes|${ActorDid}`
61
62
| `list|${ListUri}`
63
63
+
| 'demo'
62
64
export interface FeedParams {
63
65
mergeFeedEnabled?: boolean
64
66
mergeFeedSources?: string[]
···
483
485
} else if (feedDesc.startsWith('list')) {
484
486
const [_, list] = feedDesc.split('|')
485
487
return new ListFeedAPI({agent, feedParams: {list}})
488
488
+
} else if (feedDesc === 'demo') {
489
489
+
return new DemoFeedAPI({agent})
486
490
} else {
487
491
// shouldnt happen
488
492
return new FollowingFeedAPI({agent})
+58
-7
src/view/screens/Home.tsx
···
8
8
import {useSetTitle} from '#/lib/hooks/useSetTitle'
9
9
import {useRequestNotificationsPermission} from '#/lib/notifications/notifications'
10
10
import {
11
11
-
HomeTabNavigatorParams,
12
12
-
NativeStackScreenProps,
11
11
+
type HomeTabNavigatorParams,
12
12
+
type NativeStackScreenProps,
13
13
} from '#/lib/routes/types'
14
14
import {logEvent} from '#/lib/statsig/statsig'
15
15
import {isWeb} from '#/platform/detection'
16
16
import {emitSoftReset} from '#/state/events'
17
17
-
import {SavedFeedSourceInfo, usePinnedFeedsInfos} from '#/state/queries/feed'
18
18
-
import {FeedDescriptor, FeedParams} from '#/state/queries/post-feed'
17
17
+
import {
18
18
+
type SavedFeedSourceInfo,
19
19
+
usePinnedFeedsInfos,
20
20
+
} from '#/state/queries/feed'
21
21
+
import {type FeedDescriptor, type FeedParams} from '#/state/queries/post-feed'
19
22
import {usePreferencesQuery} from '#/state/queries/preferences'
20
20
-
import {UsePreferencesQueryResponse} from '#/state/queries/preferences/types'
23
23
+
import {type UsePreferencesQueryResponse} from '#/state/queries/preferences/types'
21
24
import {useSession} from '#/state/session'
22
25
import {useSetMinimalShellMode} from '#/state/shell'
23
26
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
24
27
import {useSelectedFeed, useSetSelectedFeed} from '#/state/shell/selected-feed'
25
28
import {FeedPage} from '#/view/com/feeds/FeedPage'
26
29
import {HomeHeader} from '#/view/com/home/HomeHeader'
27
27
-
import {Pager, PagerRef, RenderTabBarFnProps} from '#/view/com/pager/Pager'
30
30
+
import {
31
31
+
Pager,
32
32
+
type PagerRef,
33
33
+
type RenderTabBarFnProps,
34
34
+
} from '#/view/com/pager/Pager'
28
35
import {CustomFeedEmptyState} from '#/view/com/posts/CustomFeedEmptyState'
29
36
import {FollowingEmptyState} from '#/view/com/posts/FollowingEmptyState'
30
37
import {FollowingEndOfFeed} from '#/view/com/posts/FollowingEndOfFeed'
31
38
import {NoFeedsPinned} from '#/screens/Home/NoFeedsPinned'
32
39
import * as Layout from '#/components/Layout'
40
40
+
import {useDemoMode} from '#/storage/hooks/demo-mode'
33
41
34
42
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home' | 'Start'>
35
43
export function HomeScreen(props: Props) {
···
184
192
[setMinimalShellMode],
185
193
)
186
194
195
195
+
const [demoMode] = useDemoMode()
196
196
+
187
197
const renderTabBar = React.useCallback(
188
198
(props: RenderTabBarFnProps) => {
199
199
+
if (demoMode) {
200
200
+
return (
201
201
+
<HomeHeader
202
202
+
key="FEEDS_TAB_BAR"
203
203
+
{...props}
204
204
+
testID="homeScreenFeedTabs"
205
205
+
onPressSelected={onPressSelected}
206
206
+
// @ts-ignore
207
207
+
feeds={[{displayName: 'Following'}, {displayName: 'Discover'}]}
208
208
+
/>
209
209
+
)
210
210
+
}
189
211
return (
190
212
<HomeHeader
191
213
key="FEEDS_TAB_BAR"
···
196
218
/>
197
219
)
198
220
},
199
199
-
[onPressSelected, pinnedFeedInfos],
221
221
+
[onPressSelected, pinnedFeedInfos, demoMode],
200
222
)
201
223
202
224
const renderFollowingEmptyState = React.useCallback(() => {
···
217
239
: [],
218
240
}
219
241
}, [preferences])
242
242
+
243
243
+
if (demoMode) {
244
244
+
return (
245
245
+
<Pager
246
246
+
ref={pagerRef}
247
247
+
testID="homeScreen"
248
248
+
onPageSelected={onPageSelected}
249
249
+
onPageScrollStateChanged={onPageScrollStateChanged}
250
250
+
renderTabBar={renderTabBar}
251
251
+
initialPage={selectedIndex}>
252
252
+
<FeedPage
253
253
+
testID="demoFeedPage"
254
254
+
isPageFocused
255
255
+
isPageAdjacent={false}
256
256
+
feed="demo"
257
257
+
renderEmptyState={renderCustomFeedEmptyState}
258
258
+
feedInfo={pinnedFeedInfos[0]}
259
259
+
/>
260
260
+
<FeedPage
261
261
+
testID="customFeedPage"
262
262
+
isPageFocused
263
263
+
isPageAdjacent={false}
264
264
+
feed={`feedgen|${PROD_DEFAULT_FEED('whats-hot')}`}
265
265
+
renderEmptyState={renderCustomFeedEmptyState}
266
266
+
feedInfo={pinnedFeedInfos[0]}
267
267
+
/>
268
268
+
</Pager>
269
269
+
)
270
270
+
}
220
271
221
272
return hasSession ? (
222
273
<Pager
+9
-5
src/view/shell/bottom-bar/BottomBar.tsx
···
1
1
-
import React, {ComponentProps} from 'react'
2
2
-
import {GestureResponderEvent, View} from 'react-native'
1
1
+
import React, {type ComponentProps} from 'react'
2
2
+
import {type GestureResponderEvent, View} from 'react-native'
3
3
import Animated from 'react-native-reanimated'
4
4
import {useSafeAreaInsets} from 'react-native-safe-area-context'
5
5
import {msg, plural, Trans} from '@lingui/macro'
6
6
import {useLingui} from '@lingui/react'
7
7
-
import {BottomTabBarProps} from '@react-navigation/bottom-tabs'
7
7
+
import {type BottomTabBarProps} from '@react-navigation/bottom-tabs'
8
8
import {StackActions} from '@react-navigation/native'
9
9
10
10
import {PressableScale} from '#/lib/custom-animations/PressableScale'
11
11
+
import {BOTTOM_BAR_AVI} from '#/lib/demo'
11
12
import {useHaptics} from '#/lib/haptics'
12
13
import {useDedupe} from '#/lib/hooks/useDedupe'
13
14
import {useMinimalShellFooterTransform} from '#/lib/hooks/useMinimalShellTransform'
···
47
48
Message_Stroke2_Corner0_Rounded as Message,
48
49
Message_Stroke2_Corner0_Rounded_Filled as MessageFilled,
49
50
} from '#/components/icons/Message'
51
51
+
import {useDemoMode} from '#/storage/hooks/demo-mode'
50
52
import {styles} from './BottomBarStyles'
51
53
52
54
type TabOptions =
···
123
125
playHaptic()
124
126
accountSwitchControl.open()
125
127
}, [accountSwitchControl, playHaptic])
128
128
+
129
129
+
const [demoMode] = useDemoMode()
126
130
127
131
return (
128
132
<>
···
259
263
{borderColor: pal.text.color},
260
264
]}>
261
265
<UserAvatar
262
262
-
avatar={profile?.avatar}
266
266
+
avatar={demoMode ? BOTTOM_BAR_AVI : profile?.avatar}
263
267
size={iconWidth - 3}
264
268
// See https://github.com/bluesky-social/social-app/pull/1801:
265
269
usePlainRNImage={true}
···
270
274
<View
271
275
style={[styles.ctrlIcon, pal.text, styles.profileIcon]}>
272
276
<UserAvatar
273
273
-
avatar={profile?.avatar}
277
277
+
avatar={demoMode ? BOTTOM_BAR_AVI : profile?.avatar}
274
278
size={iconWidth - 3}
275
279
// See https://github.com/bluesky-social/social-app/pull/1801:
276
280
usePlainRNImage={true}