Files
bsky-social-app/src/state/queries/postgate/util.ts
T
Samuel Newman 300a50b69a migrate runtime call sites to lex clients and sdk actions
Phase 3 tasks 3-7 (parallel wave): composer/post pipeline on
pdsClient/appviewClient with structural+instance blob guards and a golden
CID fixture test; chat Convo/EventBus/queries on the dedicated chat client;
preferences sugar to SDK actions on the PDS client; remaining state/queries
producers (usePostThread unspecced flip, video scoped-token clients,
notifications, starter packs, lists) to client.call; UI runtime sweep
(AtUri from @atproto/syntax, moderation fns from @bsky.app/sdk/moderation,
SDK RichText, ozone reason tokens, guard rewrites via #/types/bsky).

Intermediate checkpoint (hooks skipped): ~114 typecheck errors remain in
cross-boundary consumer files, resolved by the type-only codemod next.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 18:12:10 +03:00

208 lines
5.2 KiB
TypeScript

import {type $Typed} from '@atproto/lex'
import {AtUri, type AtUriString, toDatetimeString} from '@atproto/syntax'
import {app} from '#/lexicons'
import * as bsky from '#/types/bsky'
export const POSTGATE_COLLECTION = 'app.bsky.feed.postgate'
export function createPostgateRecord(
postgate: Omit<Partial<app.bsky.feed.postgate.Main>, 'post'> & {
post: string
},
): app.bsky.feed.postgate.Main {
return {
$type: POSTGATE_COLLECTION,
createdAt: toDatetimeString(new Date()),
post: postgate.post as AtUriString,
detachedEmbeddingUris: postgate.detachedEmbeddingUris || [],
embeddingRules: postgate.embeddingRules || [],
}
}
export function mergePostgateRecords(
prev: app.bsky.feed.postgate.Main,
next: Partial<app.bsky.feed.postgate.Main>,
) {
const detachedEmbeddingUris = Array.from(
new Set([
...(prev.detachedEmbeddingUris || []),
...(next.detachedEmbeddingUris || []),
]),
)
const embeddingRules = [
...(prev.embeddingRules || []),
...(next.embeddingRules || []),
].filter(
(rule, i, all) => all.findIndex(_rule => _rule.$type === rule.$type) === i,
)
return createPostgateRecord({
post: prev.post,
detachedEmbeddingUris,
embeddingRules,
})
}
export function createEmbedViewDetachedRecord({
uri,
}: {
uri: string
}): $Typed<app.bsky.embed.record.View> {
const record: $Typed<app.bsky.embed.record.ViewDetached> = {
$type: 'app.bsky.embed.record#viewDetached',
uri: uri as AtUriString,
detached: true,
}
return {
$type: 'app.bsky.embed.record#view',
record,
}
}
export function createMaybeDetachedQuoteEmbed({
post,
quote,
quoteUri,
detached,
}:
| {
post: app.bsky.feed.defs.PostView
quote: app.bsky.feed.defs.PostView
quoteUri: undefined
detached: false
}
| {
post: app.bsky.feed.defs.PostView
quote: undefined
quoteUri: string
detached: true
}):
| app.bsky.embed.record.View
| app.bsky.embed.recordWithMedia.View
| undefined {
if (bsky.isType(app.bsky.embed.record.view, post.embed)) {
if (detached) {
return createEmbedViewDetachedRecord({uri: quoteUri})
} else {
return createEmbedRecordView({post: quote})
}
} else if (bsky.isType(app.bsky.embed.recordWithMedia.view, post.embed)) {
if (detached) {
return {
...post.embed,
record: createEmbedViewDetachedRecord({uri: quoteUri}),
}
} else {
return createEmbedRecordWithMediaView({post, quote})
}
}
}
export function createEmbedViewRecordFromPost(
post: app.bsky.feed.defs.PostView,
): $Typed<app.bsky.embed.record.ViewRecord> {
return {
$type: 'app.bsky.embed.record#viewRecord',
uri: post.uri,
cid: post.cid,
author: post.author,
value: post.record,
labels: post.labels,
replyCount: post.replyCount,
repostCount: post.repostCount,
likeCount: post.likeCount,
quoteCount: post.quoteCount,
indexedAt: post.indexedAt,
embeds: post.embed ? [post.embed] : [],
}
}
export function createEmbedRecordView({
post,
}: {
post: app.bsky.feed.defs.PostView
}): app.bsky.embed.record.View {
return {
$type: 'app.bsky.embed.record#view',
record: createEmbedViewRecordFromPost(post),
}
}
export function createEmbedRecordWithMediaView({
post,
quote,
}: {
post: app.bsky.feed.defs.PostView
quote: app.bsky.feed.defs.PostView
}): app.bsky.embed.recordWithMedia.View | undefined {
if (!bsky.isType(app.bsky.embed.recordWithMedia.view, post.embed)) return
return {
...(post.embed || {}),
record: {
record: createEmbedViewRecordFromPost(quote),
},
}
}
export function getMaybeDetachedQuoteEmbed({
viewerDid,
post,
}: {
viewerDid: string
post: app.bsky.feed.defs.PostView
}) {
if (bsky.isType(app.bsky.embed.record.view, post.embed)) {
// detached
if (bsky.isType(app.bsky.embed.record.viewDetached, post.embed.record)) {
const urip = new AtUri(post.embed.record.uri)
return {
embed: post.embed,
uri: urip.toString(),
isOwnedByViewer: urip.host === viewerDid,
isDetached: true,
}
}
// post
if (bsky.isType(app.bsky.embed.record.viewRecord, post.embed.record)) {
const urip = new AtUri(post.embed.record.uri)
return {
embed: post.embed,
uri: urip.toString(),
isOwnedByViewer: urip.host === viewerDid,
isDetached: false,
}
}
} else if (bsky.isType(app.bsky.embed.recordWithMedia.view, post.embed)) {
// detached
if (
bsky.isType(app.bsky.embed.record.viewDetached, post.embed.record.record)
) {
const urip = new AtUri(post.embed.record.record.uri)
return {
embed: post.embed,
uri: urip.toString(),
isOwnedByViewer: urip.host === viewerDid,
isDetached: true,
}
}
// post
if (
bsky.isType(app.bsky.embed.record.viewRecord, post.embed.record.record)
) {
const urip = new AtUri(post.embed.record.record.uri)
return {
embed: post.embed,
uri: urip.toString(),
isOwnedByViewer: urip.host === viewerDid,
isDetached: false,
}
}
}
}
export const embeddingRules = {
disableRule: {$type: 'app.bsky.feed.postgate#disableRule'},
}