diff --git a/bskyembed/src/components/embed.tsx b/bskyembed/src/components/embed.tsx
index 1ed107b5..3b4f5e77 100644
--- a/bskyembed/src/components/embed.tsx
+++ b/bskyembed/src/components/embed.tsx
@@ -372,7 +372,7 @@ function VideoEmbed({content}: {content: AppBskyEmbedVideo.View}) {
return (

+
{% include "scripts.html" %}
diff --git a/package.json b/package.json
index 06cce40a..d981b67c 100644
--- a/package.json
+++ b/package.json
@@ -52,7 +52,7 @@
"open-analyzer": "EXPO_PUBLIC_OPEN_ANALYZER=1 yarn build-web"
},
"dependencies": {
- "@atproto/api": "^0.13.7",
+ "@atproto/api": "0.13.5",
"@bam.tech/react-native-image-resizer": "^3.0.4",
"@braintree/sanitize-url": "^6.0.2",
"@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet",
diff --git a/src/components/FeedInterstitials.tsx b/src/components/FeedInterstitials.tsx
index 286ded13..5031f584 100644
--- a/src/components/FeedInterstitials.tsx
+++ b/src/components/FeedInterstitials.tsx
@@ -60,13 +60,16 @@ function CardOuter({
export function SuggestedFollowPlaceholder() {
const t = useTheme()
return (
-
+
-
-
+
+
+
+
+
)
}
@@ -173,14 +176,9 @@ function useExperimentalSuggestedUsersQuery() {
}
export function SuggestedFollows({feed}: {feed: FeedDescriptor}) {
- const {currentAccount} = useSession()
- const [feedType, feedUriOrDid] = feed.split('|')
+ const [feedType, feedUri] = feed.split('|')
if (feedType === 'author') {
- if (currentAccount?.did === feedUriOrDid) {
- return null
- } else {
- return
- }
+ return
} else {
return
}
@@ -199,7 +197,6 @@ export function SuggestedFollowsProfile({did}: {did: string}) {
isSuggestionsLoading={isSuggestionsLoading}
profiles={data?.suggestions ?? []}
error={error}
- viewContext="profile"
/>
)
}
@@ -215,7 +212,6 @@ export function SuggestedFollowsHome() {
isSuggestionsLoading={isSuggestionsLoading}
profiles={profiles}
error={error}
- viewContext="feed"
/>
)
}
@@ -224,12 +220,10 @@ export function ProfileGrid({
isSuggestionsLoading,
error,
profiles,
- viewContext = 'feed',
}: {
isSuggestionsLoading: boolean
profiles: AppBskyActorDefs.ProfileViewDetailed[]
error: Error | null
- viewContext: 'profile' | 'feed'
}) {
const t = useTheme()
const {_} = useLingui()
@@ -286,7 +280,7 @@ export function ProfileGrid({
shape="round"
/>
-
+
)}
@@ -303,31 +297,33 @@ export function ProfileGrid({
return (
-
-
- {viewContext === 'profile' ? (
- Similar accounts
- ) : (
- Suggested for you
- )}
+
+
+ Suggested for you
-
+
{gtMobile ? (
-
-
+
+
{content}
-
+
-
+
{content}
)
}
-export function DescriptionPlaceholder({
- numberOfLines = 3,
-}: {
- numberOfLines?: number
-}) {
+export function DescriptionPlaceholder() {
const t = useTheme()
return (
-
- {Array(numberOfLines)
- .fill(0)
- .map((_, i) => (
-
- ))}
+
+
+
+
)
}
diff --git a/src/lib/api/feed-manip.ts b/src/lib/api/feed-manip.ts
index 0eca8b16..eaa760b4 100644
--- a/src/lib/api/feed-manip.ts
+++ b/src/lib/api/feed-manip.ts
@@ -379,11 +379,7 @@ export class FeedTuner {
): FeedViewPostsSlice[] => {
for (let i = 0; i < slices.length; i++) {
const slice = slices[i]
- if (
- slice.isReply &&
- !slice.isRepost &&
- !shouldDisplayReplyInFollowing(slice.getAuthors(), userDid)
- ) {
+ if (slice.isReply && !shouldDisplayReplyInFollowing(slice, userDid)) {
slices.splice(i, 1)
i--
}
@@ -447,9 +443,13 @@ function areSameAuthor(authors: AuthorContext): boolean {
}
function shouldDisplayReplyInFollowing(
- authors: AuthorContext,
+ slice: FeedViewPostsSlice,
userDid: string,
): boolean {
+ if (slice.isRepost) {
+ return true
+ }
+ const authors = slice.getAuthors()
const {author, parentAuthor, grandparentAuthor, rootAuthor} = authors
if (!isSelfOrFollowing(author, userDid)) {
// Only show replies from self or people you follow.
@@ -463,6 +463,21 @@ function shouldDisplayReplyInFollowing(
// Always show self-threads.
return true
}
+ if (
+ parentAuthor &&
+ parentAuthor.did !== author.did &&
+ rootAuthor &&
+ rootAuthor.did === author.did &&
+ slice.items.length > 2
+ ) {
+ // If you follow A, show A -> someone[>0 likes] -> A chains too.
+ // This is different from cases below because you only know one person.
+ const parentPost = slice.items[1].post
+ const parentLikeCount = parentPost.likeCount ?? 0
+ if (parentLikeCount > 0) {
+ return true
+ }
+ }
// From this point on we need at least one more reason to show it.
if (
parentAuthor &&
diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts
index 7966767d..df9daab4 100644
--- a/src/lib/statsig/gates.ts
+++ b/src/lib/statsig/gates.ts
@@ -1,3 +1,3 @@
export type Gate =
// Keep this alphabetic please.
- 'debug_show_feedcontext' | 'suggested_feeds_interstitial'
+ 'debug_show_feedcontext' | 'suggested_feeds_interstitial' | 'video_upload' // upload videos
diff --git a/src/screens/Profile/Header/ProfileHeaderStandard.tsx b/src/screens/Profile/Header/ProfileHeaderStandard.tsx
index 846fa442..cf5fcb97 100644
--- a/src/screens/Profile/Header/ProfileHeaderStandard.tsx
+++ b/src/screens/Profile/Header/ProfileHeaderStandard.tsx
@@ -219,8 +219,6 @@ let ProfileHeaderStandard = ({
{profile.viewer?.following ? (
Following
- ) : profile.viewer?.followedBy ? (
- Follow Back
) : (
Follow
)}
diff --git a/src/screens/Signup/StepInfo/index.tsx b/src/screens/Signup/StepInfo/index.tsx
index e0a7912f..47fb4c70 100644
--- a/src/screens/Signup/StepInfo/index.tsx
+++ b/src/screens/Signup/StepInfo/index.tsx
@@ -172,7 +172,6 @@ export function StepInfo({
defaultValue={state.password}
secureTextEntry
autoComplete="new-password"
- autoCapitalize="none"
/>
diff --git a/src/state/queries/notifications/util.ts b/src/state/queries/notifications/util.ts
index 133d3ebc..e0ee0229 100644
--- a/src/state/queries/notifications/util.ts
+++ b/src/state/queries/notifications/util.ts
@@ -175,19 +175,9 @@ async function fetchSubjects(
}> {
const postUris = new Set()
const packUris = new Set()
-
- const postUrisWithLikes = new Set()
- const postUrisWithReposts = new Set()
-
for (const notif of groupedNotifs) {
if (notif.subjectUri?.includes('app.bsky.feed.post')) {
postUris.add(notif.subjectUri)
- if (notif.type === 'post-like') {
- postUrisWithLikes.add(notif.subjectUri)
- }
- if (notif.type === 'repost') {
- postUrisWithReposts.add(notif.subjectUri)
- }
} else if (
notif.notification.reasonSubject?.includes('app.bsky.graph.starterpack')
) {
@@ -216,15 +206,6 @@ async function fetchSubjects(
AppBskyFeedPost.validateRecord(post.record).success
) {
postsMap.set(post.uri, post)
-
- // HACK. In some cases, the appview appears to lag behind and returns empty counters.
- // To prevent scroll jump due to missing metrics, fill in 1 like/repost instead of 0.
- if (post.likeCount === 0 && postUrisWithLikes.has(post.uri)) {
- post.likeCount = 1
- }
- if (post.repostCount === 0 && postUrisWithReposts.has(post.uri)) {
- post.repostCount = 1
- }
}
}
for (const pack of packsChunks.flat()) {
diff --git a/src/state/queries/nuxs/definitions.ts b/src/state/queries/nuxs/definitions.ts
deleted file mode 100644
index c5cb1e9d..00000000
--- a/src/state/queries/nuxs/definitions.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import zod from 'zod'
-
-import {BaseNux} from '#/state/queries/nuxs/types'
-
-export enum Nux {
- One = 'one',
- Two = 'two',
-}
-
-export const nuxNames = new Set(Object.values(Nux))
-
-export type AppNux =
- | BaseNux<{
- id: Nux.One
- data: {
- likes: number
- }
- }>
- | BaseNux<{
- id: Nux.Two
- data: undefined
- }>
-
-export const NuxSchemas = {
- [Nux.One]: zod.object({
- likes: zod.number(),
- }),
- [Nux.Two]: undefined,
-}
diff --git a/src/state/queries/nuxs/index.ts b/src/state/queries/nuxs/index.ts
deleted file mode 100644
index 2945e67e..00000000
--- a/src/state/queries/nuxs/index.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-import {useMutation, useQueryClient} from '@tanstack/react-query'
-
-import {AppNux, Nux} from '#/state/queries/nuxs/definitions'
-import {parseAppNux, serializeAppNux} from '#/state/queries/nuxs/util'
-import {
- preferencesQueryKey,
- usePreferencesQuery,
-} from '#/state/queries/preferences'
-import {useAgent} from '#/state/session'
-
-export {Nux} from '#/state/queries/nuxs/definitions'
-
-export function useNuxs() {
- const {data, ...rest} = usePreferencesQuery()
-
- if (data && rest.isSuccess) {
- const nuxs = data.bskyAppState.nuxs
- ?.map(parseAppNux)
- ?.filter(Boolean) as AppNux[]
-
- if (nuxs) {
- return {
- nuxs,
- ...rest,
- }
- }
- }
-
- return {
- nuxs: undefined,
- ...rest,
- }
-}
-
-export function useNux(id: T) {
- const {nuxs, ...rest} = useNuxs()
-
- if (nuxs && rest.isSuccess) {
- const nux = nuxs.find(nux => nux.id === id)
-
- if (nux) {
- return {
- nux: nux as Extract,
- ...rest,
- }
- }
- }
-
- return {
- nux: undefined,
- ...rest,
- }
-}
-
-export function useUpsertNuxMutation() {
- const queryClient = useQueryClient()
- const agent = useAgent()
-
- return useMutation({
- mutationFn: async (nux: AppNux) => {
- await agent.bskyAppUpsertNux(serializeAppNux(nux))
- // triggers a refetch
- await queryClient.invalidateQueries({
- queryKey: preferencesQueryKey,
- })
- },
- })
-}
-
-export function useRemoveNuxsMutation() {
- const queryClient = useQueryClient()
- const agent = useAgent()
-
- return useMutation({
- mutationFn: async (ids: string[]) => {
- await agent.bskyAppRemoveNuxs(ids)
- // triggers a refetch
- await queryClient.invalidateQueries({
- queryKey: preferencesQueryKey,
- })
- },
- })
-}
diff --git a/src/state/queries/nuxs/types.ts b/src/state/queries/nuxs/types.ts
deleted file mode 100644
index 5b791847..00000000
--- a/src/state/queries/nuxs/types.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import {AppBskyActorDefs} from '@atproto/api'
-
-export type Data = Record | undefined
-
-export type BaseNux<
- T extends Pick & {data: Data},
-> = T & {
- completed: boolean
-}
diff --git a/src/state/queries/nuxs/util.ts b/src/state/queries/nuxs/util.ts
deleted file mode 100644
index d65b86a3..00000000
--- a/src/state/queries/nuxs/util.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import {AppBskyActorDefs, nuxSchema} from '@atproto/api'
-
-import {
- AppNux,
- Nux,
- nuxNames,
- NuxSchemas,
-} from '#/state/queries/nuxs/definitions'
-
-export function parseAppNux(nux: AppBskyActorDefs.Nux): AppNux | undefined {
- if (!nuxNames.has(nux.id as Nux)) return
- if (!nuxSchema.safeParse(nux).success) return
-
- const {data, ...rest} = nux
-
- const schema = NuxSchemas[nux.id as Nux]
-
- if (schema && data) {
- const parsedData = JSON.parse(data)
-
- if (!schema.safeParse(parsedData).success) return
-
- return {
- ...rest,
- data: parsedData,
- } as AppNux
- }
-
- return {
- ...rest,
- data: undefined,
- } as AppNux
-}
-
-export function serializeAppNux(nux: AppNux): AppBskyActorDefs.Nux {
- const {data, ...rest} = nux
- const schema = NuxSchemas[nux.id as Nux]
-
- const result: AppBskyActorDefs.Nux = {
- ...rest,
- data: undefined,
- }
-
- if (schema) {
- schema.parse(data)
- result.data = JSON.stringify(data)
- }
-
- nuxSchema.parse(result)
-
- return result
-}
diff --git a/src/state/queries/preferences/const.ts b/src/state/queries/preferences/const.ts
index e07f40ec..1ae7d206 100644
--- a/src/state/queries/preferences/const.ts
+++ b/src/state/queries/preferences/const.ts
@@ -37,6 +37,5 @@ export const DEFAULT_LOGGED_OUT_PREFERENCES: UsePreferencesQueryResponse = {
bskyAppState: {
queuedNudges: [],
activeProgressGuide: undefined,
- nuxs: [],
},
}
diff --git a/src/state/queries/suggested-follows.ts b/src/state/queries/suggested-follows.ts
index 5ae83170..f5d51a97 100644
--- a/src/state/queries/suggested-follows.ts
+++ b/src/state/queries/suggested-follows.ts
@@ -106,16 +106,13 @@ export function useSuggestedFollowsQuery(options?: SuggestedFollowsOptions) {
export function useSuggestedFollowsByActorQuery({did}: {did: string}) {
const agent = useAgent()
return useQuery({
+ gcTime: 0,
queryKey: suggestedFollowsByActorQueryKey(did),
queryFn: async () => {
const res = await agent.app.bsky.graph.getSuggestedFollowsByActor({
actor: did,
})
- const data = res.data.isFallback ? {suggestions: []} : res.data
- data.suggestions = data.suggestions.filter(profile => {
- return !profile.viewer?.following
- })
- return data
+ return res.data
},
})
}
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx
index dfdfb3eb..4c7892bc 100644
--- a/src/view/com/composer/Composer.tsx
+++ b/src/view/com/composer/Composer.tsx
@@ -59,7 +59,7 @@ import {useIsKeyboardVisible} from '#/lib/hooks/useIsKeyboardVisible'
import {usePalette} from '#/lib/hooks/usePalette'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {LikelyType} from '#/lib/link-meta/link-meta'
-import {logEvent} from '#/lib/statsig/statsig'
+import {logEvent, useGate} from '#/lib/statsig/statsig'
import {cleanError} from '#/lib/strings/errors'
import {insertMentionAt} from '#/lib/strings/mention-manip'
import {shortenLinks} from '#/lib/strings/rich-text-manip'
@@ -140,6 +140,7 @@ export const ComposePost = observer(function ComposePost({
}: Props & {
cancelRef?: React.RefObject
}) {
+ const gate = useGate()
const {currentAccount} = useSession()
const agent = useAgent()
const {data: currentProfile} = useProfileQuery({did: currentAccount!.did})
@@ -802,11 +803,13 @@ export const ComposePost = observer(function ComposePost({
) : (
-
+ {gate('video_upload') && (
+
+ )}
) => {
const selectedFile = evt.target.files?.[0]
if (selectedFile) {
- if (
- selectedFile.type === 'text/vtt' ||
- (selectedFile.type === 'text/plain' &&
- selectedFile.name.endsWith('.vtt'))
- ) {
+ if (selectedFile.type === 'text/vtt') {
onSelectFile(selectedFile)
} else {
- logger.error('Invalid subtitle file type', {
- safeMessage: `File: ${selectedFile.name} (${selectedFile.type})`,
- })
Toast.show(_(msg`Only WebVTT (.vtt) files are supported`))
}
}
diff --git a/src/view/com/profile/FollowButton.tsx b/src/view/com/profile/FollowButton.tsx
index aaa5d345..42adea3c 100644
--- a/src/view/com/profile/FollowButton.tsx
+++ b/src/view/com/profile/FollowButton.tsx
@@ -61,7 +61,7 @@ export function FollowButton({
label={_(msg({message: 'Unfollow', context: 'action'}))}
/>
)
- } else if (!profile.viewer.followedBy) {
+ } else {
return (
)
- } else {
- return (
-
- )
}
}
diff --git a/src/view/com/util/images/AutoSizedImage.tsx b/src/view/com/util/images/AutoSizedImage.tsx
index 80c48b13..f57ab4e3 100644
--- a/src/view/com/util/images/AutoSizedImage.tsx
+++ b/src/view/com/util/images/AutoSizedImage.tsx
@@ -23,10 +23,9 @@ export function useImageAspectRatio({
const [raw, setAspectRatio] = React.useState(
dimensions ? calc(dimensions) : 1,
)
- // this basically controls the width of the image
const {isCropped, constrained, max} = React.useMemo(() => {
- const ratio = 1 / 2 // max of 1:2 ratio in feeds
- const constrained = Math.max(raw, ratio)
+ const a34 = 0.75 // max of 3:4 ratio in feeds
+ const constrained = Math.max(raw, a34)
const max = Math.max(raw, 0.25) // max of 1:4 in thread
const isCropped = raw < constrained
return {
@@ -69,14 +68,14 @@ export function ConstrainedImage({
const t = useTheme()
const {gtMobile} = useBreakpoints()
/**
- * Computed as a % value to apply as `paddingTop`, this basically controls
- * the height of the image.
+ * Computed as a % value to apply as `paddingTop`
*/
const outerAspectRatio = React.useMemo(() => {
+ // capped to square or shorter
const ratio =
isNative || !gtMobile
- ? Math.min(1 / aspectRatio, 16 / 9) // 9:16 bounding box
- : Math.min(1 / aspectRatio, 1) // 1:1 bounding box
+ ? Math.min(1 / aspectRatio, 1.5)
+ : Math.min(1 / aspectRatio, 1)
return `${ratio * 100}%`
}, [aspectRatio, gtMobile])
diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx b/src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx
index be3f9071..6636883f 100644
--- a/src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx
+++ b/src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx
@@ -37,11 +37,11 @@ export function TimeIndicator({time}: {time: number}) {
]}>
- {`${minutes}:${seconds}`}
+ {minutes}:{seconds}
)
diff --git a/src/view/com/util/post-embeds/VideoEmbedInner/VideoWebControls.tsx b/src/view/com/util/post-embeds/VideoEmbedInner/VideoWebControls.tsx
index 791025f7..bb15db08 100644
--- a/src/view/com/util/post-embeds/VideoEmbedInner/VideoWebControls.tsx
+++ b/src/view/com/util/post-embeds/VideoEmbedInner/VideoWebControls.tsx
@@ -370,7 +370,7 @@ export function Controls({
onPress={onPressPlayPause}
/>
-
+
{formatTime(currentTime)} / {formatTime(duration)}
{hasSubtitleTrack && (
diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx
index 5ef64598..37111c02 100644
--- a/src/view/screens/Profile.tsx
+++ b/src/view/screens/Profile.tsx
@@ -41,7 +41,6 @@ import {ProfileFeedSection} from '#/screens/Profile/Sections/Feed'
import {ProfileLabelsSection} from '#/screens/Profile/Sections/Labels'
import {ScreenHider} from '#/components/moderation/ScreenHider'
import {ProfileStarterPacks} from '#/components/StarterPack/ProfileStarterPacks'
-import {navigate} from '#/Navigation'
import {ExpoScrollForwarderView} from '../../../modules/expo-scroll-forwarder'
import {ProfileFeedgens} from '../com/feeds/ProfileFeedgens'
import {ProfileLists} from '../com/lists/ProfileLists'
@@ -87,16 +86,6 @@ export function ProfileScreen({route}: Props) {
}
}, [resolveError, refetchDid, refetchProfile])
- // Apply hard-coded redirects as need
- React.useEffect(() => {
- if (resolveError) {
- if (name === 'lulaoficial.bsky.social') {
- console.log('Applying redirect to lula.com.br')
- navigate('Profile', {name: 'lula.com.br'})
- }
- }
- }, [name, resolveError])
-
// When we open the profile, we want to reset the posts query if we are blocked.
React.useEffect(() => {
if (resolvedDid && profile?.viewer?.blockedBy) {
diff --git a/yarn.lock b/yarn.lock
index b2e389aa..cc440109 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -72,6 +72,19 @@
resolved "https://registry.yarnpkg.com/@atproto-labs/simple-store/-/simple-store-0.1.1.tgz#e743a2722b5d8732166f0a72aca8bd10e9bff106"
integrity sha512-WKILW2b3QbAYKh+w5U2x6p5FqqLl0nAeLwGeDY+KjX01K4Dq3vQTR9b/qNp0jZm48CabPQVrqCv0PPU9LgRRRg==
+"@atproto/api@0.13.5":
+ version "0.13.5"
+ resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.13.5.tgz#04305cdb0a467ba366305c5e95cebb7ce0d39735"
+ integrity sha512-yT/YimcKYkrI0d282Zxo7O30OSYR+KDW89f81C6oYZfDRBcShC1aniVV8kluP5LrEAg8O27yrOSnBgx2v7XPew==
+ dependencies:
+ "@atproto/common-web" "^0.3.0"
+ "@atproto/lexicon" "^0.4.1"
+ "@atproto/syntax" "^0.3.0"
+ "@atproto/xrpc" "^0.6.1"
+ await-lock "^2.2.2"
+ multiformats "^9.9.0"
+ tlds "^1.234.0"
+
"@atproto/api@^0.13.0":
version "0.13.0"
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.13.0.tgz#d1c65a407f1c3c6aba5be9425f4f739a01419bd8"
@@ -85,20 +98,6 @@
multiformats "^9.9.0"
tlds "^1.234.0"
-"@atproto/api@^0.13.7":
- version "0.13.7"
- resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.13.7.tgz#072eba2025d5251505f17b0b5d2de33749ea5ee4"
- integrity sha512-41kSLmFWDbuPOenb52WRq1lnBkSZrL+X29tWcvEt6SZXK4xBoKAalw1MjF+oabhzff12iMtNaNvmmt2fu1L+cw==
- dependencies:
- "@atproto/common-web" "^0.3.0"
- "@atproto/lexicon" "^0.4.1"
- "@atproto/syntax" "^0.3.0"
- "@atproto/xrpc" "^0.6.2"
- await-lock "^2.2.2"
- multiformats "^9.9.0"
- tlds "^1.234.0"
- zod "^3.23.8"
-
"@atproto/aws@^0.2.2":
version "0.2.2"
resolved "https://registry.yarnpkg.com/@atproto/aws/-/aws-0.2.2.tgz#703e5e06f288bcf61c6d99a990738f1e7299e653"
@@ -444,10 +443,10 @@
"@atproto/lexicon" "^0.4.1"
zod "^3.23.8"
-"@atproto/xrpc@^0.6.2":
- version "0.6.2"
- resolved "https://registry.yarnpkg.com/@atproto/xrpc/-/xrpc-0.6.2.tgz#634228a7e533de01bda2214837d11574fdadad55"
- integrity sha512-as/gb08xJb02HAGNrSQSumCe10WnOAcnM6bR6KMatQyQJuEu7OY6ZDSTM/4HfjjoxsNqdvPmbYuoUab1bKTNlA==
+"@atproto/xrpc@^0.6.1":
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/@atproto/xrpc/-/xrpc-0.6.1.tgz#dcd1315c8c60eef5af2db7fa4e35a38ebc6d79d5"
+ integrity sha512-Zy5ydXEdk6sY7FDUZcEVfCL1jvbL4tXu5CcdPqbEaW6LQtk9GLds/DK1bCX9kswTGaBC88EMuqQMfkxOhp2t4A==
dependencies:
"@atproto/lexicon" "^0.4.1"
zod "^3.23.8"