community.gifthood.feed.defs
Schema Diff
+5 -0
Compatibility Analysis
Backward Compatible
Backward compatible. 4 non-breaking changes.
Non-Breaking Changes (4)
- AddedVertex AddedVertex { vertex_id: "community.gifthood.feed.defs#imageView.aspectRatio" }
- AddedVertex AddedVertex { vertex_id: "community.gifthood.listing#aspectRatio" }
- AddedEdge AddedEdge { src: "community.gifthood.feed.defs#imageView", tgt: "community.gifthood.feed.defs#imageView.aspectRatio", kind: "prop", name: Some("aspectRatio") }
- AddedEdge AddedEdge { src: "community.gifthood.feed.defs#imageView.aspectRatio", tgt: "community.gifthood.listing#aspectRatio", kind: "ref", name: None }
Migration Guidance
Added Elements
AddedVertex { vertex_id: "community.gifthood.feed.defs#imageView.aspectRatio" }AddedVertex { vertex_id: "community.gifthood.listing#aspectRatio" }
Additional Notes
- Non-breaking: AddedEdge { src: "community.gifthood.feed.defs#imageView", tgt: "community.gifthood.feed.defs#imageView.aspectRatio", kind: "prop", name: Some("aspectRatio") }
- Non-breaking: AddedEdge { src: "community.gifthood.feed.defs#imageView.aspectRatio", tgt: "community.gifthood.listing#aspectRatio", kind: "ref", name: None }
1
1
{
2
2
"id": "community.gifthood.feed.defs",
3
3
"defs": {
4
4
"imageView": {
5
5
"type": "object",
6
6
"required": [
7
7
"cid"
8
8
],
9
9
"properties": {
10
10
"alt": {
11
11
"type": "string",
12
12
"description": "Alt text for the image. Absent when no alt text was provided."
13
13
},
14
14
"cid": {
15
15
"type": "string",
16
16
"format": "cid",
17
17
"description": "CID of the image blob in the poster's repo."
18
+
},
19
+
"aspectRatio": {
20
+
"ref": "community.gifthood.listing#aspectRatio",
21
+
"type": "ref",
22
+
"description": "Aspect ratio from the listing record, passed through unchanged. Absent when the poster's client did not record one."
18
23
}
19
24
},
20
25
"description": "A single image blob associated with a listing."
21
26
},
22
27
"replyView": {
23
28
"type": "object",
24
29
"required": [
25
30
"uri",
26
31
"cid",
27
32
"author",
28
33
"text",
29
34
"createdAt"
30
35
],
31
36
"properties": {
32
37
"cid": {
33
38
"type": "string",
34
39
"format": "cid",
35
40
"description": "CID of the reply record."
36
41
},
37
42
"uri": {
38
43
"type": "string",
39
44
"format": "at-uri",
40
45
"description": "AT-URI of the reply record."
41
46
},
42
47
"text": {
43
48
"type": "string",
44
49
"description": "The note text."
45
50
},
46
51
"author": {
47
52
"ref": "#replyAuthorView",
48
53
"type": "ref",
49
54
"description": "The reply author's resolved identity."
50
55
},
51
56
"facets": {
52
57
"type": "array",
53
58
"items": {
54
59
"ref": "app.bsky.richtext.facet",
55
60
"type": "ref"
56
61
},
57
62
"description": "Rich-text annotations (links, mentions) over the text. Absent when none."
58
63
},
59
64
"hidden": {
60
65
"type": "boolean",
61
66
"description": "True when the listing owner's replygate hides this note from default views. Present only in owner (includeHidden) reads — otherwise hidden rows are omitted, not flagged."
62
67
},
63
68
"createdAt": {
64
69
"type": "string",
65
70
"format": "datetime",
66
71
"description": "When the reply was created (client-set)."
67
72
}
68
73
},
69
74
"description": "A public note on a listing, flattened with the author's resolved identity. Mirrors the ListingReplyRow db row (getRepliesForListing)."
70
75
},
71
76
"listingView": {
72
77
"type": "object",
73
78
"required": [
74
79
"did",
75
80
"rkey",
76
81
"title",
77
82
"intent",
78
83
"createdAt",
79
84
"category",
80
85
"adult",
81
86
"geohash"
82
87
],
83
88
"properties": {
84
89
"did": {
85
90
"type": "string",
86
91
"format": "did",
87
92
"description": "DID of the account that created the listing."
88
93
},
89
94
"out": {
90
95
"type": "boolean",
91
96
"description": "Derived: a lend listing whose item is currently out on loan (an exchange completed but not yet returned). Absent means not out."
92
97
},
93
98
"mode": {
94
99
"type": "string",
95
100
"description": "How the item changes hands: 'gift' or 'lend'. Open set (knownValues); absent means gift.",
96
101
"knownValues": [
97
102
"gift",
98
103
"lend"
99
104
]
100
105
},
101
106
"rkey": {
102
107
"type": "string",
103
108
"description": "Record key (TID) of the listing record in the poster's repo."
104
109
},
105
110
"adult": {
106
111
"type": "boolean",
107
112
"description": "True if the listing is flagged as adult/mature content (self-label or moderator label)."
108
113
},
109
114
"title": {
110
115
"type": "string",
111
116
"description": "Short title of the item being offered or requested."
112
117
},
113
118
"intent": {
114
119
"enum": [
115
120
"offer",
116
121
"request"
117
122
],
118
123
"type": "string",
119
124
"description": "Whether the poster is giving away an item or looking for one."
120
125
},
121
126
"paused": {
122
127
"type": "boolean",
123
128
"description": "Owner paused lending — hidden from feeds, interest disabled. Absent means active."
124
129
},
125
130
"status": {
126
131
"enum": [
127
132
"available",
128
133
"pending",
129
134
"gone"
130
135
],
131
136
"type": "string",
132
137
"description": "Current availability of the item. Absent means 'available'."
133
138
},
134
139
"geohash": {
135
140
"type": "string",
136
141
"description": "Approximate location as a geohash. Always at precision-6 (the ~1.2 km privacy floor). Used for map marker placement."
137
142
},
138
143
"category": {
139
144
"type": "string",
140
145
"description": "Single-level category for feed filtering. Unknown or missing values are indexed as 'other'. Not enum-restricted here to remain forward-compatible with new category values."
141
146
},
142
147
"imageAlt": {
143
148
"type": "string",
144
149
"description": "Alt text for the first image, parallel to imageCid. Absent when there is no first image or no alt text was provided."
145
150
},
146
151
"imageCid": {
147
152
"type": "string",
148
153
"description": "CID of the first image blob in the poster's repo. Used to construct the image proxy URL. Absent when the listing has no images."
149
154
},
150
155
"standing": {
151
156
"type": "boolean",
152
157
"description": "Standing (renewing) offer — exchanges never drive status; clients may label availability as ongoing."
153
158
},
154
159
"createdAt": {
155
160
"type": "string",
156
161
"format": "datetime",
157
162
"description": "ISO 8601 datetime when the listing was created. Clients derive relative-time display from this value."
158
163
},
159
164
"distanceKm": {
160
165
"type": "integer",
161
166
"description": "Coarse cell-center-to-cell-center distance in kilometres from the query geohash, rounded to the nearest km. Absent when no search centre is available."
162
167
},
163
168
"moderation": {
164
169
"ref": "#moderationView",
165
170
"type": "ref",
166
171
"description": "Active soft moderation signal for the listing, if any. Absent when the listing has no active moderation."
167
172
},
168
173
"replyCount": {
169
174
"type": "integer",
170
175
"description": "Visible notes on this listing. Derived by the AppView; absent on cards that don't compute it."
171
176
},
172
177
"locationName": {
173
178
"type": "string",
174
179
"description": "Human-readable area name derived from reverse geocoding the geohash. Display only; must not be used for proximity matching."
175
180
}
176
181
},
177
182
"description": "A flattened, display-ready view of a single listing as returned by the AppView feed."
178
183
},
179
184
"viewerState": {
180
185
"type": "object",
181
186
"properties": {
182
187
"interest": {
183
188
"type": "string",
184
189
"format": "at-uri",
185
190
"description": "AT-URI of the viewer's own interest record on this listing, present when they have raised their hand. Absent otherwise. Follows the app.bsky viewerState.like as-string pattern."
186
191
}
187
192
},
188
193
"description": "Metadata about the requesting account's relationship to this listing. Empty for logged-out viewers and viewers with no interaction."
189
194
},
190
195
"activityView": {
191
196
"type": "object",
192
197
"required": [
193
198
"kind",
194
199
"createdAt"
195
200
],
196
201
"properties": {
197
202
"kind": {
198
203
"type": "string",
199
204
"description": "Row kind — 'interest' = the viewer raised their hand on someone's listing (outgoing); 'hand' = a neighbor raised their hand on the viewer's listing (incoming). Open set (knownValues, not a closed enum): future kinds extend without a breaking change. 'follow' = a neighbor followed the viewer (incoming social signal; carries 'followers' instead of 'listing'). Exchange kinds (all carry 'listing' + 'neighbor' = the other party): 'exchangeChosen'/'exchangeCompleted' for the counterparty, 'exchangeConfirmed'/'exchangeDeclined' for the listing owner. 'exchangeReturned' = the owner marked the item the viewer borrowed as returned (incoming). 'reply' = a neighbor left a note on the viewer's listing (incoming; carries 'listing' + 'neighbor' + 'note'; hidden notes and the note author's own listing never produce a row).",
200
205
"knownValues": [
201
206
"interest",
202
207
"hand",
203
208
"follow",
204
209
"exchangeChosen",
205
210
"exchangeCompleted",
206
211
"exchangeConfirmed",
207
212
"exchangeDeclined",
208
213
"exchangeReturned",
209
214
"reply"
210
215
]
211
216
},
212
217
"note": {
213
218
"type": "string",
214
219
"description": "The interest's note/message — the viewer's own on an 'interest' row, the neighbor's on a 'hand' row (the same underlying interests.message field), or the note's text on a 'reply' row (replies.text). Absent when none left."
215
220
},
216
221
"listing": {
217
222
"ref": "#listingView",
218
223
"type": "ref",
219
224
"description": "The listing this activity is about (full listingView; carries the moderation signal the outgoing-row gate reads). Absent on 'follow' rows."
220
225
},
221
226
"neighbor": {
222
227
"ref": "community.gifthood.actor.defs#profileViewBasic",
223
228
"type": "ref",
224
229
"description": "The other member this row is about. Present on 'hand' rows (the neighbor who raised their hand), 'reply' rows (the note's author), and on exchange rows (the other party of the exchange); absent on 'interest' rows."
225
230
},
226
231
"createdAt": {
227
232
"type": "string",
228
233
"format": "datetime",
229
234
"description": "When this activity event was created — the newest-first merge/sort key across all activity kinds (interest, hand, follow)."
230
235
},
231
236
"followers": {
232
237
"type": "array",
233
238
"items": {
234
239
"ref": "community.gifthood.actor.defs#profileView",
235
240
"type": "ref"
236
241
},
237
242
"description": "The neighbor(s) who followed the viewer. Present only on 'follow' rows; followers[0] is the primary actor, length > 1 is a grouped card. Each row carries its own viewer state (the inline follow-back button reads viewer.following)."
238
243
},
239
244
"isFollowBack": {
240
245
"type": "boolean",
241
246
"description": "Order-sensitive reciprocity: true when the viewer followed this actor first and they reciprocated. Server-computed; meaningful only on single (ungrouped) 'follow' rows."
242
247
}
243
248
},
244
249
"description": "One row of the viewer's activity feed: an outgoing interest the viewer raised on someone's listing, an incoming raised hand on the viewer's own listing, or a neighbor who followed the viewer (carries 'followers' instead of 'listing')."
245
250
},
246
251
"exchangeView": {
247
252
"type": "object",
248
253
"required": [
249
254
"uri",
250
255
"cid",
251
256
"listingUri",
252
257
"giverDid",
253
258
"receiverDid",
254
259
"counterparty",
255
260
"state",
256
261
"createdAt"
257
262
],
258
263
"properties": {
259
264
"cid": {
260
265
"type": "string",
261
266
"format": "cid",
262
267
"description": "CID of the exchange record."
263
268
},
264
269
"uri": {
265
270
"type": "string",
266
271
"format": "at-uri",
267
272
"description": "AT-URI of the exchange record."
268
273
},
269
274
"state": {
270
275
"type": "string",
271
276
"description": "Derived exchange state. Open set (knownValues, not a closed enum).",
272
277
"knownValues": [
273
278
"pending",
274
279
"completed",
275
280
"confirmed",
276
281
"declined"
277
282
]
278
283
},
279
284
"giverDid": {
280
285
"type": "string",
281
286
"format": "did",
282
287
"description": "DID of the party giving the item."
283
288
},
284
289
"createdAt": {
285
290
"type": "string",
286
291
"format": "datetime",
287
292
"description": "When the exchange record was created."
288
293
},
289
294
"listingUri": {
290
295
"type": "string",
291
296
"format": "at-uri",
292
297
"description": "AT-URI of the listing this exchange is for."
293
298
},
294
299
"returnedAt": {
295
300
"type": "string",
296
301
"format": "datetime",
297
302
"description": "When the owner marked the lent item returned. Absent until then; only present on lend-listing exchanges."
298
303
},
299
304
"completedAt": {
300
305
"type": "string",
301
306
"format": "datetime",
302
307
"description": "When the giver marked the gift complete — the item handed over or the service rendered. Absent until then."
303
308
},
304
309
"receiverDid": {
305
310
"type": "string",
306
311
"format": "did",
307
312
"description": "DID of the party receiving the item."
308
313
},
309
314
"respondedAt": {
310
315
"type": "string",
311
316
"format": "datetime",
312
317
"description": "When the counterparty confirmed or declined. Absent until they respond."
313
318
},
314
319
"counterparty": {
315
320
"ref": "#exchangeCounterpartyView",
316
321
"type": "ref",
317
322
"description": "The non-listing-owner party's resolved identity."
318
323
}
319
324
},
320
325
"description": "The active exchange on a listing, flattened with the counterparty's resolved identity."
321
326
},
322
327
"interestView": {
323
328
"type": "object",
324
329
"required": [
325
330
"did",
326
331
"uri",
327
332
"cid",
328
333
"createdAt"
329
334
],
330
335
"properties": {
331
336
"cid": {
332
337
"type": "string",
333
338
"format": "cid",
334
339
"description": "CID of the interest record (used for messaging/report)."
335
340
},
336
341
"did": {
337
342
"type": "string",
338
343
"format": "did",
339
344
"description": "DID of the interested person."
340
345
},
341
346
"uri": {
342
347
"type": "string",
343
348
"format": "at-uri",
344
349
"description": "AT-URI of the interest record."
345
350
},
346
351
"note": {
347
352
"type": "string",
348
353
"description": "The interest's note/message. Absent when none left."
349
354
},
350
355
"avatar": {
351
356
"type": "string",
352
357
"description": "Avatar image URL (proxied). Absent when none."
353
358
},
354
359
"handle": {
355
360
"type": "string",
356
361
"description": "Live-resolved handle (without leading @). Absent when unresolvable."
357
362
},
358
363
"createdAt": {
359
364
"type": "string",
360
365
"format": "datetime",
361
366
"description": "When the interest was created (client-set; the row order key)."
362
367
},
363
368
"noteFacets": {
364
369
"type": "array",
365
370
"items": {
366
371
"ref": "app.bsky.richtext.facet",
367
372
"type": "ref"
368
373
},
369
374
"description": "Rich-text facets (links + mentions) over the note. Absent when none."
370
375
},
371
376
"displayName": {
372
377
"type": "string",
373
378
"description": "Real display name (nullable — the row shows name + @handle separately). Absent when none set."
374
379
}
375
380
},
376
381
"description": "An interest ('raised hand') on a listing, flattened with the interested person's resolved identity (real displayName + handle + avatar). Mirrors the InterestedPerson row."
377
382
},
378
383
"moderationView": {
379
384
"type": "object",
380
385
"required": [
381
386
"tier",
382
387
"reason"
383
388
],
384
389
"properties": {
385
390
"tier": {
386
391
"enum": [
387
392
"warn",
388
393
"hide"
389
394
],
390
395
"type": "string",
391
396
"description": "Severity tier. 'warn' shows a content warning over the card; 'hide' collapses the card to a quiet strip behind an expand affordance. Both tiers keep the listing in the feed response — only hard takedowns and blocked authors are omitted."
392
397
},
393
398
"reason": {
394
399
"type": "string",
395
400
"description": "Human-readable reason for the moderation action."
396
401
}
397
402
},
398
403
"description": "Active soft moderation signal applied to a listing (warn or hide tier)."
399
404
},
400
405
"replyAuthorView": {
401
406
"type": "object",
402
407
"required": [
403
408
"did"
404
409
],
405
410
"properties": {
406
411
"did": {
407
412
"type": "string",
408
413
"format": "did",
409
414
"description": "DID of the reply's author."
410
415
},
411
416
"avatar": {
412
417
"type": "string",
413
418
"description": "Avatar image URL (proxied). Absent when none."
414
419
},
415
420
"handle": {
416
421
"type": "string",
417
422
"format": "handle",
418
423
"description": "Live-resolved handle (without leading @). Absent when unresolvable."
419
424
},
420
425
"displayName": {
421
426
"type": "string",
422
427
"maxLength": 640,
423
428
"description": "Real display name. Absent when none set.",
424
429
"maxGraphemes": 64
425
430
}
426
431
},
427
432
"description": "A reply author's resolved identity — sibling def to exchangeCounterpartyView (inline object property types are illegal in this lexicon toolchain)."
428
433
},
429
434
"listingDetailView": {
430
435
"type": "object",
431
436
"required": [
432
437
"listing",
433
438
"cid",
434
439
"images"
435
440
],
436
441
"properties": {
437
442
"cid": {
438
443
"type": "string",
439
444
"format": "cid",
440
445
"description": "CID of the listing record in the poster's repo."
441
446
},
442
447
"langs": {
443
448
"type": "array",
444
449
"items": {
445
450
"type": "string",
446
451
"format": "language"
447
452
},
448
453
"description": "BCP-47 language tags for the listing's text content. Absent when unspecified."
449
454
},
450
455
"facets": {
451
456
"type": "array",
452
457
"items": {
453
458
"ref": "app.bsky.richtext.facet",
454
459
"type": "ref"
455
460
},
456
461
"description": "Rich-text annotations (links, mentions, tags) over the description. Absent when no facets are present."
457
462
},
458
463
"images": {
459
464
"type": "array",
460
465
"items": {
461
466
"ref": "#imageView",
462
467
"type": "ref"
463
468
},
464
469
"description": "All images associated with the listing. May be empty."
465
470
},
466
471
"viewer": {
467
472
"ref": "#viewerState",
468
473
"type": "ref",
469
474
"description": "The requesting account's viewer-specific state for this listing (e.g. their own interest). Absent for logged-out viewers."
470
475
},
471
476
"listing": {
472
477
"ref": "#listingView",
473
478
"type": "ref",
474
479
"description": "The summary listing view."
475
480
},
476
481
"description": {
477
482
"type": "string",
478
483
"description": "Full description of the item, in plain text. Absent when the poster provided no description."
479
484
}
480
485
},
481
486
"description": "Full detail view of a single listing, including all images, rich-text facets, and viewer state."
482
487
},
483
488
"exchangeCounterpartyView": {
484
489
"type": "object",
485
490
"required": [
486
491
"did"
487
492
],
488
493
"properties": {
489
494
"did": {
490
495
"type": "string",
491
496
"format": "did",
492
497
"description": "DID of the counterparty."
493
498
},
494
499
"handle": {
495
500
"type": "string",
496
501
"format": "handle",
497
502
"description": "Live-resolved handle (without leading @). Absent when unresolvable."
498
503
},
499
504
"displayName": {
500
505
"type": "string",
501
506
"maxLength": 640,
502
507
"description": "Real display name. Absent when none set.",
503
508
"maxGraphemes": 64
504
509
}
505
510
},
506
511
"description": "The non-listing-owner exchange party's resolved identity."
507
512
}
508
513
},
509
514
"$type": "com.atproto.lexicon.schema",
510
515
"lexicon": 1
511
516
}