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