Compare commits

...

18 Commits

Author SHA1 Message Date
Eric Bailey 9e7b8c8ae8 Bump dev-env 2026-02-11 17:01:16 -06:00
Eric Bailey 19b3718840 Bump API SDK 2026-02-11 15:43:08 -06:00
Spence Pope 830f305d2d Merge pull request #9823 from bluesky-social/app-1788
[APP-1788] update design for stacked avatars
2026-02-11 15:57:07 -05:00
DS Boyce 8a088e8710 Merge pull request #9853 from bluesky-social/d/profile-screen
Use Tanstack Query isPending over isLoading
2026-02-11 10:27:13 -08:00
Spence Pope 8cbe3985fa Update src/components/ProgressGuide/List.tsx
Co-authored-by: Samuel Newman <mozzius@protonmail.com>
2026-02-11 13:11:30 -05:00
Spence Pope 1ec70420e6 Update src/components/ProgressGuide/List.tsx
Co-authored-by: Samuel Newman <mozzius@protonmail.com>
2026-02-11 13:11:24 -05:00
Spence Pope 63fbf577d9 Update src/components/ProgressGuide/List.tsx
Co-authored-by: Samuel Newman <mozzius@protonmail.com>
2026-02-11 13:11:15 -05:00
DS Boyce 355868e01e Address feedback and lint warnings 2026-02-11 09:46:47 -08:00
Samuel Newman 37e5a0f7a4 Clean up starter packs dialog styles + add optimistic updates (#9469) 2026-02-11 17:46:28 +00:00
DS Boyce 4356ff0040 Merge branch 'main' into d/profile-screen 2026-02-11 09:43:55 -08:00
Samuel Newman 4eca13941a Fix GIFs breaking mute state/pausing phone audio (#9859)
* fix operator precedence mistake

* ensure gifs can't change the global mute state
2026-02-11 08:51:23 -08:00
Samuel Newman 8580e4a00e absolutely position header, set a headerheight (#9858) 2026-02-11 07:22:13 -08:00
Samuel Newman 8581285d34 simplify some conditionals 2026-02-11 16:20:48 +02:00
Samuel Newman 57997d6d16 Add --what-to-test to iOS submissions (#9856)
Sets the "What to test" field in TestFlight to distinguish between
TestFlight and Production builds.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 06:01:27 -08:00
DS Boyce 5459857112 Use Tanstack Query status over isPending 2026-02-10 13:27:18 -08:00
vineyardbovines 3a28a07081 Merge branch 'main' into app-1788 2026-02-05 08:59:22 -05:00
vineyardbovines a57faedbae update icon 2026-02-05 08:51:53 -05:00
vineyardbovines a148b2477b update design for stacked avatars 2026-02-04 15:06:42 -05:00
14 changed files with 494 additions and 313 deletions
+6 -1
View File
@@ -118,7 +118,12 @@ jobs:
fi
- name: 🚀 Deploy
run: eas submit -p ios --non-interactive --path ios-build/ios/build/Bluesky.ipa
env:
PROFILE: ${{ inputs.profile || 'testflight' }}
run: >
eas submit -p ios --non-interactive
--path ios-build/ios/build/Bluesky.ipa
--what-to-test "$([[ "$PROFILE" == "production" ]] && echo "Production build" || echo "TestFlight build")"
- name: 🪲 Upload dSYM to Sentry
run: >
@@ -242,7 +242,7 @@ jobs:
--local --output build.ipa --non-interactive
- name: 🚀 Deploy
run: eas submit -p ios --non-interactive --path build.ipa
run: eas submit -p ios --non-interactive --path build.ipa --what-to-test "TestFlight build"
- name: ⬇️ Restore Cache
id: get-base-commit
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" d="M12.233 2a4.433 4.433 0 1 0 0 8.867 4.433 4.433 0 0 0 0-8.867Zm0 10.133c-3.888 0-6.863 2.263-8.071 5.435-.346.906-.11 1.8.44 2.436.535.619 1.36.996 2.25.996h10.762c.89 0 1.716-.377 2.25-.996.55-.636.786-1.53.441-2.436-1.208-3.173-4.184-5.435-8.072-5.435Z"/></svg>

After

Width:  |  Height:  |  Size: 357 B

+2 -2
View File
@@ -73,7 +73,7 @@
"icons:optimize": "svgo -f ./assets/icons"
},
"dependencies": {
"@atproto/api": "^0.18.20",
"@atproto/api": "^0.18.21",
"@bitdrift/react-native": "^0.6.8",
"@braintree/sanitize-url": "^6.0.2",
"@bsky.app/alf": "^0.1.6",
@@ -229,7 +229,7 @@
"zod": "^3.20.2"
},
"devDependencies": {
"@atproto/dev-env": "^0.3.208",
"@atproto/dev-env": "^0.3.209",
"@babel/core": "^7.26.0",
"@babel/preset-env": "^7.26.0",
"@babel/runtime": "^7.26.0",
@@ -58,7 +58,7 @@ export function VideoEmbedInnerNative({
<BlueskyVideoView
url={embed.playlist}
autoplay={!autoplayDisabled && !isWithinMessage}
beginMuted={isGif || autoplayDisabled ? false : muted}
beginMuted={isGif || (autoplayDisabled ? false : muted)}
style={[a.rounded_sm]}
onActiveChange={e => {
setIsActive(e.nativeEvent.isActive)
@@ -67,7 +67,9 @@ export function VideoEmbedInnerNative({
setIsLoading(e.nativeEvent.isLoading)
}}
onMutedChange={e => {
setMuted(e.nativeEvent.isMuted)
if (!isGif) {
setMuted(e.nativeEvent.isMuted)
}
}}
onStatusChange={e => {
setStatus(e.nativeEvent.status)
+79 -63
View File
@@ -1,4 +1,10 @@
import {type StyleProp, View, type ViewStyle} from 'react-native'
import {useState} from 'react'
import {
type LayoutChangeEvent,
type StyleProp,
View,
type ViewStyle,
} from 'react-native'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
@@ -11,7 +17,7 @@ import {
import {UserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a, useBreakpoints, useLayoutBreakpoints, useTheme} from '#/alf'
import {Button, ButtonIcon} from '#/components/Button'
import {Person_Stroke2_Corner2_Rounded as PersonIcon} from '#/components/icons/Person'
import {Person_Filled_Corner2_Rounded as PersonIcon} from '#/components/icons/Person'
import {TimesLarge_Stroke2_Corner0_Rounded as Times} from '#/components/icons/Times'
import {Text} from '#/components/Typography'
import type * as bsky from '#/types/bsky'
@@ -51,7 +57,7 @@ export function ProgressGuideList({style}: {style?: StyleProp<ViewStyle>}) {
a.flex_col,
a.gap_md,
a.rounded_md,
t.atoms.bg_contrast_25,
t.atoms.bg_contrast_50,
a.p_lg,
style,
]}>
@@ -81,8 +87,7 @@ export function ProgressGuideList({style}: {style?: StyleProp<ViewStyle>}) {
a.justify_between,
a.gap_sm,
]
: a.flex_col,
!inlineLayout && a.gap_md,
: [a.flex_col, a.gap_md],
]}>
<StackedAvatars follows={follows?.pages?.[0]?.follows} />
<FollowDialog guide={guide} showArrow={inlineLayout} />
@@ -112,70 +117,81 @@ export function ProgressGuideList({style}: {style?: StyleProp<ViewStyle>}) {
function StackedAvatars({follows}: {follows?: bsky.profile.AnyProfileView[]}) {
const t = useTheme()
const {centerColumnOffset} = useLayoutBreakpoints()
const [containerWidth, setContainerWidth] = useState(0)
// Smaller avatars for narrower viewport
const avatarSize = centerColumnOffset ? 30 : 37
const overlap = centerColumnOffset ? 9 : 11
const iconSize = centerColumnOffset ? 14 : 18
const onLayout = (e: LayoutChangeEvent) => {
setContainerWidth(e.nativeEvent.layout.width)
}
// Overlap ratio (22% of avatar size)
const overlapRatio = 0.22
// Calculate avatar size to fill container width
// Formula: containerWidth = avatarSize * count - overlap * (count - 1)
// Where overlap = avatarSize * overlapRatio
const visiblePortions = TOTAL_AVATARS - overlapRatio * (TOTAL_AVATARS - 1)
const avatarSize = containerWidth > 0 ? containerWidth / visiblePortions : 0
const overlap = avatarSize * overlapRatio
const iconSize = avatarSize * 0.5
// Use actual follows count, not the guide's event counter
const followedAvatars = follows?.slice(0, TOTAL_AVATARS) ?? []
const remainingSlots = TOTAL_AVATARS - followedAvatars.length
// Total width calculation: first avatar + (remaining * visible portion)
const totalWidth = avatarSize + (TOTAL_AVATARS - 1) * (avatarSize - overlap)
return (
<View style={[a.flex_row, a.self_start, {width: totalWidth}]}>
{/* Show followed user avatars */}
{followedAvatars.map((follow, i) => (
<View
key={follow.did}
style={[
a.rounded_full,
{
marginLeft: i === 0 ? 0 : -overlap,
zIndex: TOTAL_AVATARS - i,
borderWidth: 2,
borderColor: t.atoms.bg_contrast_25.backgroundColor,
},
]}>
<UserAvatar
type="user"
size={avatarSize - 4}
avatar={follow.avatar}
/>
</View>
))}
{/* Show placeholder avatars for remaining slots */}
{Array(remainingSlots)
.fill(0)
.map((_, i) => (
<View
key={`placeholder-${i}`}
style={[
a.align_center,
a.justify_center,
a.rounded_full,
t.atoms.bg_contrast_100,
{
width: avatarSize,
height: avatarSize,
marginLeft:
followedAvatars.length === 0 && i === 0 ? 0 : -overlap,
zIndex: TOTAL_AVATARS - followedAvatars.length - i,
borderWidth: 2,
borderColor: t.atoms.bg_contrast_25.backgroundColor,
},
]}>
<PersonIcon
width={iconSize}
height={iconSize}
fill={t.atoms.text_contrast_low.color}
/>
</View>
))}
<View style={[a.flex_row, a.flex_1]} onLayout={onLayout}>
{containerWidth > 0 && (
<>
{/* Show followed user avatars */}
{followedAvatars.map((follow, i) => (
<View
key={follow.did}
style={[
a.rounded_full,
a.border,
t.atoms.border_contrast_low,
{
marginLeft: i === 0 ? 0 : -overlap,
zIndex: TOTAL_AVATARS - i,
},
]}>
<UserAvatar
type="user"
size={avatarSize - 2}
avatar={follow.avatar}
noBorder
/>
</View>
))}
{/* Show placeholder avatars for remaining slots */}
{Array(remainingSlots)
.fill(0)
.map((_, i) => (
<View
key={`placeholder-${i}`}
style={[
a.align_center,
a.justify_center,
a.rounded_full,
t.atoms.bg_contrast_300,
a.border,
t.atoms.border_contrast_low,
{
width: avatarSize,
height: avatarSize,
marginLeft:
followedAvatars.length === 0 && i === 0 ? 0 : -overlap,
zIndex: TOTAL_AVATARS - followedAvatars.length - i,
},
]}>
<PersonIcon
width={iconSize}
height={iconSize}
fill={t.atoms.bg_contrast_50.backgroundColor}
/>
</View>
))}
</>
)}
</View>
)
}
+56 -58
View File
@@ -1,4 +1,4 @@
import {useCallback, useState} from 'react'
import {useCallback} from 'react'
import {View} from 'react-native'
import {
type AppBskyGraphGetStarterPacksWithMembership,
@@ -7,20 +7,18 @@ import {
import {msg, Plural, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useNavigation} from '@react-navigation/native'
import {useQueryClient} from '@tanstack/react-query'
import {useRequireEmailVerification} from '#/lib/hooks/useRequireEmailVerification'
import {type NavigationProp} from '#/lib/routes/types'
import {
invalidateActorStarterPacksWithMembershipQuery,
useActorStarterPacksWithMembershipsQuery,
} from '#/state/queries/actor-starter-packs'
import {isNetworkError} from '#/lib/strings/errors'
import {logger} from '#/logger'
import {useActorStarterPacksWithMembershipsQuery} from '#/state/queries/actor-starter-packs'
import {
useListMembershipAddMutation,
useListMembershipRemoveMutation,
} from '#/state/queries/list-memberships'
import * as Toast from '#/view/com/util/Toast'
import {atoms as a, useTheme} from '#/alf'
import {useProfileQuery} from '#/state/queries/profile'
import {atoms as a, native, platform, useTheme} from '#/alf'
import {AvatarStack} from '#/components/AvatarStack'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
@@ -29,6 +27,7 @@ import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/
import {StarterPack} from '#/components/icons/StarterPack'
import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times'
import {Loader} from '#/components/Loader'
import * as Toast from '#/components/Toast'
import {Text} from '#/components/Typography'
import {useAnalytics} from '#/analytics'
import {IS_WEB} from '#/env'
@@ -131,6 +130,7 @@ function StarterPackList({
}) {
const control = Dialog.useDialogContext()
const {_} = useLingui()
const {data: subject} = useProfileQuery({did: targetDid})
const {
data,
@@ -155,9 +155,13 @@ function StarterPackList({
const renderItem = useCallback(
({item}: {item: StarterPackWithMembership}) => (
<StarterPackItem starterPackWithMembership={item} targetDid={targetDid} />
<StarterPackItem
starterPackWithMembership={item}
targetDid={targetDid}
subject={subject}
/>
),
[targetDid],
[targetDid, subject],
)
const onClose = useCallback(() => {
@@ -168,9 +172,11 @@ function StarterPackList({
<>
<View
style={[
{justifyContent: 'space-between', flexDirection: 'row'},
IS_WEB ? a.mb_2xl : a.my_lg,
a.justify_between,
a.align_center,
a.flex_row,
a.pb_lg,
native(a.pt_lg),
]}>
<Text style={[a.text_lg, a.font_semi_bold]}>
<Trans>Add to starter packs</Trans>
@@ -181,7 +187,8 @@ function StarterPackList({
variant="ghost"
color="secondary"
size="small"
shape="round">
shape="round"
style={{margin: -8}}>
<ButtonIcon icon={XIcon} />
</Button>
</View>
@@ -232,7 +239,10 @@ function StarterPackList({
onEndReachedThreshold={0.1}
ListHeaderComponent={listHeader}
ListEmptyComponent={<Empty onStartWizard={onStartWizard} />}
style={IS_WEB ? [a.px_md, {minHeight: 500}] : [a.px_2xl, a.pt_lg]}
style={platform({
web: [a.px_2xl, {minHeight: 500}],
native: [a.px_2xl, a.pt_lg],
})}
/>
)
}
@@ -240,66 +250,54 @@ function StarterPackList({
function StarterPackItem({
starterPackWithMembership,
targetDid,
subject,
}: {
starterPackWithMembership: StarterPackWithMembership
targetDid: string
subject?: bsky.profile.AnyProfileView
}) {
const t = useTheme()
const ax = useAnalytics()
const {_} = useLingui()
const queryClient = useQueryClient()
const starterPack = starterPackWithMembership.starterPack
const isInPack = !!starterPackWithMembership.listItem
const [isPendingRefresh, setIsPendingRefresh] = useState(false)
const {mutate: addMembership, isPending: isPendingAdd} =
useListMembershipAddMutation({
subject,
onSuccess: () => {
Toast.show(_(msg`Added to starter pack`))
},
onError: err => {
if (!isNetworkError(err)) {
logger.error('Failed to add to starter pack', {safeMessage: err})
}
Toast.show(_(msg`Failed to add to starter pack`), {type: 'error'})
},
})
const {mutate: addMembership} = useListMembershipAddMutation({
onSuccess: () => {
Toast.show(_(msg`Added to starter pack`))
// Use a timeout to wait for the appview to update, matching the pattern
// in list-memberships.ts
setTimeout(() => {
invalidateActorStarterPacksWithMembershipQuery({
queryClient,
did: targetDid,
})
setIsPendingRefresh(false)
}, 1e3)
},
onError: () => {
Toast.show(_(msg`Failed to add to starter pack`), 'xmark')
setIsPendingRefresh(false)
},
})
const {mutate: removeMembership, isPending: isPendingRemove} =
useListMembershipRemoveMutation({
onSuccess: () => {
Toast.show(_(msg`Removed from starter pack`))
},
onError: err => {
if (!isNetworkError(err)) {
logger.error('Failed to remove from starter pack', {safeMessage: err})
}
Toast.show(_(msg`Failed to remove from starter pack`), {type: 'error'})
},
})
const {mutate: removeMembership} = useListMembershipRemoveMutation({
onSuccess: () => {
Toast.show(_(msg`Removed from starter pack`))
// Use a timeout to wait for the appview to update, matching the pattern
// in list-memberships.ts
setTimeout(() => {
invalidateActorStarterPacksWithMembershipQuery({
queryClient,
did: targetDid,
})
setIsPendingRefresh(false)
}, 1e3)
},
onError: () => {
Toast.show(_(msg`Failed to remove from starter pack`), 'xmark')
setIsPendingRefresh(false)
},
})
const isPending = isPendingAdd || isPendingRemove
const handleToggleMembership = () => {
if (!starterPack.list?.uri || isPendingRefresh) return
if (!starterPack.list?.uri || isPending) return
const listUri = starterPack.list.uri
const starterPackUri = starterPack.uri
setIsPendingRefresh(true)
if (!isInPack) {
addMembership({
listUri: listUri,
@@ -309,7 +307,6 @@ function StarterPackItem({
} else {
if (!starterPackWithMembership.listItem?.uri) {
console.error('Cannot remove: missing membership URI')
setIsPendingRefresh(false)
return
}
removeMembership({
@@ -344,7 +341,7 @@ function StarterPackItem({
starterPack.listItemsSample.length > 0 && (
<>
<AvatarStack
size={32}
size={24}
profiles={starterPack.listItemsSample
?.slice(0, 4)
.map(p => p.subject)}
@@ -375,8 +372,9 @@ function StarterPackItem({
label={isInPack ? _(msg`Remove`) : _(msg`Add`)}
color={isInPack ? 'secondary' : 'primary_subtle'}
size="tiny"
disabled={isPendingRefresh}
disabled={isPending}
onPress={handleToggleMembership}>
{isPending && <ButtonIcon icon={Loader} />}
<ButtonText>
{isInPack ? <Trans>Remove</Trans> : <Trans>Add</Trans>}
</ButtonText>
+4
View File
@@ -36,3 +36,7 @@ export const PersonPlus_Stroke2_Corner2_Rounded = createSinglePathSVG({
export const PersonGroup_Stroke2_Corner2_Rounded = createSinglePathSVG({
path: 'M8 5a2 2 0 1 0 0 4 2 2 0 0 0 0-4ZM4 7a4 4 0 1 1 8 0 4 4 0 0 1-8 0Zm13-1a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Zm-3.5 1.5a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0Zm7.301 9.7c-.836-2.6-2.88-3.503-4.575-3.111a1 1 0 0 1-.451-1.949c2.815-.651 5.81.966 6.93 4.448a2.49 2.49 0 0 1-.506 2.43A2.92 2.92 0 0 1 20 20h-2a1 1 0 1 1 0-2h2a.92.92 0 0 0 .69-.295.49.49 0 0 0 .112-.505ZM8 14c-1.865 0-3.878 1.274-4.681 4.151a.57.57 0 0 0 .132.55c.15.171.4.299.695.299h7.708a.93.93 0 0 0 .695-.299.57.57 0 0 0 .132-.55C11.878 15.274 9.865 14 8 14Zm0-2c2.87 0 5.594 1.98 6.607 5.613.53 1.9-1.09 3.387-2.753 3.387H4.146c-1.663 0-3.283-1.487-2.753-3.387C2.406 13.981 5.129 12 8 12Z',
})
export const Person_Filled_Corner2_Rounded = createSinglePathSVG({
path: 'M12.233 2a4.433 4.433 0 1 0 0 8.867 4.433 4.433 0 0 0 0-8.867ZM12.233 12.133c-3.888 0-6.863 2.263-8.071 5.435-.346.906-.11 1.8.44 2.436.535.619 1.36.996 2.25.996h10.762c.89 0 1.716-.377 2.25-.996.55-.636.786-1.53.441-2.436-1.208-3.173-4.184-5.435-8.072-5.435Z',
})
+24 -9
View File
@@ -1,4 +1,4 @@
import {useCallback, useMemo, useRef} from 'react'
import {useCallback, useMemo, useRef, useState} from 'react'
import {View} from 'react-native'
import {useAnimatedRef} from 'react-native-reanimated'
import {
@@ -35,12 +35,13 @@ import {PagerWithHeader} from '#/view/com/pager/PagerWithHeader'
import {FAB} from '#/view/com/util/fab/FAB'
import {type ListRef} from '#/view/com/util/List'
import {ListHiddenScreen} from '#/screens/List/ListHiddenScreen'
import {atoms as a, platform} from '#/alf'
import {atoms as a, native, platform, useTheme} from '#/alf'
import {useDialogControl} from '#/components/Dialog'
import {ListAddRemoveUsersDialog} from '#/components/dialogs/lists/ListAddRemoveUsersDialog'
import * as Layout from '#/components/Layout'
import {Loader} from '#/components/Loader'
import * as Hider from '#/components/moderation/Hider'
import {IS_WEB} from '#/env'
import {AboutSection} from './AboutSection'
import {ErrorScreen} from './components/ErrorScreen'
import {Header} from './components/Header'
@@ -149,6 +150,7 @@ function ProfileListScreenLoaded({
moderationOpts: ModerationOpts
preferences: UsePreferencesQueryResponse
}) {
const t = useTheme()
const {_} = useLingui()
const queryClient = useQueryClient()
const {openComposer} = useOpenComposer()
@@ -164,6 +166,8 @@ function ProfileListScreenLoaded({
const scrollElRef = useAnimatedRef()
const addUserDialogControl = useDialogControl()
const sectionTitlesCurate = [_(msg`Posts`), _(msg`People`)]
// modlist only
const [headerHeight, setHeaderHeight] = useState<number | null>(null)
const moderation = useMemo(() => {
return moderateUserList(list, moderationOpts)
@@ -263,13 +267,24 @@ function ProfileListScreenLoaded({
</Hider.Mask>
<Hider.Content>
<View style={[a.util_screen_outer]}>
<Layout.Center>{renderHeader()}</Layout.Center>
<AboutSection
list={list}
scrollElRef={scrollElRef as ListRef}
onPressAddUser={addUserDialogControl.open}
headerHeight={0}
/>
<Layout.Center
onLayout={evt => setHeaderHeight(evt.nativeEvent.layout.height)}
style={[
native([a.absolute, a.z_10, t.atoms.bg]),
a.border_b,
t.atoms.border_contrast_low,
]}>
{renderHeader()}
</Layout.Center>
{headerHeight !== null && (
<AboutSection
list={list}
scrollElRef={scrollElRef as ListRef}
onPressAddUser={addUserDialogControl.open}
headerHeight={IS_WEB ? 0 : headerHeight}
/>
)}
<FAB
testID="composeFAB"
onPress={() => openComposer({logContext: 'Fab'})}
+3 -24
View File
@@ -1,13 +1,4 @@
import {
type AppBskyGraphGetActorStarterPacks,
type AppBskyGraphGetStarterPacksWithMembership,
} from '@atproto/api'
import {
type InfiniteData,
type QueryClient,
type QueryKey,
useInfiniteQuery,
} from '@tanstack/react-query'
import {type QueryClient, useInfiniteQuery} from '@tanstack/react-query'
import {useAgent} from '#/state/session'
@@ -28,13 +19,7 @@ export function useActorStarterPacksQuery({
}) {
const agent = useAgent()
return useInfiniteQuery<
AppBskyGraphGetActorStarterPacks.OutputSchema,
Error,
InfiniteData<AppBskyGraphGetActorStarterPacks.OutputSchema>,
QueryKey,
string | undefined
>({
return useInfiniteQuery({
queryKey: RQKEY(did),
queryFn: async ({pageParam}: {pageParam?: string}) => {
const res = await agent.app.bsky.graph.getActorStarterPacks({
@@ -59,13 +44,7 @@ export function useActorStarterPacksWithMembershipsQuery({
}) {
const agent = useAgent()
return useInfiniteQuery<
AppBskyGraphGetStarterPacksWithMembership.OutputSchema,
Error,
InfiniteData<AppBskyGraphGetStarterPacksWithMembership.OutputSchema>,
QueryKey,
string | undefined
>({
return useInfiniteQuery({
queryKey: RQKEY_WITH_MEMBERSHIP(did),
queryFn: async ({pageParam}: {pageParam?: string}) => {
const res = await agent.app.bsky.graph.getStarterPacksWithMembership({
+116 -2
View File
@@ -14,12 +14,23 @@
* -prf
*/
import {AtUri} from '@atproto/api'
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
import {
type AppBskyActorDefs,
type AppBskyGraphGetStarterPacksWithMembership,
AtUri,
} from '@atproto/api'
import {
type InfiniteData,
useMutation,
useQuery,
useQueryClient,
} from '@tanstack/react-query'
import {STALE} from '#/state/queries'
import {RQKEY as LIST_MEMBERS_RQKEY} from '#/state/queries/list-members'
import {useAgent, useSession} from '#/state/session'
import type * as bsky from '#/types/bsky'
import {RQKEY_WITH_MEMBERSHIP as STARTER_PACKS_WITH_MEMBERSHIPS_RKEY} from './actor-starter-packs'
// sanity limit is SANITY_PAGE_LIMIT*PAGE_SIZE total records
const SANITY_PAGE_LIMIT = 1000
@@ -91,9 +102,14 @@ export function getMembership(
}
export function useListMembershipAddMutation({
subject,
onSuccess,
onError,
}: {
/**
* Needed for optimistic update of starter pack query
*/
subject?: bsky.profile.AnyProfileView
onSuccess?: (data: {uri: string; cid: string}) => void
onError?: (error: Error) => void
} = {}) {
@@ -151,6 +167,60 @@ export function useListMembershipAddMutation({
queryKey: LIST_MEMBERS_RQKEY(variables.listUri),
})
}, 1e3)
// update WITH_MEMBERSHIPS query
if (subject) {
queryClient.setQueryData<
InfiniteData<AppBskyGraphGetStarterPacksWithMembership.OutputSchema>
>(STARTER_PACKS_WITH_MEMBERSHIPS_RKEY(variables.actorDid), old => {
if (!old) return old
return {
...old,
pages: old.pages.map(page => ({
...page,
starterPacksWithMembership: page.starterPacksWithMembership.map(
spWithMembership => {
if (
spWithMembership.starterPack.list &&
spWithMembership.starterPack.list?.uri === variables.listUri
) {
return {
...spWithMembership,
starterPack: {
...spWithMembership.starterPack,
listItemsSample: [
{
uri: data.uri,
subject: subject as AppBskyActorDefs.ProfileView,
},
...(spWithMembership.starterPack.listItemsSample?.filter(
item => item.subject.did !== variables.actorDid,
) ?? []),
],
list: {
...spWithMembership.starterPack.list,
listItemCount:
(spWithMembership.starterPack.list.listItemCount ??
0) + 1,
},
},
listItem: {
uri: data.uri,
subject: subject as AppBskyActorDefs.ProfileView,
},
}
}
return spWithMembership
},
),
})),
}
})
}
onSuccess?.(data)
},
onError,
@@ -206,6 +276,50 @@ export function useListMembershipRemoveMutation({
queryKey: LIST_MEMBERS_RQKEY(variables.listUri),
})
}, 1e3)
queryClient.setQueryData<
InfiniteData<AppBskyGraphGetStarterPacksWithMembership.OutputSchema>
>(STARTER_PACKS_WITH_MEMBERSHIPS_RKEY(variables.actorDid), old => {
if (!old) return old
return {
...old,
pages: old.pages.map(page => ({
...page,
starterPacksWithMembership: page.starterPacksWithMembership.map(
spWithMembership => {
if (
spWithMembership.starterPack.list &&
spWithMembership.starterPack.list.uri === variables.listUri
) {
return {
...spWithMembership,
starterPack: {
...spWithMembership.starterPack,
listItemsSample:
spWithMembership.starterPack.listItemsSample?.filter(
item => item.subject.did !== variables.actorDid,
),
list: {
...spWithMembership.starterPack.list,
listItemCount: Math.max(
0,
(spWithMembership.starterPack.list.listItemCount ??
1) - 1,
),
},
},
listItem: undefined,
}
}
return spWithMembership
},
),
})),
}
})
onSuccess?.(data)
},
onError,
+11 -9
View File
@@ -329,15 +329,17 @@ let ProfileMenu = ({
)}
</>
)}
<Menu.Item
testID="profileHeaderDropdownStarterPackAddRemoveBtn"
label={_(msg`Add to starter packs`)}
onPress={onPressAddToStarterPacks}>
<Menu.ItemText>
<Trans>Add to starter packs</Trans>
</Menu.ItemText>
<Menu.ItemIcon icon={StarterPack} />
</Menu.Item>
{!isSelf && (
<Menu.Item
testID="profileHeaderDropdownStarterPackAddRemoveBtn"
label={_(msg`Add to starter packs`)}
onPress={onPressAddToStarterPacks}>
<Menu.ItemText>
<Trans>Add to starter packs</Trans>
</Menu.ItemText>
<Menu.ItemIcon icon={StarterPack} />
</Menu.Item>
)}
<Menu.Item
testID="profileHeaderDropdownListAddRemoveBtn"
label={_(msg`Add to lists`)}
+27 -27
View File
@@ -1,4 +1,4 @@
import React, {useCallback, useMemo} from 'react'
import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
import {StyleSheet} from 'react-native'
import {SafeAreaView} from 'react-native-safe-area-context'
import {
@@ -79,45 +79,45 @@ function ProfileScreenInner({route}: Props) {
data: resolvedDid,
error: resolveError,
refetch: refetchDid,
isLoading: isLoadingDid,
isPending: isDidPending,
} = useResolveDidQuery(name)
const {
data: profile,
error: profileError,
refetch: refetchProfile,
isLoading: isLoadingProfile,
isPlaceholderData: isPlaceholderProfile,
isPending: isProfilePending,
} = useProfileQuery({
did: resolvedDid,
})
const onPressTryAgain = React.useCallback(() => {
const onPressTryAgain = useCallback(() => {
if (resolveError) {
refetchDid()
void refetchDid()
} else {
refetchProfile()
void refetchProfile()
}
}, [resolveError, refetchDid, refetchProfile])
// Apply hard-coded redirects as need
React.useEffect(() => {
useEffect(() => {
if (resolveError) {
if (name === 'lulaoficial.bsky.social') {
console.log('Applying redirect to lula.com.br')
navigate('Profile', {name: 'lula.com.br'})
void 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(() => {
useEffect(() => {
if (resolvedDid && profile?.viewer?.blockedBy) {
resetProfilePostsQueries(queryClient, resolvedDid)
}
}, [queryClient, profile?.viewer?.blockedBy, resolvedDid])
// Most pushes will happen here, since we will have only placeholder data
if (isLoadingDid || isLoadingProfile) {
if (isDidPending || isProfilePending) {
return (
<Layout.Content>
<ProfileHeaderLoading />
@@ -186,20 +186,20 @@ function ProfileScreenLoaded({
did: profile.did,
enabled: !!profile.associated?.labeler,
})
const [currentPage, setCurrentPage] = React.useState(0)
const [currentPage, setCurrentPage] = useState(0)
const {_} = useLingui()
const [scrollViewTag, setScrollViewTag] = React.useState<number | null>(null)
const [scrollViewTag, setScrollViewTag] = useState<number | null>(null)
const postsSectionRef = React.useRef<SectionRef>(null)
const repliesSectionRef = React.useRef<SectionRef>(null)
const mediaSectionRef = React.useRef<SectionRef>(null)
const videosSectionRef = React.useRef<SectionRef>(null)
const likesSectionRef = React.useRef<SectionRef>(null)
const feedsSectionRef = React.useRef<SectionRef>(null)
const listsSectionRef = React.useRef<SectionRef>(null)
const starterPacksSectionRef = React.useRef<SectionRef>(null)
const labelsSectionRef = React.useRef<SectionRef>(null)
const postsSectionRef = useRef<SectionRef>(null)
const repliesSectionRef = useRef<SectionRef>(null)
const mediaSectionRef = useRef<SectionRef>(null)
const videosSectionRef = useRef<SectionRef>(null)
const likesSectionRef = useRef<SectionRef>(null)
const feedsSectionRef = useRef<SectionRef>(null)
const listsSectionRef = useRef<SectionRef>(null)
const starterPacksSectionRef = useRef<SectionRef>(null)
const labelsSectionRef = useRef<SectionRef>(null)
useSetTitle(combinedDisplayName(profile))
@@ -315,7 +315,7 @@ function ProfileScreenLoaded({
)
useFocusEffect(
React.useCallback(() => {
useCallback(() => {
setMinimalShellMode(false)
return listenSoftReset(() => {
scrollSectionToTop(currentPage)
@@ -601,16 +601,16 @@ function ProfileScreenLoaded({
function useRichText(text: string): [RichTextAPI, boolean] {
const agent = useAgent()
const [prevText, setPrevText] = React.useState(text)
const [rawRT, setRawRT] = React.useState(() => new RichTextAPI({text}))
const [resolvedRT, setResolvedRT] = React.useState<RichTextAPI | null>(null)
const [prevText, setPrevText] = useState(text)
const [rawRT, setRawRT] = useState(() => new RichTextAPI({text}))
const [resolvedRT, setResolvedRT] = useState<RichTextAPI | null>(null)
if (text !== prevText) {
setPrevText(text)
setRawRT(new RichTextAPI({text}))
setResolvedRT(null)
// This will queue an immediate re-render
}
React.useEffect(() => {
useEffect(() => {
let ignore = false
async function resolveRTFacets() {
// new each time
@@ -620,7 +620,7 @@ function useRichText(text: string): [RichTextAPI, boolean] {
setResolvedRT(resolvedRT)
}
}
resolveRTFacets()
void resolveRTFacets()
return () => {
ignore = true
}
+160 -115
View File
@@ -82,12 +82,12 @@
"@atproto/xrpc" "^0.7.6"
"@atproto/xrpc-server" "^0.10.0"
"@atproto/api@^0.18.18":
version "0.18.18"
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.18.18.tgz#0c02585ea1b0f4a37444b0d8dfbe347c99ea5f64"
integrity sha512-Vg7/sjbwDQZDj8fXtb4E48U4gA+6RC1iSt7onGnH2NyR0E25uds1KnqSKMzqcphdJXrz5GXrgHWc747XPGibzg==
"@atproto/api@^0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.18.20.tgz#3fdbb7b7fae90bd59101970c2b56cc31e8cf417d"
integrity sha512-BZYZkh2VJIFCXEnc/vzKwAwWjAQQTgbNJ8FBxpBK+z+KYh99O0uPCsRYKoCQsRrnkgrhzdU9+g2G+7zanTIGbw==
dependencies:
"@atproto/common-web" "^0.4.14"
"@atproto/common-web" "^0.4.15"
"@atproto/lexicon" "^0.6.1"
"@atproto/syntax" "^0.4.3"
"@atproto/xrpc" "^0.7.7"
@@ -96,12 +96,12 @@
tlds "^1.234.0"
zod "^3.23.8"
"@atproto/api@^0.18.19", "@atproto/api@^0.18.20":
version "0.18.20"
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.18.20.tgz#3fdbb7b7fae90bd59101970c2b56cc31e8cf417d"
integrity sha512-BZYZkh2VJIFCXEnc/vzKwAwWjAQQTgbNJ8FBxpBK+z+KYh99O0uPCsRYKoCQsRrnkgrhzdU9+g2G+7zanTIGbw==
"@atproto/api@^0.18.21":
version "0.18.21"
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.18.21.tgz#19dbea422de22e7f5253f1c5475e3588c3a9d4a0"
integrity sha512-s35MIJerGT/pKe2xJtKKswqlIr/ola2r2iURBKBL0Mk1OKe6jP4YvTMh1N2d2PEANFzNNTbKoDaLfJPo2Uvc/w==
dependencies:
"@atproto/common-web" "^0.4.15"
"@atproto/common-web" "^0.4.16"
"@atproto/lexicon" "^0.6.1"
"@atproto/syntax" "^0.4.3"
"@atproto/xrpc" "^0.7.7"
@@ -128,23 +128,23 @@
multiformats "^9.9.0"
uint8arrays "3.0.0"
"@atproto/bsky@^0.0.214":
version "0.0.214"
resolved "https://registry.yarnpkg.com/@atproto/bsky/-/bsky-0.0.214.tgz#54183957e323fe0800606acdcc3137acadf6672e"
integrity sha512-Fn+o3WcaX57EmcLoUdcViY4wU2UzfbyNlS/qFQu+1clADtL3pthylwTJgwUU73cZE+7MILZPfTzhQTc15snR9A==
"@atproto/bsky@^0.0.215":
version "0.0.215"
resolved "https://registry.yarnpkg.com/@atproto/bsky/-/bsky-0.0.215.tgz#df566ccc7bc77d27dd2f9506c96739352b395cef"
integrity sha512-1X9G4L8boh9naCtXzorFYm8VskE1xLOJslthR16Hd4uH+ryOEVxN0BLk16OP86qy00hU6VNvyGkDqBthUyy62w==
dependencies:
"@atproto-labs/fetch-node" "^0.2.0"
"@atproto-labs/xrpc-utils" "^0.0.24"
"@atproto/api" "^0.18.20"
"@atproto/common" "^0.5.10"
"@atproto/api" "^0.18.21"
"@atproto/common" "^0.5.11"
"@atproto/crypto" "^0.4.5"
"@atproto/did" "^0.3.0"
"@atproto/identity" "^0.4.10"
"@atproto/identity" "^0.4.11"
"@atproto/lexicon" "^0.6.1"
"@atproto/repo" "^0.8.12"
"@atproto/sync" "^0.1.39"
"@atproto/syntax" "^0.4.3"
"@atproto/xrpc-server" "^0.10.11"
"@atproto/xrpc-server" "^0.10.12"
"@bufbuild/protobuf" "^1.5.0"
"@connectrpc/connect" "^1.1.4"
"@connectrpc/connect-express" "^1.1.4"
@@ -194,7 +194,7 @@
pino-http "^8.2.1"
typed-emitter "^2.1.0"
"@atproto/common-web@^0.4.13", "@atproto/common-web@^0.4.14", "@atproto/common-web@^0.4.4", "@atproto/common-web@^0.4.7":
"@atproto/common-web@^0.4.13", "@atproto/common-web@^0.4.4", "@atproto/common-web@^0.4.7":
version "0.4.14"
resolved "https://registry.yarnpkg.com/@atproto/common-web/-/common-web-0.4.14.tgz#77545d454add08d735af00ffc29dd6abf1ab77b3"
integrity sha512-rMU8Q+kpyPpirUS9OqT7aOD1hxKa+diem3vc7BA0lOkj0tU6wcAxegxmbPZ8JaOsR7SSYhP/jCt/5wbT4qqkuQ==
@@ -214,6 +214,16 @@
"@atproto/syntax" "^0.4.3"
zod "^3.23.8"
"@atproto/common-web@^0.4.16":
version "0.4.16"
resolved "https://registry.yarnpkg.com/@atproto/common-web/-/common-web-0.4.16.tgz#1bee6073ee7a102ea6c47a512a8f6992c683012e"
integrity sha512-Ufvaff5JgxUyUyTAG0/3o7ltpy3lnZ1DvLjyAnvAf+hHfiK7OMQg+8byr+orN+KP9MtIQaRTsCgYPX+PxMKUoA==
dependencies:
"@atproto/lex-data" "^0.0.11"
"@atproto/lex-json" "^0.0.11"
"@atproto/syntax" "^0.4.3"
zod "^3.23.8"
"@atproto/common@0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@atproto/common/-/common-0.1.0.tgz#4216a8fef5b985ab62ac21252a0f8ca0f4a0f210"
@@ -246,14 +256,14 @@
multiformats "^9.9.0"
pino "^8.21.0"
"@atproto/common@^0.5.10":
version "0.5.10"
resolved "https://registry.yarnpkg.com/@atproto/common/-/common-0.5.10.tgz#f0d6b7e012b3dd3991d7e2975e26913d4f41e90f"
integrity sha512-A1+4W3JmjZIgmtJFLJBAaoVruZhRL0ANtyjZ91aJR4rjHcZuaQ+v4IFR1UcE6yyTATacLdBk6ADy8OtxXzq14g==
"@atproto/common@^0.5.11":
version "0.5.11"
resolved "https://registry.yarnpkg.com/@atproto/common/-/common-0.5.11.tgz#88499978d3b29c451e7ad9a71f9d2f89446e89f6"
integrity sha512-WRlT4s+wv80WdQuzkQub9D5vTD82O8dH2p91u4b+x3O17q5IQbmA3Lj+1NICINNSy2voqloqAWdqXEkRfdlAPw==
dependencies:
"@atproto/common-web" "^0.4.15"
"@atproto/lex-cbor" "^0.0.10"
"@atproto/lex-data" "^0.0.10"
"@atproto/common-web" "^0.4.16"
"@atproto/lex-cbor" "^0.0.11"
"@atproto/lex-data" "^0.0.11"
iso-datestring-validator "^2.2.2"
multiformats "^9.9.0"
pino "^8.21.0"
@@ -278,23 +288,23 @@
"@noble/hashes" "^1.6.1"
uint8arrays "3.0.0"
"@atproto/dev-env@^0.3.208":
version "0.3.208"
resolved "https://registry.yarnpkg.com/@atproto/dev-env/-/dev-env-0.3.208.tgz#01d07d4b1ef2404adb07bdddf324393989231a52"
integrity sha512-ttx+MIT21iFICW7sZNaFJoCPlxZAqgFy1ztOEn1ebW2MkJzsNHSq2oQC8MHxmpbYEOMPekTXC2iMHW6PYVQJxQ==
"@atproto/dev-env@^0.3.209":
version "0.3.209"
resolved "https://registry.yarnpkg.com/@atproto/dev-env/-/dev-env-0.3.209.tgz#0df234a7feab7628ad376773de0e8bd612fb7f2a"
integrity sha512-i4IVqoyCAqULNeye9xpo5YVfrNjFQjGwAJZ4QAA26L9LuFlXmudnAX17KOCMvmJiPgtRe2ofIy6h+ZJ6qm52tQ==
dependencies:
"@atproto/api" "^0.18.20"
"@atproto/bsky" "^0.0.214"
"@atproto/api" "^0.18.21"
"@atproto/bsky" "^0.0.215"
"@atproto/bsync" "^0.0.23"
"@atproto/common-web" "^0.4.15"
"@atproto/common-web" "^0.4.16"
"@atproto/crypto" "^0.4.5"
"@atproto/identity" "^0.4.10"
"@atproto/identity" "^0.4.11"
"@atproto/lexicon" "^0.6.1"
"@atproto/ozone" "^0.1.162"
"@atproto/pds" "^0.4.207"
"@atproto/ozone" "^0.1.163"
"@atproto/pds" "^0.4.209"
"@atproto/sync" "^0.1.39"
"@atproto/syntax" "^0.4.3"
"@atproto/xrpc-server" "^0.10.11"
"@atproto/xrpc-server" "^0.10.12"
"@did-plc/lib" "^0.0.1"
"@did-plc/server" "^0.0.1"
dotenv "^16.0.3"
@@ -319,6 +329,14 @@
"@atproto/common-web" "^0.4.4"
"@atproto/crypto" "^0.4.4"
"@atproto/identity@^0.4.11":
version "0.4.11"
resolved "https://registry.yarnpkg.com/@atproto/identity/-/identity-0.4.11.tgz#9622496499919cb2c05d21ab1ba45132dd1e3b52"
integrity sha512-/xXPPojR0PRD0pqoEPmgKMeclUCrkaMKfFGFkssAmXSuT39aCtiibxNqvhp+S2jOdeM6rKrs2QgH91OCGvwPcg==
dependencies:
"@atproto/common-web" "^0.4.16"
"@atproto/crypto" "^0.4.5"
"@atproto/jwk-jose@^0.1.11":
version "0.1.11"
resolved "https://registry.yarnpkg.com/@atproto/jwk-jose/-/jwk-jose-0.1.11.tgz#ef64bce940a66e267fc3cf0db8df4dbd062bb28a"
@@ -343,22 +361,22 @@
"@atproto/lex-data" "0.0.9"
tslib "^2.8.1"
"@atproto/lex-cbor@^0.0.10":
version "0.0.10"
resolved "https://registry.yarnpkg.com/@atproto/lex-cbor/-/lex-cbor-0.0.10.tgz#712abc4fedef4854c341e32d0fd1608e45616984"
integrity sha512-5RtV90iIhRNCXXvvETd3KlraV8XGAAAgOmiszUb+l8GySDU/sGk7AlVvArFfXnj/S/GXJq8DP6IaUxCw/sPASA==
"@atproto/lex-cbor@^0.0.11":
version "0.0.11"
resolved "https://registry.yarnpkg.com/@atproto/lex-cbor/-/lex-cbor-0.0.11.tgz#7a464a2032eb61a1dfc254bae1e2759c8279d6a6"
integrity sha512-A7ETtPsEsJ/VuPJOFw4bPNTKxHvFN1JbTQ2NjLuisd3ry7fVxgMpo/qGXsUQsAh/I/uziGbhpNqdS6GnI2p/Wg==
dependencies:
"@atproto/lex-data" "^0.0.10"
"@atproto/lex-data" "^0.0.11"
tslib "^2.8.1"
"@atproto/lex-client@^0.0.11":
version "0.0.11"
resolved "https://registry.yarnpkg.com/@atproto/lex-client/-/lex-client-0.0.11.tgz#b8e8d0da81ed27f1093d0a1e1c7fdb994394e323"
integrity sha512-2DCidAlhATtZc1Z11PUd+C98BiW/Od4pWtDlQSAxkjHSC/56ZwuSkZQVx27ISk1HldfKVc9qUvQWA9nhmrxYIw==
"@atproto/lex-client@^0.0.12":
version "0.0.12"
resolved "https://registry.yarnpkg.com/@atproto/lex-client/-/lex-client-0.0.12.tgz#87be8075746e32dafc3d23f282e53daadb50ea66"
integrity sha512-ef4jQQ7SOtBsXr+Gf1UHuBfCiAGYZxO5PCCXl3eT4ObO83SROtIf7pyO06jBQI/IZChSVsXqXsgakR0aru6lYQ==
dependencies:
"@atproto/lex-data" "^0.0.10"
"@atproto/lex-json" "^0.0.10"
"@atproto/lex-schema" "^0.0.11"
"@atproto/lex-data" "^0.0.11"
"@atproto/lex-json" "^0.0.11"
"@atproto/lex-schema" "^0.0.12"
tslib "^2.8.1"
"@atproto/lex-data@0.0.9":
@@ -381,12 +399,22 @@
uint8arrays "3.0.0"
unicode-segmenter "^0.14.0"
"@atproto/lex-document@^0.0.12":
version "0.0.12"
resolved "https://registry.yarnpkg.com/@atproto/lex-document/-/lex-document-0.0.12.tgz#18e0ca344101bb742a2930f663473e0213721f52"
integrity sha512-+no+ZXyCNdOdjkOj6a4n4WHAQzZz3M6VJTwx7IQQC2+to41/4fFr6k8U1y3Jtq2lYcbHqapkJj3RGIxzFcrtwA==
"@atproto/lex-data@^0.0.11":
version "0.0.11"
resolved "https://registry.yarnpkg.com/@atproto/lex-data/-/lex-data-0.0.11.tgz#de98894a80ff907f9cd6c3faa561d3a16e21dd8d"
integrity sha512-4+KTtHdqwlhiTKA7D4SACea4jprsNpCQsNALW09wsZ6IHhCDGO5tr1cmV+QnLYe3G3mu1E1yXHXbPUHrUUDT/A==
dependencies:
"@atproto/lex-schema" "^0.0.11"
multiformats "^9.9.0"
tslib "^2.8.1"
uint8arrays "3.0.0"
unicode-segmenter "^0.14.0"
"@atproto/lex-document@^0.0.13":
version "0.0.13"
resolved "https://registry.yarnpkg.com/@atproto/lex-document/-/lex-document-0.0.13.tgz#fcf794addf6cb328c78d4393e91ec0050a43c623"
integrity sha512-LWsBsKIbyuG7jFObTtnCFQNYHxWWVpVVspqv6UtnS/QsaCyCMg1GIz5vlgi8QBnmGvaPiQxIzGt6mERpTvEXpg==
dependencies:
"@atproto/lex-schema" "^0.0.12"
core-js "^3"
tslib "^2.8.1"
@@ -406,27 +434,35 @@
"@atproto/lex-data" "^0.0.10"
tslib "^2.8.1"
"@atproto/lex-resolver@^0.0.13":
version "0.0.13"
resolved "https://registry.yarnpkg.com/@atproto/lex-resolver/-/lex-resolver-0.0.13.tgz#6407b813001d3a3f9e4048197755deccb3cc6f62"
integrity sha512-CcqCE6W3ZVMVzAihatpVbXLxO15mtvddzRzovIJ9QxBTpUmtkdmIk9/gle0oAsRToTJYQy2a6dwAmnAosxP/XQ==
"@atproto/lex-json@^0.0.11":
version "0.0.11"
resolved "https://registry.yarnpkg.com/@atproto/lex-json/-/lex-json-0.0.11.tgz#56be96a4d2366113da3911020cedfb0fb97fbb4f"
integrity sha512-2IExAoQ4KsR5fyPa1JjIvtR316PvdgRH/l3BVGLBd3cSxM3m5MftIv1B6qZ9HjNiK60SgkWp0mi9574bTNDhBQ==
dependencies:
"@atproto/lex-data" "^0.0.11"
tslib "^2.8.1"
"@atproto/lex-resolver@^0.0.14":
version "0.0.14"
resolved "https://registry.yarnpkg.com/@atproto/lex-resolver/-/lex-resolver-0.0.14.tgz#8bc32e49c231bc29ca0d1ea927b33d7a21447de3"
integrity sha512-jBkYRmMKap2OM1zm0VDvs7Heuf3pGjw9xJEHQx1ohkMmM5f+cHPS40RQ8x8SNE+Vl9gMuOrgmgKyPDIuYSIBTw==
dependencies:
"@atproto-labs/did-resolver" "^0.2.6"
"@atproto/crypto" "^0.4.5"
"@atproto/lex-client" "^0.0.11"
"@atproto/lex-data" "^0.0.10"
"@atproto/lex-document" "^0.0.12"
"@atproto/lex-schema" "^0.0.11"
"@atproto/lex-client" "^0.0.12"
"@atproto/lex-data" "^0.0.11"
"@atproto/lex-document" "^0.0.13"
"@atproto/lex-schema" "^0.0.12"
"@atproto/repo" "^0.8.12"
"@atproto/syntax" "^0.4.3"
tslib "^2.8.1"
"@atproto/lex-schema@^0.0.11":
version "0.0.11"
resolved "https://registry.yarnpkg.com/@atproto/lex-schema/-/lex-schema-0.0.11.tgz#7ff8a1e94971cb750cacaa40e30f546a64db0acd"
integrity sha512-1vLUPQIMeawKP6ehSx2RiqaJDkiseFTXyUk3C4PaoFCktaH8FgVgPVKgUSSy02m1pxVMKnsOBV5psKeN53HG+Q==
"@atproto/lex-schema@^0.0.12":
version "0.0.12"
resolved "https://registry.yarnpkg.com/@atproto/lex-schema/-/lex-schema-0.0.12.tgz#3519b1e4040503e1e6e5a47df1012660f4c38d8c"
integrity sha512-l1RNYmqNwIEjgMEjC9i2o6FLsUFdpAc610xQYK/CRxN31cRzY0lAJ2GFbp2GZ4rRAD3FGYCXid6gZ42KsieUcw==
dependencies:
"@atproto/lex-data" "^0.0.10"
"@atproto/lex-data" "^0.0.11"
"@atproto/syntax" "^0.4.3"
tslib "^2.8.1"
@@ -441,7 +477,7 @@
multiformats "^9.9.0"
zod "^3.23.8"
"@atproto/oauth-provider-api@0.3.7", "@atproto/oauth-provider-api@^0.3.7":
"@atproto/oauth-provider-api@0.3.7":
version "0.3.7"
resolved "https://registry.yarnpkg.com/@atproto/oauth-provider-api/-/oauth-provider-api-0.3.7.tgz#7b911256536a72dbba6f38081a200836a3ab50b8"
integrity sha512-7yU9vuQFt/hy4NzlDtn+LuhIGvVKkhgWAkCmopnseMPBw6oGPT90uOsTxMkVGtHuKVvBSz7hOXoELXpnZq3gDQ==
@@ -449,41 +485,41 @@
"@atproto/jwk" "0.6.0"
"@atproto/oauth-types" "0.6.2"
"@atproto/oauth-provider-frontend@^0.2.8":
version "0.2.8"
resolved "https://registry.yarnpkg.com/@atproto/oauth-provider-frontend/-/oauth-provider-frontend-0.2.8.tgz#97f6dc33257b0f846229839dd74f606528f49846"
integrity sha512-wHypQrsbwE6LUlyDADfiJfOH5pDAHWm4l/v1dwkQhRndOai7L+knsbdLAOZVXuz6bRs7oV2Frb3mSS03OS4Gdw==
"@atproto/oauth-provider-frontend@0.2.9":
version "0.2.9"
resolved "https://registry.yarnpkg.com/@atproto/oauth-provider-frontend/-/oauth-provider-frontend-0.2.9.tgz#68486c0d9710a1325a02a303a6b22c733239b219"
integrity sha512-m1NhqC7ydEpF5ELV13CxkgCawyMX8wbAXls/4af3Tsz3IOkdM6zPgQeYK7Zj+7Nywjml4mZTkJ8rZiT6O9+5sw==
optionalDependencies:
"@atproto/oauth-provider-api" "0.3.7"
"@atproto/oauth-provider-ui@^0.4.2":
version "0.4.2"
resolved "https://registry.yarnpkg.com/@atproto/oauth-provider-ui/-/oauth-provider-ui-0.4.2.tgz#4edd41c71fd1bf5aaa1df219fb8a20c0b8d90738"
integrity sha512-j3Afu23JYy68GY8L4t/cTEZz1PnrvxLrjiG/nPhXPURZbCnqZ1puIU+HRVCBJfqKyCwaWthXgUjwaL+SFlPGKA==
"@atproto/oauth-provider-ui@0.4.3":
version "0.4.3"
resolved "https://registry.yarnpkg.com/@atproto/oauth-provider-ui/-/oauth-provider-ui-0.4.3.tgz#c80b076c2d2a7cc45e45866796f09b959bcf1c03"
integrity sha512-BLNZmtOwoHu2qk/Oi6dUR8TcXQaJre6wgW8YjkW5bKf+Vftn3PGzh8bKgr1fQZDYweZ6AF148imA2OzVSAaLHQ==
optionalDependencies:
"@atproto/oauth-provider-api" "0.3.7"
"@atproto/oauth-provider@^0.15.7":
version "0.15.7"
resolved "https://registry.yarnpkg.com/@atproto/oauth-provider/-/oauth-provider-0.15.7.tgz#fc5bd420984dac60f726949d2473bbdb3ddff991"
integrity sha512-rsPez44TJGVC5vFdwSTfSOLIIG5O1JF8NXLe1BjjsUbzRuflN6TyM6HyfBJVsnOVF2bOq6W6xPcnBFUJHCSyUQ==
"@atproto/oauth-provider@^0.15.9":
version "0.15.9"
resolved "https://registry.yarnpkg.com/@atproto/oauth-provider/-/oauth-provider-0.15.9.tgz#3ec21380711e34f4303a0782ecfffffb8080f144"
integrity sha512-nuyOVw5piS/iEk7xKEtdulRbcFZ7HBB0gPXL62z5IpxmluEvLem/VLqsntj2OK/mtP6xhnIfFHP5pb5+PnLuYQ==
dependencies:
"@atproto-labs/fetch" "^0.2.3"
"@atproto-labs/fetch-node" "^0.2.0"
"@atproto-labs/pipe" "^0.1.1"
"@atproto-labs/simple-store" "^0.3.0"
"@atproto-labs/simple-store-memory" "^0.1.4"
"@atproto/common" "^0.5.10"
"@atproto/common" "^0.5.11"
"@atproto/did" "^0.3.0"
"@atproto/jwk" "^0.6.0"
"@atproto/jwk-jose" "^0.1.11"
"@atproto/lex-document" "^0.0.12"
"@atproto/lex-resolver" "^0.0.13"
"@atproto/oauth-provider-api" "^0.3.7"
"@atproto/oauth-provider-frontend" "^0.2.8"
"@atproto/oauth-provider-ui" "^0.4.2"
"@atproto/lex-document" "^0.0.13"
"@atproto/lex-resolver" "^0.0.14"
"@atproto/oauth-provider-api" "0.3.7"
"@atproto/oauth-provider-frontend" "0.2.9"
"@atproto/oauth-provider-ui" "0.4.3"
"@atproto/oauth-scopes" "^0.3.1"
"@atproto/oauth-types" "^0.6.2"
"@atproto/oauth-types" "^0.6.3"
"@atproto/syntax" "^0.4.3"
"@hapi/accept" "^6.0.3"
"@hapi/address" "^5.1.1"
@@ -505,7 +541,7 @@
"@atproto/did" "^0.3.0"
"@atproto/syntax" "^0.4.3"
"@atproto/oauth-types@0.6.2", "@atproto/oauth-types@^0.6.2":
"@atproto/oauth-types@0.6.2":
version "0.6.2"
resolved "https://registry.yarnpkg.com/@atproto/oauth-types/-/oauth-types-0.6.2.tgz#d829fae63421dcea7ac703e84460175d2f8d9299"
integrity sha512-2cuboM4RQBCYR8NQC5uGRkW6KgCgKyq/B5/+tnMmWZYtZGVUQvsUWQHK/ZiMCnVXbcDNtc/RIEJQJDZ8FXMoxg==
@@ -514,20 +550,29 @@
"@atproto/jwk" "0.6.0"
zod "^3.23.8"
"@atproto/ozone@^0.1.162":
version "0.1.162"
resolved "https://registry.yarnpkg.com/@atproto/ozone/-/ozone-0.1.162.tgz#2c1775c1c45fb03b96087937c7675f7f1825c429"
integrity sha512-UeSbHJ4qHIrWcRelifPm4BFh8PFN7DG9tpKuXSqFjZFG3hO2ZDyBzxV6e9zh7Qg+m2tibdst4Hrk4vyT/7wO6Q==
"@atproto/oauth-types@^0.6.3":
version "0.6.3"
resolved "https://registry.yarnpkg.com/@atproto/oauth-types/-/oauth-types-0.6.3.tgz#4fc996d0af61874830079d1b1bddc7c0774ad6b2"
integrity sha512-jdKuoPknJuh/WjI+mYk7agSbx9mNVMbS6Dr3k1z2YMY2oRiCQjxYBuo4MLKATbxj05nMQaZRWlHRUazoAu5Cng==
dependencies:
"@atproto/api" "^0.18.18"
"@atproto/common" "^0.5.9"
"@atproto/did" "^0.3.0"
"@atproto/jwk" "^0.6.0"
zod "^3.23.8"
"@atproto/ozone@^0.1.163":
version "0.1.163"
resolved "https://registry.yarnpkg.com/@atproto/ozone/-/ozone-0.1.163.tgz#7935f9180b6c7f3bd05043d42e14b6055c268596"
integrity sha512-Rz5p0Bf9K3KVo6VuPNe5KTiuJVM5eV7fXSIeWidC1WbDgfEEM8jJu2YRQHvDQq5r95SKKY0gTFYqU6vW0DKSJA==
dependencies:
"@atproto/api" "^0.18.20"
"@atproto/common" "^0.5.11"
"@atproto/crypto" "^0.4.5"
"@atproto/identity" "^0.4.10"
"@atproto/identity" "^0.4.11"
"@atproto/lexicon" "^0.6.1"
"@atproto/syntax" "^0.4.3"
"@atproto/ws-client" "^0.0.4"
"@atproto/xrpc" "^0.7.7"
"@atproto/xrpc-server" "^0.10.10"
"@atproto/xrpc-server" "^0.10.12"
"@did-plc/lib" "^0.0.1"
compression "^1.7.4"
cors "^2.8.5"
@@ -545,30 +590,30 @@
undici "^6.14.1"
ws "^8.12.0"
"@atproto/pds@^0.4.207":
version "0.4.207"
resolved "https://registry.yarnpkg.com/@atproto/pds/-/pds-0.4.207.tgz#20ff3afcf1541b96f016a6843d69d3731b562f19"
integrity sha512-LtzgqeD4aB3O1efzDbhdEopdujB4GG/klLT4N/3v35+0Jpylrkht5Ux2yJGKAqJkzFCfpBdRhNMWpJqS2ssu8A==
"@atproto/pds@^0.4.209":
version "0.4.209"
resolved "https://registry.yarnpkg.com/@atproto/pds/-/pds-0.4.209.tgz#eb12f325c55692a6487c0620a7754b13f0914fe1"
integrity sha512-jW9dnrrqj65u6FZoj/idIez+idGgYBG4KGVB75i7pqrRkE1a91Bj5MXX60JfIQ0VY9YpfBthSnJG9Pzz5OzA/Q==
dependencies:
"@atproto-labs/fetch-node" "^0.2.0"
"@atproto-labs/simple-store" "^0.3.0"
"@atproto-labs/simple-store-memory" "^0.1.4"
"@atproto-labs/simple-store-redis" "^0.0.1"
"@atproto-labs/xrpc-utils" "^0.0.24"
"@atproto/api" "^0.18.19"
"@atproto/api" "^0.18.21"
"@atproto/aws" "^0.2.31"
"@atproto/common" "^0.5.10"
"@atproto/common" "^0.5.11"
"@atproto/crypto" "^0.4.5"
"@atproto/identity" "^0.4.10"
"@atproto/lex-cbor" "^0.0.10"
"@atproto/lex-data" "^0.0.10"
"@atproto/identity" "^0.4.11"
"@atproto/lex-cbor" "^0.0.11"
"@atproto/lex-data" "^0.0.11"
"@atproto/lexicon" "^0.6.1"
"@atproto/oauth-provider" "^0.15.7"
"@atproto/oauth-provider" "^0.15.9"
"@atproto/oauth-scopes" "^0.3.1"
"@atproto/repo" "^0.8.12"
"@atproto/syntax" "^0.4.3"
"@atproto/xrpc" "^0.7.7"
"@atproto/xrpc-server" "^0.10.11"
"@atproto/xrpc-server" "^0.10.12"
"@did-plc/lib" "^0.0.4"
"@hapi/address" "^5.1.1"
better-sqlite3 "^10.0.0"
@@ -642,7 +687,7 @@
"@atproto/common" "^0.5.3"
ws "^8.12.0"
"@atproto/xrpc-server@^0.10.0", "@atproto/xrpc-server@^0.10.10", "@atproto/xrpc-server@^0.10.3":
"@atproto/xrpc-server@^0.10.0", "@atproto/xrpc-server@^0.10.3":
version "0.10.10"
resolved "https://registry.yarnpkg.com/@atproto/xrpc-server/-/xrpc-server-0.10.10.tgz#69d1a3ce8278a6b49286a5df33ebc204d42129ed"
integrity sha512-USDjOZGiletqzuWHC3Q2fk30hJDk4uYt6KPgvnZidShCouTf3hzwJZ8d2eOKOofSjGXW+GC0QYp7fYJFn6lZ2Q==
@@ -661,15 +706,15 @@
ws "^8.12.0"
zod "^3.23.8"
"@atproto/xrpc-server@^0.10.11":
version "0.10.11"
resolved "https://registry.yarnpkg.com/@atproto/xrpc-server/-/xrpc-server-0.10.11.tgz#cd6f880ebb6a913db2925a23aa0ef0b05f5b02fd"
integrity sha512-7XR+n1G4j1PO33slr2Agl+lmTXbEQzk5iaCJmrcsfTC/0BbCHqSSbm+WHduz3EH4dfFioZsnDo1UesCF0EQEtg==
"@atproto/xrpc-server@^0.10.12":
version "0.10.12"
resolved "https://registry.yarnpkg.com/@atproto/xrpc-server/-/xrpc-server-0.10.12.tgz#6b88c9af5d301302e5e177b5f982a3494e734d78"
integrity sha512-HuciFHxvQfWtvq+dmH3vK9uXii49vqkJgZFLcHums1xMvLvNBY5bgtUBdpQdsdEuq8B4DTuanZRs+z3zGariyg==
dependencies:
"@atproto/common" "^0.5.10"
"@atproto/common" "^0.5.11"
"@atproto/crypto" "^0.4.5"
"@atproto/lex-cbor" "^0.0.10"
"@atproto/lex-data" "^0.0.10"
"@atproto/lex-cbor" "^0.0.11"
"@atproto/lex-data" "^0.0.11"
"@atproto/lexicon" "^0.6.1"
"@atproto/ws-client" "^0.0.4"
"@atproto/xrpc" "^0.7.7"