Skip to content

Commit

Permalink
Move remaining composer state into reducer (#5623)
Browse files Browse the repository at this point in the history
Co-authored-by: Mary <[email protected]>
Co-authored-by: surfdude29 <[email protected]>
Co-authored-by: Hailey <[email protected]>
  • Loading branch information
4 people authored Oct 8, 2024
1 parent c06040c commit e1ca3ae
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 362 deletions.
45 changes: 17 additions & 28 deletions src/lib/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
AppBskyEmbedRecord,
AppBskyEmbedRecordWithMedia,
AppBskyEmbedVideo,
AppBskyFeedPostgate,
AtUri,
BlobRef,
BskyAgent,
Expand All @@ -17,40 +16,26 @@ import {QueryClient} from '@tanstack/react-query'
import {isNetworkError} from '#/lib/strings/errors'
import {shortenLinks, stripInvalidMentions} from '#/lib/strings/rich-text-manip'
import {logger} from '#/logger'
import {ComposerImage, compressImage} from '#/state/gallery'
import {compressImage} from '#/state/gallery'
import {writePostgateRecord} from '#/state/queries/postgate'
import {
fetchResolveGifQuery,
fetchResolveLinkQuery,
} from '#/state/queries/resolve-link'
import {
createThreadgateRecord,
ThreadgateAllowUISetting,
threadgateAllowUISettingToAllowRecordValue,
writeThreadgateRecord,
} from '#/state/queries/threadgate'
import {ComposerState, EmbedDraft} from '#/view/com/composer/state/composer'
import {ComposerDraft, EmbedDraft} from '#/view/com/composer/state/composer'
import {createGIFDescription} from '../gif-alt-text'
import {LinkMeta} from '../link-meta/link-meta'
import {uploadBlob} from './upload-blob'

export {uploadBlob}

export interface ExternalEmbedDraft {
uri: string
isLoading: boolean
meta?: LinkMeta
embed?: AppBskyEmbedRecord.Main
localThumb?: ComposerImage
}

interface PostOpts {
composerState: ComposerState // TODO: Not used yet.
rawText: string
draft: ComposerDraft
replyTo?: string
labels?: string[]
threadgate: ThreadgateAllowUISetting[]
postgate: AppBskyFeedPostgate.Record
onStateChange?: (state: string) => void
langs?: string[]
}
Expand All @@ -60,8 +45,12 @@ export async function post(
queryClient: QueryClient,
opts: PostOpts,
) {
const draft = opts.draft
let reply
let rt = new RichText({text: opts.rawText.trimEnd()}, {cleanNewlines: true})
let rt = new RichText(
{text: draft.richtext.text.trimEnd()},
{cleanNewlines: true},
)

opts.onStateChange?.('Processing...')

Expand All @@ -73,7 +62,7 @@ export async function post(
const embed = await resolveEmbed(
agent,
queryClient,
opts.composerState,
draft,
opts.onStateChange,
)

Expand All @@ -98,10 +87,10 @@ export async function post(

// set labels
let labels: ComAtprotoLabelDefs.SelfLabels | undefined
if (opts.labels?.length) {
if (draft.labels.length) {
labels = {
$type: 'com.atproto.label.defs#selfLabels',
values: opts.labels.map(val => ({val})),
values: draft.labels.map(val => ({val})),
}
}

Expand Down Expand Up @@ -135,15 +124,15 @@ export async function post(
}
}

if (opts.threadgate.some(tg => tg.type !== 'everybody')) {
if (draft.threadgate.some(tg => tg.type !== 'everybody')) {
try {
// TODO: this needs to be batch-created with the post!
await writeThreadgateRecord({
agent,
postUri: res.uri,
threadgate: createThreadgateRecord({
post: res.uri,
allow: threadgateAllowUISettingToAllowRecordValue(opts.threadgate),
allow: threadgateAllowUISettingToAllowRecordValue(draft.threadgate),
}),
})
} catch (e: any) {
Expand All @@ -158,16 +147,16 @@ export async function post(
}

if (
opts.postgate.embeddingRules?.length ||
opts.postgate.detachedEmbeddingUris?.length
draft.postgate.embeddingRules?.length ||
draft.postgate.detachedEmbeddingUris?.length
) {
try {
// TODO: this needs to be batch-created with the post!
await writePostgateRecord({
agent,
postUri: res.uri,
postgate: {
...opts.postgate,
...draft.postgate,
post: res.uri,
},
})
Expand All @@ -188,7 +177,7 @@ export async function post(
async function resolveEmbed(
agent: BskyAgent,
queryClient: QueryClient,
draft: ComposerState,
draft: ComposerDraft,
onStateChange: ((state: string) => void) | undefined,
): Promise<
| AppBskyEmbedImages.Main
Expand Down
93 changes: 68 additions & 25 deletions src/lib/api/resolve.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import {AppBskyActorDefs, ComAtprotoRepoStrongRef} from '@atproto/api'
import {
AppBskyActorDefs,
AppBskyFeedPost,
AppBskyGraphStarterpack,
ComAtprotoRepoStrongRef,
} from '@atproto/api'
import {AtUri} from '@atproto/api'
import {BskyAgent} from '@atproto/api'

import {POST_IMG_MAX} from '#/lib/constants'
import {
getFeedAsEmbed,
getListAsEmbed,
getPostAsQuote,
getStarterPackAsEmbed,
} from '#/lib/link-meta/bsky'
import {getLinkMeta} from '#/lib/link-meta/link-meta'
import {resolveShortLink} from '#/lib/link-meta/resolve-short-link'
import {downloadAndResize} from '#/lib/media/manip'
import {
createStarterPackUri,
parseStarterPackUri,
} from '#/lib/strings/starter-pack'
import {
isBskyCustomFeedUrl,
isBskyListUrl,
Expand All @@ -24,6 +27,7 @@ import {ComposerImage} from '#/state/gallery'
import {createComposerImage} from '#/state/gallery'
import {Gif} from '#/state/queries/tenor'
import {createGIFDescription} from '../gif-alt-text'
import {convertBskyAppUrlIfNeeded, makeRecordUri} from '../strings/url-helpers'

type ResolvedExternalLink = {
type: 'external'
Expand Down Expand Up @@ -60,6 +64,12 @@ export type ResolvedLink =
| ResolvedPostRecord
| ResolvedOtherRecord

class EmbeddingDisabledError extends Error {
constructor() {
super('Embedding is disabled for this record')
}
}

export async function resolveLink(
agent: BskyAgent,
uri: string,
Expand All @@ -68,55 +78,88 @@ export async function resolveLink(
uri = await resolveShortLink(uri)
}
if (isBskyPostUrl(uri)) {
// TODO: Remove this abstraction.
// TODO: Nice error messages (e.g. EmbeddingDisabledError).
const result = await getPostAsQuote(getPost, uri)
uri = convertBskyAppUrlIfNeeded(uri)
const [_0, user, _1, rkey] = uri.split('/').filter(Boolean)
const recordUri = makeRecordUri(user, 'app.bsky.feed.post', rkey)
const post = await getPost({uri: recordUri})
if (post.viewer?.embeddingDisabled) {
throw new EmbeddingDisabledError()
}
return {
type: 'record',
record: {
cid: result.cid,
uri: result.uri,
cid: post.cid,
uri: post.uri,
},
kind: 'post',
meta: result,
meta: {
text: AppBskyFeedPost.isRecord(post.record) ? post.record.text : '',
indexedAt: post.indexedAt,
author: post.author,
},
}
}
if (isBskyCustomFeedUrl(uri)) {
// TODO: Remove this abstraction.
const result = await getFeedAsEmbed(agent, fetchDid, uri)
uri = convertBskyAppUrlIfNeeded(uri)
const [_0, handleOrDid, _1, rkey] = uri.split('/').filter(Boolean)
const did = await fetchDid(handleOrDid)
const feed = makeRecordUri(did, 'app.bsky.feed.generator', rkey)
const res = await agent.app.bsky.feed.getFeedGenerator({feed})
return {
type: 'record',
record: result.embed!.record,
record: {
uri: res.data.view.uri,
cid: res.data.view.cid,
},
kind: 'other',
meta: {
// TODO: Include hydrated content instead.
title: result.meta!.title!,
title: res.data.view.displayName,
},
}
}
if (isBskyListUrl(uri)) {
// TODO: Remove this abstraction.
const result = await getListAsEmbed(agent, fetchDid, uri)
uri = convertBskyAppUrlIfNeeded(uri)
const [_0, handleOrDid, _1, rkey] = uri.split('/').filter(Boolean)
const did = await fetchDid(handleOrDid)
const list = makeRecordUri(did, 'app.bsky.graph.list', rkey)
const res = await agent.app.bsky.graph.getList({list})
return {
type: 'record',
record: result.embed!.record,
record: {
uri: res.data.list.uri,
cid: res.data.list.cid,
},
kind: 'other',
meta: {
// TODO: Include hydrated content instead.
title: result.meta!.title!,
title: res.data.list.name,
},
}
}
if (isBskyStartUrl(uri) || isBskyStarterPackUrl(uri)) {
// TODO: Remove this abstraction.
const result = await getStarterPackAsEmbed(agent, fetchDid, uri)
const parsed = parseStarterPackUri(uri)
if (!parsed) {
throw new Error(
'Unexpectedly called getStarterPackAsEmbed with a non-starterpack url',
)
}
const did = await fetchDid(parsed.name)
const starterPack = createStarterPackUri({did, rkey: parsed.rkey})
const res = await agent.app.bsky.graph.getStarterPack({starterPack})
const record = res.data.starterPack.record
return {
type: 'record',
record: result.embed!.record,
record: {
uri: res.data.starterPack.uri,
cid: res.data.starterPack.cid,
},
kind: 'other',
meta: {
// TODO: Include hydrated content instead.
title: result.meta!.title!,
title: AppBskyGraphStarterpack.isRecord(record)
? record.name
: 'Starter Pack',
},
}
}
Expand Down
Loading

0 comments on commit e1ca3ae

Please sign in to comment.