[SDK] Adopt @bsky.app/sdk and migrate RichText (#11369)
This commit is contained in:
+2
-1
@@ -115,6 +115,7 @@
|
||||
"@bsky.app/expo-translate-text": "^0.2.9",
|
||||
"@bsky.app/peek-menu": "^0.3.2",
|
||||
"@bsky.app/react-native-uitextview": "^2.7.0",
|
||||
"@bsky.app/sdk": "npm:bsky-sdk-preview@0.0.1-preview.2",
|
||||
"@bsky.app/sift": "^0.3.9",
|
||||
"@bsky.app/tapper": "^0.6.1",
|
||||
"@bsky.app/video": "0.3.6",
|
||||
@@ -339,7 +340,7 @@
|
||||
"^await-lock$": "<rootDir>/node_modules/await-lock/build/AwaitLock.js"
|
||||
},
|
||||
"transformIgnorePatterns": [
|
||||
"node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|nanoid|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|normalize-url|react-native-svg|@sentry/.*|sentry-expo|bcp-47-match|@atproto/.*|multiformats|uint8arrays|@ipld/.*|cborg|await-lock)"
|
||||
"node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|nanoid|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|normalize-url|react-native-svg|@sentry/.*|sentry-expo|bcp-47-match|@atproto/.*|@bsky.app/sdk|tlds|multiformats|uint8arrays|@ipld/.*|cborg|await-lock)"
|
||||
],
|
||||
"modulePathIgnorePatterns": [
|
||||
"__tests__/.*/__mocks__",
|
||||
|
||||
Generated
+27
@@ -287,6 +287,9 @@ importers:
|
||||
'@bsky.app/react-native-uitextview':
|
||||
specifier: ^2.7.0
|
||||
version: 2.7.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)
|
||||
'@bsky.app/sdk':
|
||||
specifier: npm:bsky-sdk-preview@0.0.1-preview.2
|
||||
version: bsky-sdk-preview@0.0.1-preview.2(@atproto/lex@0.3.6)
|
||||
'@bsky.app/sift':
|
||||
specifier: ^0.3.9
|
||||
version: 0.3.9(2d5c14016450c7e55c8cd0dffcca6f69)
|
||||
@@ -889,6 +892,10 @@ packages:
|
||||
resolution: {integrity: sha512-iDFZEoNqfL17EVKE+uNzdUD7YF8LWhEhxeBtP6x+PNuAxoXv3ip9vLmB8nNzNxFwVn++FipfDtSiPqmziv1bdA==}
|
||||
engines: {node: '>=22'}
|
||||
|
||||
'@atproto-labs/handle-resolver@0.4.8':
|
||||
resolution: {integrity: sha512-38I+j8Efs+cCgW/NX9RFKKu7qv/AF+FqxlKS8sWjXlNZSHQZqPj5cnKVQhSsJCs4ypPB84iKch8w4/STd33bLg==}
|
||||
engines: {node: '>=22'}
|
||||
|
||||
'@atproto-labs/pipe@0.2.4':
|
||||
resolution: {integrity: sha512-n67jCcrC+ouAeO10cWkpPzzLMlDi/lDCU30Us+LGqhOPhT6c4t5ASdBLQi9W3jUQtRzQBt3G9zipF+xKWNvVbw==}
|
||||
engines: {node: '>=22'}
|
||||
@@ -4505,6 +4512,12 @@ packages:
|
||||
bser@2.1.1:
|
||||
resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
|
||||
|
||||
bsky-sdk-preview@0.0.1-preview.2:
|
||||
resolution: {integrity: sha512-LOcuvzc6UASiI5os1BUIo9dxAeVPIFiplcVSYbaDCgpH+tkpv2mqSqqsCRTP0g2CaVCwozqaTVy+jnsTO6/9BQ==}
|
||||
engines: {node: '>=22.12.0'}
|
||||
peerDependencies:
|
||||
'@atproto/lex': ^0.3.0
|
||||
|
||||
buffer-crc32@0.2.13:
|
||||
resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
|
||||
|
||||
@@ -9725,6 +9738,13 @@ snapshots:
|
||||
dependencies:
|
||||
'@atproto-labs/pipe': 0.2.4
|
||||
|
||||
'@atproto-labs/handle-resolver@0.4.8':
|
||||
dependencies:
|
||||
'@atproto-labs/simple-store': 0.5.1
|
||||
'@atproto-labs/simple-store-memory': 0.2.6
|
||||
'@atproto/did': 0.5.4
|
||||
zod: 3.25.76
|
||||
|
||||
'@atproto-labs/pipe@0.2.4': {}
|
||||
|
||||
'@atproto-labs/simple-store-memory@0.2.6':
|
||||
@@ -14063,6 +14083,13 @@ snapshots:
|
||||
dependencies:
|
||||
node-int64: 0.4.0
|
||||
|
||||
bsky-sdk-preview@0.0.1-preview.2(@atproto/lex@0.3.6):
|
||||
dependencies:
|
||||
'@atproto-labs/handle-resolver': 0.4.8
|
||||
'@atproto/lex': 0.3.6
|
||||
'@atproto/syntax': 0.7.4
|
||||
tlds: 1.261.0
|
||||
|
||||
buffer-crc32@0.2.13: {}
|
||||
|
||||
buffer-from@1.1.2: {}
|
||||
|
||||
@@ -44,6 +44,7 @@ patchedDependencies:
|
||||
'react-native@0.81.5': patches/react-native@0.81.5.patch
|
||||
minimumReleaseAgeExclude:
|
||||
- '@atproto/*'
|
||||
- '@atproto-labs/*'
|
||||
- '@bsky.app/*'
|
||||
# todo: remove when old enough
|
||||
- '@oxlint-tsgolint/darwin-arm64@7.0.2001'
|
||||
@@ -53,3 +54,4 @@ minimumReleaseAgeExclude:
|
||||
- '@oxlint-tsgolint/win32-arm64@7.0.2001'
|
||||
- '@oxlint-tsgolint/win32-x64@7.0.2001'
|
||||
- oxlint-tsgolint@7.0.2001
|
||||
- bsky-sdk-preview@0.0.1-preview.2
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import {useCallback, useEffect, useMemo} from 'react'
|
||||
import {type GestureResponderEvent, View} from 'react-native'
|
||||
import {
|
||||
type AppBskyFeedDefs,
|
||||
type AppBskyGraphDefs,
|
||||
AtUri,
|
||||
RichText as RichTextApi,
|
||||
} from '@atproto/api'
|
||||
import {type AppBskyFeedDefs, type AppBskyGraphDefs, AtUri} from '@atproto/api'
|
||||
import {RichText as RichTextApi} from '@bsky.app/sdk/richtext'
|
||||
import {Plural, Trans, useLingui} from '@lingui/react/macro'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
|
||||
@@ -6,12 +6,13 @@ import {
|
||||
AppBskyFeedPost,
|
||||
AtUri,
|
||||
moderatePost,
|
||||
RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {asSdkFacets} from '#/lib/strings/rich-text-helpers'
|
||||
import {getChatInviteCodeFromUrl} from '#/lib/strings/url-helpers'
|
||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
import {unstableCacheProfileView} from '#/state/queries/profile'
|
||||
@@ -295,7 +296,7 @@ export function QuoteEmbed({
|
||||
return undefined
|
||||
const {text, facets} = quote.record
|
||||
return text.trim()
|
||||
? new RichTextAPI({text: text, facets: facets})
|
||||
? new RichTextAPI({text: text, facets: asSdkFacets(facets)})
|
||||
: undefined
|
||||
}, [quote.record])
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ import {
|
||||
type AppBskyFeedPost,
|
||||
type AppBskyFeedThreadgate,
|
||||
AtUri,
|
||||
type RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {type RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {plural} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
|
||||
@@ -4,8 +4,8 @@ import {
|
||||
type AppBskyFeedDefs,
|
||||
type AppBskyFeedPost,
|
||||
type AppBskyFeedThreadgate,
|
||||
type RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {type RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {type Shadow} from '#/state/cache/post-shadow'
|
||||
|
||||
@@ -3,8 +3,8 @@ import {
|
||||
type AppBskyFeedDefs,
|
||||
type AppBskyFeedPost,
|
||||
type AppBskyFeedThreadgate,
|
||||
type RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {type RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
|
||||
import {type Shadow} from '#/state/cache/post-shadow'
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
type AppBskyFeedPost,
|
||||
type AppBskyFeedThreadgate,
|
||||
AtUri,
|
||||
type RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {type RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ import {
|
||||
type AppBskyFeedDefs,
|
||||
type AppBskyFeedPost,
|
||||
type AppBskyFeedThreadgate,
|
||||
type RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {type RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {plural} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
|
||||
|
||||
@@ -6,11 +6,8 @@ import {
|
||||
View,
|
||||
type ViewStyle,
|
||||
} from 'react-native'
|
||||
import {
|
||||
moderateProfile,
|
||||
type ModerationOpts,
|
||||
RichText as RichTextApi,
|
||||
} from '@atproto/api'
|
||||
import {moderateProfile, type ModerationOpts} from '@atproto/api'
|
||||
import {RichText as RichTextApi} from '@bsky.app/sdk/richtext'
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {getModerationCauseKey} from '#/lib/moderation'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {useMemo} from 'react'
|
||||
import {type StyleProp, type TextStyle} from 'react-native'
|
||||
import {AppBskyRichtextFacet, RichText as RichTextAPI} from '@atproto/api'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
|
||||
import {toShortUrl} from '#/lib/strings/url-helpers'
|
||||
import {android, atoms as a, flatten, type TextStyleProp} from '#/alf'
|
||||
@@ -9,6 +9,8 @@ import {InlineLinkText, type LinkProps} from '#/components/Link'
|
||||
import {ProfileHoverCard} from '#/components/ProfileHoverCard'
|
||||
import {RichTextTag} from '#/components/RichTextTag'
|
||||
import {Text, type TextProps} from '#/components/Typography'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
|
||||
const WORD_WRAP = {wordWrap: 1}
|
||||
// lifted from facet detection in `RichText` impl, _without_ `gm` flags
|
||||
@@ -139,7 +141,7 @@ export function RichText({
|
||||
if (
|
||||
mention &&
|
||||
(disableMentionFacetValidation ||
|
||||
AppBskyRichtextFacet.validateMention(mention).success) &&
|
||||
bsky.matches(app.bsky.richtext.facet.mention, mention)) &&
|
||||
!disableLinks
|
||||
) {
|
||||
els.push(
|
||||
@@ -156,7 +158,7 @@ export function RichText({
|
||||
</InlineLinkText>
|
||||
</ProfileHoverCard>,
|
||||
)
|
||||
} else if (link && AppBskyRichtextFacet.validateLink(link).success) {
|
||||
} else if (link && bsky.matches(app.bsky.richtext.facet.link, link)) {
|
||||
const isValidLink = URL_REGEX.test(link.uri)
|
||||
if (!isValidLink || disableLinks) {
|
||||
els.push(toShortUrl(segment.text))
|
||||
@@ -181,7 +183,7 @@ export function RichText({
|
||||
!disableLinks &&
|
||||
enableTags &&
|
||||
tag &&
|
||||
AppBskyRichtextFacet.validateTag(tag).success
|
||||
bsky.matches(app.bsky.richtext.facet.tag, tag)
|
||||
) {
|
||||
els.push(
|
||||
<RichTextTag
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import {useCallback, useEffect, useMemo, useState} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {type AppBskyGraphDefs, RichText as RichTextAPI} from '@atproto/api'
|
||||
import {type AppBskyGraphDefs} from '@atproto/api'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Plural, Trans} from '@lingui/react/macro'
|
||||
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {isOverMaxGraphemeCount} from '#/lib/strings/helpers'
|
||||
import {richTextToString} from '#/lib/strings/rich-text-helpers'
|
||||
import {asSdkFacets, richTextToString} from '#/lib/strings/rich-text-helpers'
|
||||
import {shortenLinks, stripInvalidMentions} from '#/lib/strings/rich-text-manip'
|
||||
import {logger} from '#/logger'
|
||||
import {type ImageMeta} from '#/state/gallery'
|
||||
@@ -15,7 +16,7 @@ import {
|
||||
useListCreateMutation,
|
||||
useListMetadataMutation,
|
||||
} from '#/state/queries/list'
|
||||
import {useAgent} from '#/state/session'
|
||||
import {useAppviewClient} from '#/state/session'
|
||||
import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
|
||||
import {EditableUserAvatar} from '#/view/com/util/UserAvatar'
|
||||
import {atoms as a, useTheme, web} from '#/alf'
|
||||
@@ -133,7 +134,11 @@ function DialogInner({
|
||||
|
||||
const {_} = useLingui()
|
||||
const t = useTheme()
|
||||
const agent = useAgent()
|
||||
/*
|
||||
* Facet/mention resolution is an appview job - it resolves handles through
|
||||
* the appview, and the public fallback keeps it working when logged out.
|
||||
*/
|
||||
const appviewClient = useAppviewClient()
|
||||
const control = Dialog.useDialogContext()
|
||||
const {
|
||||
mutateAsync: createListMutation,
|
||||
@@ -163,7 +168,10 @@ function DialogInner({
|
||||
|
||||
// We want to be working with a blank state here, so let's get the
|
||||
// serialized version and turn it back into a RichText
|
||||
const serialized = richTextToString(new RichTextAPI({text, facets}), false)
|
||||
const serialized = richTextToString(
|
||||
new RichTextAPI({text, facets: asSdkFacets(facets)}),
|
||||
false,
|
||||
)
|
||||
|
||||
const richText = new RichTextAPI({text: serialized})
|
||||
richText.detectFacetsWithoutResolution()
|
||||
@@ -224,7 +232,7 @@ function DialogInner({
|
||||
{cleanNewlines: true},
|
||||
)
|
||||
|
||||
await richText.detectFacets(agent)
|
||||
await richText.detectFacets(appviewClient)
|
||||
richText = shortenLinks(richText)
|
||||
richText = stripInvalidMentions(richText)
|
||||
|
||||
@@ -272,7 +280,7 @@ function DialogInner({
|
||||
setImageError,
|
||||
activePurpose,
|
||||
isCurateList,
|
||||
agent,
|
||||
appviewClient,
|
||||
_,
|
||||
])
|
||||
|
||||
|
||||
@@ -2,17 +2,14 @@ import {memo, useCallback} from 'react'
|
||||
import {Platform} from 'react-native'
|
||||
import {type GestureType} from 'react-native-gesture-handler'
|
||||
import * as Clipboard from 'expo-clipboard'
|
||||
import {
|
||||
type ChatBskyConvoDefs,
|
||||
type ModerationOpts,
|
||||
RichText,
|
||||
} from '@atproto/api'
|
||||
import {type ChatBskyConvoDefs, type ModerationOpts} from '@atproto/api'
|
||||
import {RichText} from '@bsky.app/sdk/richtext'
|
||||
import {plural} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {EMOJI_REACTION_LIMIT} from '#/lib/constants'
|
||||
import {useGoogleTranslate} from '#/lib/hooks/useGoogleTranslate'
|
||||
import {richTextToString} from '#/lib/strings/rich-text-helpers'
|
||||
import {asSdkFacets, richTextToString} from '#/lib/strings/rich-text-helpers'
|
||||
import {useMaybeProfileShadow} from '#/state/cache/profile-shadow'
|
||||
import {useConvoActive} from '#/state/messages/convo'
|
||||
import {useLanguagePrefs} from '#/state/preferences'
|
||||
@@ -88,7 +85,7 @@ export let MessageContextMenu = ({
|
||||
const str = richTextToString(
|
||||
new RichText({
|
||||
text: message.text,
|
||||
facets: message.facets,
|
||||
facets: asSdkFacets(message.facets),
|
||||
}),
|
||||
true,
|
||||
)
|
||||
|
||||
@@ -28,8 +28,8 @@ import {
|
||||
ChatBskyConvoDefs,
|
||||
ChatBskyEmbedJoinLink,
|
||||
moderateProfile,
|
||||
RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {plural} from '@lingui/core/macro'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
@@ -37,6 +37,7 @@ import {useQueryClient} from '@tanstack/react-query'
|
||||
import {isBlockedOrBlocking} from '#/lib/moderation/blocked-and-muted'
|
||||
import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
|
||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||
import {asSdkFacets} from '#/lib/strings/rich-text-helpers'
|
||||
import {useMaybeProfileShadow} from '#/state/cache/profile-shadow'
|
||||
import {type Shadow} from '#/state/cache/types'
|
||||
import {type ConvoItem} from '#/state/messages/convo/types'
|
||||
@@ -245,7 +246,10 @@ let MessageItem = ({
|
||||
? t.palette.primary_300
|
||||
: t.palette.primary_100
|
||||
|
||||
const rt = new RichTextAPI({text: message.text, facets: message.facets})
|
||||
const rt = new RichTextAPI({
|
||||
text: message.text,
|
||||
facets: asSdkFacets(message.facets),
|
||||
})
|
||||
|
||||
const isEmojiOnly = isOnlyEmoji(message.text)
|
||||
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
import {RichText as RichTextAPI} from '@atproto/api'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
|
||||
import {useAgent} from '#/state/session'
|
||||
import {useAppviewClient} from '#/state/session'
|
||||
|
||||
export function useRichText(text: string): [RichTextAPI, boolean] {
|
||||
const [prevText, setPrevText] = useState(text)
|
||||
const [rawRT, setRawRT] = useState(() => new RichTextAPI({text}))
|
||||
const [resolvedRT, setResolvedRT] = useState<RichTextAPI | null>(null)
|
||||
const agent = useAgent()
|
||||
/*
|
||||
* Facet/mention resolution is an appview job - it resolves handles via
|
||||
* `com.atproto.identity.resolveHandle` through the appview. The public
|
||||
* fallback keeps mentions working on logged-out surfaces.
|
||||
*/
|
||||
const client = useAppviewClient()
|
||||
if (text !== prevText) {
|
||||
setPrevText(text)
|
||||
setRawRT(new RichTextAPI({text}))
|
||||
@@ -19,7 +24,7 @@ export function useRichText(text: string): [RichTextAPI, boolean] {
|
||||
async function resolveRTFacets() {
|
||||
// new each time
|
||||
const resolvedRT = new RichTextAPI({text})
|
||||
await resolvedRT.detectFacets(agent)
|
||||
await resolvedRT.detectFacets(client)
|
||||
if (!ignore) {
|
||||
setResolvedRT(resolvedRT)
|
||||
}
|
||||
@@ -28,7 +33,7 @@ export function useRichText(text: string): [RichTextAPI, boolean] {
|
||||
return () => {
|
||||
ignore = true
|
||||
}
|
||||
}, [text, agent])
|
||||
}, [text, client])
|
||||
const isResolving = resolvedRT === null
|
||||
return [resolvedRT ?? rawRT, isResolving]
|
||||
}
|
||||
|
||||
+11
-4
@@ -13,9 +13,10 @@ import {
|
||||
type ComAtprotoLabelDefs,
|
||||
type ComAtprotoRepoApplyWrites,
|
||||
type ComAtprotoRepoStrongRef,
|
||||
RichText,
|
||||
} from '@atproto/api'
|
||||
import {TID} from '@atproto/common-web'
|
||||
import {type Client} from '@atproto/lex'
|
||||
import {RichText} from '@bsky.app/sdk/richtext'
|
||||
import {t} from '@lingui/core/macro'
|
||||
import {type QueryClient} from '@tanstack/react-query'
|
||||
import {sha256} from 'js-sha256'
|
||||
@@ -51,6 +52,12 @@ interface PostOpts {
|
||||
replyTo?: string
|
||||
onStateChange?: (state: string) => void
|
||||
langs?: string[]
|
||||
/*
|
||||
* Facet/mention resolution is an appview job - it resolves handles through
|
||||
* the appview, and the public fallback keeps it working when logged out.
|
||||
* The rest of this pipeline still writes through the agent.
|
||||
*/
|
||||
appviewClient: Client
|
||||
}
|
||||
|
||||
export async function post(
|
||||
@@ -87,7 +94,7 @@ export async function post(
|
||||
const draft = thread.posts[i]
|
||||
|
||||
// Not awaited to avoid waterfalls.
|
||||
const rtPromise = resolveRT(agent, draft.richtext)
|
||||
const rtPromise = resolveRT(opts.appviewClient, draft.richtext)
|
||||
const embedPromise = resolveEmbed(
|
||||
agent,
|
||||
queryClient,
|
||||
@@ -196,14 +203,14 @@ export async function post(
|
||||
return {uris}
|
||||
}
|
||||
|
||||
async function resolveRT(agent: AtpAgent, richtext: RichText) {
|
||||
async function resolveRT(appviewClient: Client, richtext: RichText) {
|
||||
const trimmedText = richtext.text
|
||||
// Trim leading whitespace-only lines (but don't break ASCII art).
|
||||
.replace(/^(\s*\n)+/, '')
|
||||
// Trim any trailing whitespace.
|
||||
.trimEnd()
|
||||
let rt = new RichText({text: trimmedText}, {cleanNewlines: true})
|
||||
await rt.detectFacets(agent)
|
||||
await rt.detectFacets(appviewClient)
|
||||
|
||||
rt = shortenLinks(rt)
|
||||
rt = stripInvalidMentions(rt)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {type RichText} from '@atproto/api'
|
||||
import {type RichText} from '@bsky.app/sdk/richtext'
|
||||
import {countGraphemes} from 'unicode-segmenter/grapheme'
|
||||
|
||||
import {shortenLinks} from './rich-text-manip'
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import {AppBskyRichtextFacet, type RichText} from '@atproto/api'
|
||||
import {type RichText} from '@bsky.app/sdk/richtext'
|
||||
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {linkRequiresWarning} from './url-helpers'
|
||||
|
||||
export function richTextToString(rt: RichText, loose: boolean): string {
|
||||
@@ -14,7 +16,7 @@ export function richTextToString(rt: RichText, loose: boolean): string {
|
||||
for (const segment of rt.segments()) {
|
||||
const link = segment.link
|
||||
|
||||
if (link && AppBskyRichtextFacet.validateLink(link).success) {
|
||||
if (link && bsky.matches(app.bsky.richtext.facet.link, link)) {
|
||||
const href = link.uri
|
||||
const text = segment.text
|
||||
|
||||
@@ -28,3 +30,26 @@ export function richTextToString(rt: RichText, loose: boolean): string {
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Widens facets typed by the legacy `@atproto/api` codegen into the shape the
|
||||
* SDK's `RichText` accepts.
|
||||
*
|
||||
* The two are the same lexicon and identical at runtime; they differ only in
|
||||
* that the SDK brands `did`/`uri` as template literal types, which makes the
|
||||
* legacy `string` versions unassignable. Call this where facets read off an
|
||||
* `@atproto/api` view type are handed to `new RichText(...)`.
|
||||
*
|
||||
* Transitional: it goes away once the view types come from the SDK too.
|
||||
*/
|
||||
export function asSdkFacets(
|
||||
facets: {index: {byteStart: number; byteEnd: number}; features: unknown[]}[],
|
||||
): app.bsky.richtext.facet.Main[]
|
||||
export function asSdkFacets(
|
||||
facets:
|
||||
| {index: {byteStart: number; byteEnd: number}; features: unknown[]}[]
|
||||
| undefined,
|
||||
): app.bsky.richtext.facet.Main[] | undefined
|
||||
export function asSdkFacets(facets: unknown) {
|
||||
return facets as app.bsky.richtext.facet.Main[] | undefined
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import {AppBskyRichtextFacet, type RichText, UnicodeString} from '@atproto/api'
|
||||
import {type RichText, UnicodeString} from '@bsky.app/sdk/richtext'
|
||||
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {toShortUrl} from './url-helpers'
|
||||
|
||||
export function shortenLinks(rt: RichText): RichText {
|
||||
@@ -10,7 +12,9 @@ export function shortenLinks(rt: RichText): RichText {
|
||||
// enumerate the link facets
|
||||
if (rt.facets) {
|
||||
for (const facet of rt.facets) {
|
||||
const isLink = !!facet.features.find(AppBskyRichtextFacet.isLink)
|
||||
const isLink = !!facet.features.find(f =>
|
||||
bsky.isType(app.bsky.richtext.facet.link, f),
|
||||
)
|
||||
if (!isLink) {
|
||||
continue
|
||||
}
|
||||
@@ -40,7 +44,9 @@ export function stripInvalidMentions(rt: RichText): RichText {
|
||||
rt = rt.clone()
|
||||
if (rt.facets) {
|
||||
rt.facets = rt.facets?.filter(facet => {
|
||||
const mention = facet.features.find(AppBskyRichtextFacet.isMention)
|
||||
const mention = facet.features.find(f =>
|
||||
bsky.isType(app.bsky.richtext.facet.mention, f),
|
||||
)
|
||||
if (mention && !mention.did) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import {useCallback, useEffect, useMemo, useState} from 'react'
|
||||
import {LayoutAnimation, View} from 'react-native'
|
||||
import {
|
||||
AppBskyFeedPost,
|
||||
AppBskyRichtextFacet,
|
||||
AtUri,
|
||||
moderatePost,
|
||||
RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {AppBskyFeedPost, AtUri, moderatePost} from '@atproto/api'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
import {type RouteProp, useNavigation, useRoute} from '@react-navigation/native'
|
||||
|
||||
@@ -16,6 +11,7 @@ import {
|
||||
type CommonNavigatorParams,
|
||||
type NavigationProp,
|
||||
} from '#/lib/routes/types'
|
||||
import {asSdkFacets} from '#/lib/strings/rich-text-helpers'
|
||||
import {
|
||||
convertBskyAppUrlIfNeeded,
|
||||
getChatInviteCodeFromUrl,
|
||||
@@ -36,6 +32,7 @@ import {ContentHider} from '#/components/moderation/ContentHider'
|
||||
import {PostAlerts} from '#/components/moderation/PostAlerts'
|
||||
import {RichText} from '#/components/RichText'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
|
||||
/**
|
||||
@@ -106,7 +103,7 @@ export function useExtractEmbedFromFacets(
|
||||
for (const facet of rt.facets ?? []) {
|
||||
for (const feature of facet.features) {
|
||||
if (
|
||||
AppBskyRichtextFacet.isLink(feature) &&
|
||||
bsky.isType(app.bsky.richtext.facet.link, feature) &&
|
||||
(isBskyPostUrl(feature.uri) || isBskyChatInviteUrl(feature.uri))
|
||||
) {
|
||||
uriFromFacet = feature.uri
|
||||
@@ -176,7 +173,7 @@ function MessageInputPostEmbed({
|
||||
return {
|
||||
rt: new RichTextAPI({
|
||||
text: post.record.text,
|
||||
facets: post.record.facets,
|
||||
facets: asSdkFacets(post.record.facets),
|
||||
}),
|
||||
record: post.record,
|
||||
}
|
||||
|
||||
@@ -25,13 +25,12 @@ import {scheduleOnRN} from 'react-native-worklets'
|
||||
import {
|
||||
type $Typed,
|
||||
type AppBskyEmbedRecord,
|
||||
AppBskyRichtextFacet,
|
||||
ChatBskyConvoDefs,
|
||||
type ChatBskyEmbedJoinLink,
|
||||
ChatBskyGroupDefs,
|
||||
RichText,
|
||||
} from '@atproto/api'
|
||||
import {useScrollEdgeEffectRef} from '@bsky.app/expo-scroll-edge-effect'
|
||||
import {RichText} from '@bsky.app/sdk/richtext'
|
||||
|
||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||
import {mergeRefs} from '#/lib/merge-refs'
|
||||
@@ -52,7 +51,7 @@ import {type ConvoState, ConvoStatus} from '#/state/messages/convo/types'
|
||||
import {useGetJoinLinkPreview} from '#/state/queries/join-links'
|
||||
import {useGetPost} from '#/state/queries/post'
|
||||
import {createEmbedViewRecordFromPost} from '#/state/queries/postgate/util'
|
||||
import {useAgent, useSession} from '#/state/session'
|
||||
import {useAppviewClient, useSession} from '#/state/session'
|
||||
import {List, type ListMethods} from '#/view/com/util/List'
|
||||
import {MessageComposer} from '#/screens/Messages/components/MessageComposer'
|
||||
import {MessageListError} from '#/screens/Messages/components/MessageListError'
|
||||
@@ -71,6 +70,8 @@ import {Loader} from '#/components/Loader'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_ANDROID, IS_NATIVE, IS_WEB} from '#/env'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {ChatStatusInfo} from './ChatStatusInfo'
|
||||
import {groupSystemMessages, type RenderItem} from './groupSystemMessages'
|
||||
import {InviteLinkDialogProvider} from './InviteLinkDialogProvider'
|
||||
@@ -144,7 +145,11 @@ export function MessagesList({
|
||||
}) {
|
||||
const ax = useAnalytics()
|
||||
const convoState = useConvoActive()
|
||||
const agent = useAgent()
|
||||
/*
|
||||
* Facet/mention resolution is an appview job - it resolves handles through
|
||||
* the appview, and the public fallback keeps it working when logged out.
|
||||
*/
|
||||
const appviewClient = useAppviewClient()
|
||||
const {hasSession, currentAccount} = useSession()
|
||||
const getPost = useGetPost()
|
||||
const getJoinLinkPreview = useGetJoinLinkPreview()
|
||||
@@ -550,7 +555,8 @@ export function MessagesList({
|
||||
const linkFacet = rt.facets?.find(facet =>
|
||||
facet.features.find(
|
||||
feature =>
|
||||
AppBskyRichtextFacet.isLink(feature) && predicate(feature.uri),
|
||||
bsky.isType(app.bsky.richtext.facet.link, feature) &&
|
||||
predicate(feature.uri),
|
||||
),
|
||||
)
|
||||
if (linkFacet) {
|
||||
@@ -617,7 +623,7 @@ export function MessagesList({
|
||||
replyTo = {messageId: reply.id}
|
||||
}
|
||||
|
||||
await rt.detectFacets(agent)
|
||||
await rt.detectFacets(appviewClient)
|
||||
|
||||
rt = shortenLinks(rt)
|
||||
rt = stripInvalidMentions(rt)
|
||||
@@ -667,7 +673,7 @@ export function MessagesList({
|
||||
}
|
||||
},
|
||||
[
|
||||
agent,
|
||||
appviewClient,
|
||||
convoState,
|
||||
getPost,
|
||||
getJoinLinkPreview,
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
AppBskyFeedPost,
|
||||
type AppBskyFeedThreadgate,
|
||||
AtUri,
|
||||
RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {Plural, Trans, useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||
@@ -14,6 +14,7 @@ import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||
import {asSdkFacets} from '#/lib/strings/rich-text-helpers'
|
||||
import {niceDate} from '#/lib/strings/time'
|
||||
import {
|
||||
POST_TOMBSTONE,
|
||||
@@ -199,7 +200,7 @@ const ThreadItemAnchorInner = memo(function ThreadItemAnchorInner({
|
||||
() =>
|
||||
new RichTextAPI({
|
||||
text: record.text,
|
||||
facets: record.facets,
|
||||
facets: asSdkFacets(record.facets),
|
||||
}),
|
||||
[record],
|
||||
)
|
||||
|
||||
@@ -4,14 +4,15 @@ import {
|
||||
type AppBskyFeedDefs,
|
||||
type AppBskyFeedThreadgate,
|
||||
AtUri,
|
||||
RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
import {MAX_POST_LINES} from '#/lib/constants'
|
||||
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {countLines} from '#/lib/strings/helpers'
|
||||
import {asSdkFacets} from '#/lib/strings/rich-text-helpers'
|
||||
import {
|
||||
POST_TOMBSTONE,
|
||||
type Shadow,
|
||||
@@ -211,7 +212,7 @@ const ThreadItemPostInner = memo(function ThreadItemPostInner({
|
||||
() =>
|
||||
new RichTextAPI({
|
||||
text: record.text,
|
||||
facets: record.facets,
|
||||
facets: asSdkFacets(record.facets),
|
||||
}),
|
||||
[record],
|
||||
)
|
||||
|
||||
@@ -4,14 +4,15 @@ import {
|
||||
type AppBskyFeedDefs,
|
||||
type AppBskyFeedThreadgate,
|
||||
AtUri,
|
||||
RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
import {MAX_POST_LINES} from '#/lib/constants'
|
||||
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {countLines} from '#/lib/strings/helpers'
|
||||
import {asSdkFacets} from '#/lib/strings/rich-text-helpers'
|
||||
import {
|
||||
POST_TOMBSTONE,
|
||||
type Shadow,
|
||||
@@ -271,7 +272,7 @@ const ThreadItemTreePostInner = memo(function ThreadItemTreePostInner({
|
||||
() =>
|
||||
new RichTextAPI({
|
||||
text: record.text,
|
||||
facets: record.facets,
|
||||
facets: asSdkFacets(record.facets),
|
||||
}),
|
||||
[record],
|
||||
)
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
type AppBskyLabelerDefs,
|
||||
moderateProfile,
|
||||
type ModerationOpts,
|
||||
type RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {type RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {msg, plural} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Plural, Trans} from '@lingui/react/macro'
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
moderateProfile,
|
||||
type ModerationDecision,
|
||||
type ModerationOpts,
|
||||
type RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {type RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
@@ -12,8 +12,8 @@ import {
|
||||
type AppBskyLabelerDefs,
|
||||
moderateProfile,
|
||||
type ModerationOpts,
|
||||
type RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {type RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {useIsFocused} from '@react-navigation/native'
|
||||
|
||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import {useMemo} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {AppBskyGraphDefs, RichText as RichTextAPI} from '@atproto/api'
|
||||
import {AppBskyGraphDefs} from '@atproto/api'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
|
||||
import {useHaptics} from '#/lib/haptics'
|
||||
import {makeListLink} from '#/lib/routes/links'
|
||||
import {asSdkFacets} from '#/lib/strings/rich-text-helpers'
|
||||
import {logger} from '#/logger'
|
||||
import {useListBlockMutation, useListMuteMutation} from '#/state/queries/list'
|
||||
import {
|
||||
@@ -128,7 +130,7 @@ export function Header({
|
||||
list.description
|
||||
? new RichTextAPI({
|
||||
text: list.description,
|
||||
facets: list.descriptionFacets,
|
||||
facets: asSdkFacets(list.descriptionFacets),
|
||||
})
|
||||
: undefined,
|
||||
[list],
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import {useMemo} from 'react'
|
||||
import {Pressable, View} from 'react-native'
|
||||
import {Image} from 'expo-image'
|
||||
import {
|
||||
type AppBskyUnspeccedDefs,
|
||||
moderateProfile,
|
||||
RichText as RichTextApi,
|
||||
} from '@atproto/api'
|
||||
import {type AppBskyUnspeccedDefs, moderateProfile} from '@atproto/api'
|
||||
import {RichText as RichTextApi} from '@bsky.app/sdk/richtext'
|
||||
import {Plural, Trans, useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
AppBskyGraphStarterpack,
|
||||
AtUri,
|
||||
type ModerationOpts,
|
||||
RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Plural, Trans} from '@lingui/react/macro'
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
type NavigationProp,
|
||||
} from '#/lib/routes/types'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {asSdkFacets} from '#/lib/strings/rich-text-helpers'
|
||||
import {getStarterPackOgCard} from '#/lib/strings/starter-pack'
|
||||
import {logger} from '#/logger'
|
||||
import {updateProfileShadow} from '#/state/cache/profile-shadow'
|
||||
@@ -418,7 +419,7 @@ function Header({
|
||||
const richText = record.description
|
||||
? new RichTextAPI({
|
||||
text: record.description,
|
||||
facets: record.descriptionFacets,
|
||||
facets: asSdkFacets(record.descriptionFacets),
|
||||
})
|
||||
: undefined
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@ import {
|
||||
AppBskyFeedPost,
|
||||
AtUri,
|
||||
type ModerationDecision,
|
||||
RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
import {
|
||||
type RouteProp,
|
||||
@@ -58,6 +58,7 @@ import {
|
||||
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||
import {asSdkFacets} from '#/lib/strings/rich-text-helpers'
|
||||
import {logger} from '#/logger'
|
||||
import {useA11y} from '#/state/a11y'
|
||||
import {
|
||||
@@ -834,7 +835,7 @@ function Overlay({
|
||||
: undefined
|
||||
const richText = new RichTextAPI({
|
||||
text: record?.text || '',
|
||||
facets: record?.facets,
|
||||
facets: asSdkFacets(record?.facets),
|
||||
})
|
||||
const handle = sanitizeHandle(post.author.handle, '@')
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import {
|
||||
type AppBskyGraphDefs,
|
||||
AtUri,
|
||||
moderateFeedGenerator,
|
||||
RichText,
|
||||
} from '@atproto/api'
|
||||
import {type AtUriString} from '@atproto/syntax'
|
||||
import {RichText} from '@bsky.app/sdk/richtext'
|
||||
import {t} from '@lingui/core/macro'
|
||||
import {
|
||||
type InfiniteData,
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
import {DISCOVER_FEED_URI, DISCOVER_SAVED_FEED} from '#/lib/constants'
|
||||
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||
import {sanitizeHandle} from '#/lib/strings/handles'
|
||||
import {asSdkFacets} from '#/lib/strings/rich-text-helpers'
|
||||
import {GCTIME, STALE} from '#/state/queries'
|
||||
import {RQKEY as listQueryKey} from '#/state/queries/list'
|
||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||
@@ -104,7 +105,7 @@ export function hydrateFeedGenerator(
|
||||
|
||||
const description = new RichText({
|
||||
text: view.description || '',
|
||||
facets: (view.descriptionFacets || [])?.slice(),
|
||||
facets: asSdkFacets((view.descriptionFacets || [])?.slice()),
|
||||
})
|
||||
|
||||
if (!view.descriptionFacets) {
|
||||
@@ -145,7 +146,7 @@ export function hydrateList(view: AppBskyGraphDefs.ListView): FeedSourceInfo {
|
||||
|
||||
const description = new RichText({
|
||||
text: view.description || '',
|
||||
facets: (view.descriptionFacets || [])?.slice(),
|
||||
facets: asSdkFacets((view.descriptionFacets || [])?.slice()),
|
||||
})
|
||||
|
||||
if (!view.descriptionFacets) {
|
||||
|
||||
@@ -3,11 +3,10 @@ import {
|
||||
AppBskyGraphDefs,
|
||||
type AppBskyGraphGetStarterPack,
|
||||
AppBskyGraphStarterpack,
|
||||
type AppBskyRichtextFacet,
|
||||
type AtpAgent,
|
||||
AtUri,
|
||||
RichText,
|
||||
} from '@atproto/api'
|
||||
import {RichText} from '@bsky.app/sdk/richtext'
|
||||
import {
|
||||
type QueryClient,
|
||||
useMutation,
|
||||
@@ -26,7 +25,8 @@ import {
|
||||
import {invalidateActorStarterPacksQuery} from '#/state/queries/actor-starter-packs'
|
||||
import {STALE} from '#/state/queries/index'
|
||||
import {invalidateListMembersQuery} from '#/state/queries/list-members'
|
||||
import {useAgent} from '#/state/session'
|
||||
import {useAgent, useAppviewClient} from '#/state/session'
|
||||
import {type app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
|
||||
const RQKEY_ROOT = 'starter-pack'
|
||||
@@ -105,6 +105,11 @@ export function useCreateStarterPackMutation({
|
||||
}) {
|
||||
const queryClient = useQueryClient()
|
||||
const agent = useAgent()
|
||||
/*
|
||||
* Facet/mention resolution is an appview job - it resolves handles through
|
||||
* the appview, and the public fallback keeps it working when logged out.
|
||||
*/
|
||||
const appviewClient = useAppviewClient()
|
||||
|
||||
return useMutation<
|
||||
{uri: string; cid: string},
|
||||
@@ -112,10 +117,10 @@ export function useCreateStarterPackMutation({
|
||||
UseCreateStarterPackMutationParams
|
||||
>({
|
||||
mutationFn: async ({name, description, feeds, profiles}) => {
|
||||
let descriptionFacets: AppBskyRichtextFacet.Main[] | undefined
|
||||
let descriptionFacets: app.bsky.richtext.facet.Main[] | undefined
|
||||
if (description) {
|
||||
const rt = new RichText({text: description})
|
||||
await rt.detectFacets(agent)
|
||||
await rt.detectFacets(appviewClient)
|
||||
descriptionFacets = rt.facets
|
||||
}
|
||||
|
||||
@@ -167,6 +172,7 @@ export function useEditStarterPackMutation({
|
||||
}) {
|
||||
const queryClient = useQueryClient()
|
||||
const agent = useAgent()
|
||||
const appviewClient = useAppviewClient()
|
||||
|
||||
return useMutation<
|
||||
void,
|
||||
@@ -184,10 +190,10 @@ export function useEditStarterPackMutation({
|
||||
currentStarterPack,
|
||||
currentListItems,
|
||||
}) => {
|
||||
let descriptionFacets: AppBskyRichtextFacet.Main[] | undefined
|
||||
let descriptionFacets: app.bsky.richtext.facet.Main[] | undefined
|
||||
if (description) {
|
||||
const rt = new RichText({text: description})
|
||||
await rt.detectFacets(agent)
|
||||
await rt.detectFacets(appviewClient)
|
||||
descriptionFacets = rt.facets
|
||||
}
|
||||
|
||||
|
||||
@@ -49,10 +49,10 @@ import {
|
||||
AppBskyUnspeccedDefs,
|
||||
AtUri,
|
||||
ChatBskyGroupDefs,
|
||||
type RichText,
|
||||
} from '@atproto/api'
|
||||
import {type Client} from '@atproto/lex'
|
||||
import {type AtUriString} from '@atproto/syntax'
|
||||
import {type RichText} from '@bsky.app/sdk/richtext'
|
||||
import {plural} from '@lingui/core/macro'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
@@ -1083,6 +1083,7 @@ export const ComposePost = ({
|
||||
replyTo: replyTo?.uri,
|
||||
onStateChange: setPublishingStage,
|
||||
langs: currentLanguages,
|
||||
appviewClient: client,
|
||||
})
|
||||
).uris[0]
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Type converters for Draft API - convert between ComposerState and server Draft types.
|
||||
*/
|
||||
import {AppBskyDraftDefs, AtUri, RichText} from '@atproto/api'
|
||||
import {AppBskyDraftDefs, AtUri} from '@atproto/api'
|
||||
import {RichText} from '@bsky.app/sdk/richtext'
|
||||
import {nanoid} from 'nanoid/non-secure'
|
||||
|
||||
import {resolveLink} from '#/lib/api/resolve'
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {useEffect, useMemo, useRef, useState} from 'react'
|
||||
import {Platform, Text as RNText, View} from 'react-native'
|
||||
import {RichText} from '@atproto/api'
|
||||
import {parseLanguageString} from '@atproto/syntax'
|
||||
import {
|
||||
guessLanguageAsync,
|
||||
type LanguageResult,
|
||||
} from '@bsky.app/expo-guess-language'
|
||||
import {RichText} from '@bsky.app/sdk/richtext'
|
||||
import {Trans, useLingui} from '@lingui/react/macro'
|
||||
import debounce from 'lodash.debounce'
|
||||
|
||||
|
||||
@@ -3,9 +3,8 @@ import {
|
||||
type AppBskyActorDefs,
|
||||
type AppBskyDraftDefs,
|
||||
type AppBskyFeedPostgate,
|
||||
AppBskyRichtextFacet,
|
||||
RichText,
|
||||
} from '@atproto/api'
|
||||
import {RichText} from '@bsky.app/sdk/richtext'
|
||||
import {nanoid} from 'nanoid/non-secure'
|
||||
|
||||
import {type VideoTelemetry} from '#/lib/media/video/telemetry'
|
||||
@@ -28,6 +27,8 @@ import {
|
||||
suggestLinkCardUri,
|
||||
} from '#/view/com/composer/text-input/text-input-util'
|
||||
import {type Gif} from '#/features/gifPicker/types'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {
|
||||
createVideoState,
|
||||
type VideoAction,
|
||||
@@ -677,7 +678,7 @@ export function createComposerState({
|
||||
if (initRichText.facets) {
|
||||
for (const facet of initRichText.facets) {
|
||||
for (const feature of facet.features) {
|
||||
if (AppBskyRichtextFacet.isLink(feature)) {
|
||||
if (bsky.isType(app.bsky.richtext.facet.link, feature)) {
|
||||
if (isBskyPostUrl(feature.uri)) {
|
||||
detectedPostUris.set(feature.uri, {facet, rt: initRichText})
|
||||
} else {
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
View,
|
||||
} from 'react-native'
|
||||
import {type PasteEventPayload, TextInputWrapper} from 'expo-paste-input'
|
||||
import {AppBskyRichtextFacet, RichText} from '@atproto/api'
|
||||
import {RichText} from '@bsky.app/sdk/richtext'
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
|
||||
import {IMAGE_SIZE_CONFIG_POSTS} from '#/lib/constants'
|
||||
@@ -27,6 +27,8 @@ import {
|
||||
import {atoms as a, useAlf} from '#/alf'
|
||||
import {normalizeTextStyles} from '#/alf/typography'
|
||||
import {IS_ANDROID} from '#/env'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {Autocomplete} from './mobile/Autocomplete'
|
||||
import {type TextInputProps} from './TextInput.types'
|
||||
|
||||
@@ -88,7 +90,7 @@ export function TextInput({
|
||||
if (newRt.facets) {
|
||||
for (const facet of newRt.facets) {
|
||||
for (const feature of facet.features) {
|
||||
if (AppBskyRichtextFacet.isLink(feature)) {
|
||||
if (bsky.isType(app.bsky.richtext.facet.link, feature)) {
|
||||
if (isUriImage(feature.uri)) {
|
||||
const res = await downloadAndResize({
|
||||
uri: feature.uri,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {type TextInput} from 'react-native'
|
||||
import {type RichText} from '@atproto/api'
|
||||
import {type RichText} from '@bsky.app/sdk/richtext'
|
||||
|
||||
export type TextInputRef = {
|
||||
focus: () => void
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
} from 'react'
|
||||
import {StyleSheet, View} from 'react-native'
|
||||
import Animated, {FadeIn, FadeOut} from 'react-native-reanimated'
|
||||
import {AppBskyRichtextFacet, RichText} from '@atproto/api'
|
||||
import {RichText} from '@bsky.app/sdk/richtext'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
import {getSchema} from '@tiptap/core'
|
||||
import {Document} from '@tiptap/extension-document'
|
||||
@@ -40,6 +40,8 @@ import {normalizeTextStyles} from '#/alf/typography'
|
||||
import {type Emoji} from '#/components/EmojiPicker'
|
||||
import {Portal} from '#/components/Portal'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {type TextInputProps} from './TextInput.types'
|
||||
import {type AutocompleteRef, createSuggestion} from './web/Autocomplete'
|
||||
import {LinkDecorator} from './web/LinkDecorator'
|
||||
@@ -265,7 +267,7 @@ export function TextInput({
|
||||
if (newRt.facets) {
|
||||
for (const facet of newRt.facets) {
|
||||
for (const feature of facet.features) {
|
||||
if (AppBskyRichtextFacet.isLink(feature)) {
|
||||
if (bsky.isType(app.bsky.richtext.facet.link, feature)) {
|
||||
nextDetectedUris.set(feature.uri, {facet, rt: newRt})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import {type AppBskyRichtextFacet, type RichText} from '@atproto/api'
|
||||
import {type RichText} from '@bsky.app/sdk/richtext'
|
||||
|
||||
import {type app} from '#/lexicons'
|
||||
|
||||
export type LinkFacetMatch = {
|
||||
rt: RichText
|
||||
facet: AppBskyRichtextFacet.Main
|
||||
facet: app.bsky.richtext.facet.Main
|
||||
}
|
||||
|
||||
export function suggestLinkCardUri(
|
||||
|
||||
@@ -6,14 +6,15 @@ import {
|
||||
AtUri,
|
||||
moderatePost,
|
||||
type ModerationDecision,
|
||||
RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {MAX_POST_LINES} from '#/lib/constants'
|
||||
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {countLines} from '#/lib/strings/helpers'
|
||||
import {asSdkFacets} from '#/lib/strings/rich-text-helpers'
|
||||
import {
|
||||
POST_TOMBSTONE,
|
||||
type Shadow,
|
||||
@@ -68,7 +69,7 @@ export function Post({
|
||||
record
|
||||
? new RichTextAPI({
|
||||
text: record.text,
|
||||
facets: record.facets,
|
||||
facets: asSdkFacets(record.facets),
|
||||
})
|
||||
: undefined,
|
||||
[record],
|
||||
|
||||
@@ -25,8 +25,8 @@ import {
|
||||
AppBskyEmbedImages,
|
||||
AppBskyEmbedVideo,
|
||||
type AppBskyFeedDefs,
|
||||
type RichText as RichTextType,
|
||||
} from '@atproto/api'
|
||||
import {type RichText as RichTextType} from '@bsky.app/sdk/richtext'
|
||||
import {useLingui} from '@lingui/react/macro'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
AppBskyFeedThreadgate,
|
||||
AtUri,
|
||||
type ModerationDecision,
|
||||
RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {type ReasonFeedSource} from '#/lib/api/feed/types'
|
||||
@@ -17,6 +17,7 @@ import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
||||
import {usePalette} from '#/lib/hooks/usePalette'
|
||||
import {makeProfileLink} from '#/lib/routes/links'
|
||||
import {countLines} from '#/lib/strings/helpers'
|
||||
import {asSdkFacets} from '#/lib/strings/rich-text-helpers'
|
||||
import {
|
||||
POST_TOMBSTONE,
|
||||
type Shadow,
|
||||
@@ -104,7 +105,7 @@ export function PostFeedItem({
|
||||
() =>
|
||||
new RichTextAPI({
|
||||
text: record.text,
|
||||
facets: record.facets,
|
||||
facets: asSdkFacets(record.facets),
|
||||
}),
|
||||
[record],
|
||||
)
|
||||
|
||||
@@ -15,8 +15,8 @@ import {
|
||||
type ModerationBehavior,
|
||||
type ModerationDecision,
|
||||
type ModerationOpts,
|
||||
RichText,
|
||||
} from '@atproto/api'
|
||||
import {RichText} from '@bsky.app/sdk/richtext'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
type AppBskyActorDefs,
|
||||
moderateProfile,
|
||||
type ModerationOpts,
|
||||
RichText as RichTextAPI,
|
||||
} from '@atproto/api'
|
||||
import {RichText as RichTextAPI} from '@bsky.app/sdk/richtext'
|
||||
import {msg} from '@lingui/core/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {Trans} from '@lingui/react/macro'
|
||||
@@ -33,7 +33,7 @@ import {useLabelerInfoQuery} from '#/state/queries/labeler'
|
||||
import {resetProfilePostsQueries} from '#/state/queries/post-feed'
|
||||
import {useProfileQuery} from '#/state/queries/profile'
|
||||
import {useResolveDidQuery} from '#/state/queries/resolve-uri'
|
||||
import {useAgent, useSession} from '#/state/session'
|
||||
import {useAppviewClient, useSession} from '#/state/session'
|
||||
import {ProfileFeedgens} from '#/view/com/feeds/ProfileFeedgens'
|
||||
import {ProfileLists} from '#/view/com/lists/ProfileLists'
|
||||
import {PagerWithHeader} from '#/view/com/pager/PagerWithHeader'
|
||||
@@ -613,7 +613,11 @@ function ProfileScreenLoaded({
|
||||
}
|
||||
|
||||
function useRichText(text: string): [RichTextAPI, boolean] {
|
||||
const agent = useAgent()
|
||||
/*
|
||||
* Facet/mention resolution is an appview job - it resolves handles through
|
||||
* the appview, and the public fallback keeps it working when logged out.
|
||||
*/
|
||||
const client = useAppviewClient()
|
||||
const [prevText, setPrevText] = useState(text)
|
||||
const [rawRT, setRawRT] = useState(() => new RichTextAPI({text}))
|
||||
const [resolvedRT, setResolvedRT] = useState<RichTextAPI | null>(null)
|
||||
@@ -628,7 +632,7 @@ function useRichText(text: string): [RichTextAPI, boolean] {
|
||||
async function resolveRTFacets() {
|
||||
// new each time
|
||||
const resolvedRT = new RichTextAPI({text})
|
||||
await resolvedRT.detectFacets(agent)
|
||||
await resolvedRT.detectFacets(client)
|
||||
if (!ignore) {
|
||||
setResolvedRT(resolvedRT)
|
||||
}
|
||||
@@ -637,7 +641,7 @@ function useRichText(text: string): [RichTextAPI, boolean] {
|
||||
return () => {
|
||||
ignore = true
|
||||
}
|
||||
}, [text, agent])
|
||||
}, [text, client])
|
||||
const isResolving = resolvedRT === null
|
||||
return [resolvedRT ?? rawRT, isResolving]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user