Add starter pack embeds to posts (#4699)
* starter pack embeds * revert test code * Types * add `BaseLink` * precache on click * rm log * add a comment * loading state * top margin --------- Co-authored-by: Dan Abramov <dan.abramov@gmail.com>zio/stable
parent
a3d4fb652b
commit
aa7117edb6
|
@ -1,5 +1,10 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {GestureResponderEvent} from 'react-native'
|
import {
|
||||||
|
GestureResponderEvent,
|
||||||
|
Pressable,
|
||||||
|
StyleProp,
|
||||||
|
ViewStyle,
|
||||||
|
} from 'react-native'
|
||||||
import {sanitizeUrl} from '@braintree/sanitize-url'
|
import {sanitizeUrl} from '@braintree/sanitize-url'
|
||||||
import {StackActions, useLinkProps} from '@react-navigation/native'
|
import {StackActions, useLinkProps} from '@react-navigation/native'
|
||||||
|
|
||||||
|
@ -323,3 +328,45 @@ export function InlineLinkText({
|
||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Pressable that uses useLink to handle navigation. It is unstyled, so can be used in cases where the Button styles
|
||||||
|
* in Link are not desired.
|
||||||
|
* @param displayText
|
||||||
|
* @param style
|
||||||
|
* @param children
|
||||||
|
* @param rest
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
export function BaseLink({
|
||||||
|
displayText,
|
||||||
|
onPress: onPressOuter,
|
||||||
|
style,
|
||||||
|
children,
|
||||||
|
...rest
|
||||||
|
}: {
|
||||||
|
style?: StyleProp<ViewStyle>
|
||||||
|
children: React.ReactNode
|
||||||
|
to: string
|
||||||
|
action: 'push' | 'replace' | 'navigate'
|
||||||
|
onPress?: () => false | void
|
||||||
|
shareOnLongPress?: boolean
|
||||||
|
label: string
|
||||||
|
displayText?: string
|
||||||
|
}) {
|
||||||
|
const {onPress, ...btnProps} = useLink({
|
||||||
|
displayText: displayText ?? rest.to,
|
||||||
|
...rest,
|
||||||
|
})
|
||||||
|
return (
|
||||||
|
<Pressable
|
||||||
|
style={style}
|
||||||
|
onPress={e => {
|
||||||
|
onPressOuter?.()
|
||||||
|
onPress(e)
|
||||||
|
}}
|
||||||
|
{...btnProps}>
|
||||||
|
{children}
|
||||||
|
</Pressable>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -11,10 +11,12 @@ import {InfiniteData, UseInfiniteQueryResult} from '@tanstack/react-query'
|
||||||
import {useBottomBarOffset} from 'lib/hooks/useBottomBarOffset'
|
import {useBottomBarOffset} from 'lib/hooks/useBottomBarOffset'
|
||||||
import {isBlockedOrBlocking} from 'lib/moderation/blocked-and-muted'
|
import {isBlockedOrBlocking} from 'lib/moderation/blocked-and-muted'
|
||||||
import {isNative, isWeb} from 'platform/detection'
|
import {isNative, isWeb} from 'platform/detection'
|
||||||
|
import {useListMembersQuery} from 'state/queries/list-members'
|
||||||
import {useSession} from 'state/session'
|
import {useSession} from 'state/session'
|
||||||
import {List, ListRef} from 'view/com/util/List'
|
import {List, ListRef} from 'view/com/util/List'
|
||||||
import {SectionRef} from '#/screens/Profile/Sections/types'
|
import {SectionRef} from '#/screens/Profile/Sections/types'
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
|
import {ListMaybePlaceholder} from '#/components/Lists'
|
||||||
import {Default as ProfileCard} from '#/components/ProfileCard'
|
import {Default as ProfileCard} from '#/components/ProfileCard'
|
||||||
|
|
||||||
function keyExtractor(item: AppBskyActorDefs.ProfileViewBasic, index: number) {
|
function keyExtractor(item: AppBskyActorDefs.ProfileViewBasic, index: number) {
|
||||||
|
@ -33,18 +35,17 @@ interface ProfilesListProps {
|
||||||
|
|
||||||
export const ProfilesList = React.forwardRef<SectionRef, ProfilesListProps>(
|
export const ProfilesList = React.forwardRef<SectionRef, ProfilesListProps>(
|
||||||
function ProfilesListImpl(
|
function ProfilesListImpl(
|
||||||
{listUri, listMembersQuery, moderationOpts, headerHeight, scrollElRef},
|
{listUri, moderationOpts, headerHeight, scrollElRef},
|
||||||
ref,
|
ref,
|
||||||
) {
|
) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const [initialHeaderHeight] = React.useState(headerHeight)
|
const [initialHeaderHeight] = React.useState(headerHeight)
|
||||||
const bottomBarOffset = useBottomBarOffset(20)
|
const bottomBarOffset = useBottomBarOffset(20)
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
|
const {data, refetch, isError} = useListMembersQuery(listUri, 50)
|
||||||
|
|
||||||
const [isPTRing, setIsPTRing] = React.useState(false)
|
const [isPTRing, setIsPTRing] = React.useState(false)
|
||||||
|
|
||||||
const {data, refetch} = listMembersQuery
|
|
||||||
|
|
||||||
// The server returns these sorted by descending creation date, so we want to invert
|
// The server returns these sorted by descending creation date, so we want to invert
|
||||||
const profiles = data?.pages
|
const profiles = data?.pages
|
||||||
.flatMap(p => p.items.map(i => i.subject))
|
.flatMap(p => p.items.map(i => i.subject))
|
||||||
|
@ -96,7 +97,19 @@ export const ProfilesList = React.forwardRef<SectionRef, ProfilesListProps>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listMembersQuery)
|
if (!data) {
|
||||||
|
return (
|
||||||
|
<View style={{marginTop: headerHeight, marginBottom: bottomBarOffset}}>
|
||||||
|
<ListMaybePlaceholder
|
||||||
|
isLoading={true}
|
||||||
|
isError={isError}
|
||||||
|
onRetry={refetch}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data)
|
||||||
return (
|
return (
|
||||||
<List
|
<List
|
||||||
data={getSortedProfiles()}
|
data={getSortedProfiles()}
|
||||||
|
|
|
@ -1,15 +1,20 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
|
import {Image} from 'expo-image'
|
||||||
import {AppBskyGraphStarterpack, AtUri} from '@atproto/api'
|
import {AppBskyGraphStarterpack, AtUri} from '@atproto/api'
|
||||||
import {StarterPackViewBasic} from '@atproto/api/dist/client/types/app/bsky/graph/defs'
|
import {StarterPackViewBasic} from '@atproto/api/dist/client/types/app/bsky/graph/defs'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {sanitizeHandle} from 'lib/strings/handles'
|
import {sanitizeHandle} from 'lib/strings/handles'
|
||||||
|
import {getStarterPackOgCard} from 'lib/strings/starter-pack'
|
||||||
|
import {precacheResolvedUri} from 'state/queries/resolve-uri'
|
||||||
|
import {precacheStarterPack} from 'state/queries/starter-packs'
|
||||||
import {useSession} from 'state/session'
|
import {useSession} from 'state/session'
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {StarterPack} from '#/components/icons/StarterPack'
|
import {StarterPack} from '#/components/icons/StarterPack'
|
||||||
import {Link as InternalLink, LinkProps} from '#/components/Link'
|
import {BaseLink} from '#/components/Link'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
|
|
||||||
export function Default({starterPack}: {starterPack?: StarterPackViewBasic}) {
|
export function Default({starterPack}: {starterPack?: StarterPackViewBasic}) {
|
||||||
|
@ -88,10 +93,13 @@ export function Card({
|
||||||
export function Link({
|
export function Link({
|
||||||
starterPack,
|
starterPack,
|
||||||
children,
|
children,
|
||||||
...rest
|
|
||||||
}: {
|
}: {
|
||||||
starterPack: StarterPackViewBasic
|
starterPack: StarterPackViewBasic
|
||||||
} & Omit<LinkProps, 'to'>) {
|
onPress?: () => void
|
||||||
|
children: React.ReactNode
|
||||||
|
}) {
|
||||||
|
const {_} = useLingui()
|
||||||
|
const queryClient = useQueryClient()
|
||||||
const {record} = starterPack
|
const {record} = starterPack
|
||||||
const {rkey, handleOrDid} = React.useMemo(() => {
|
const {rkey, handleOrDid} = React.useMemo(() => {
|
||||||
const rkey = new AtUri(starterPack.uri).rkey
|
const rkey = new AtUri(starterPack.uri).rkey
|
||||||
|
@ -104,14 +112,46 @@ export function Link({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InternalLink
|
<BaseLink
|
||||||
label={record.name}
|
action="push"
|
||||||
{...rest}
|
to={`/starter-pack/${handleOrDid}/${rkey}`}
|
||||||
to={{
|
label={_(msg`Navigate to ${record.name}`)}
|
||||||
screen: 'StarterPack',
|
onPress={() => {
|
||||||
params: {name: handleOrDid, rkey},
|
precacheResolvedUri(
|
||||||
|
queryClient,
|
||||||
|
starterPack.creator.handle,
|
||||||
|
starterPack.creator.did,
|
||||||
|
)
|
||||||
|
precacheStarterPack(queryClient, starterPack)
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</InternalLink>
|
</BaseLink>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Embed({starterPack}: {starterPack: StarterPackViewBasic}) {
|
||||||
|
const t = useTheme()
|
||||||
|
const imageUri = getStarterPackOgCard(starterPack)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.mt_xs,
|
||||||
|
a.border,
|
||||||
|
a.rounded_sm,
|
||||||
|
a.overflow_hidden,
|
||||||
|
t.atoms.border_contrast_low,
|
||||||
|
]}>
|
||||||
|
<Link starterPack={starterPack}>
|
||||||
|
<Image
|
||||||
|
source={imageUri}
|
||||||
|
style={[a.w_full, {aspectRatio: 1.91}]}
|
||||||
|
accessibilityIgnoresInvertColors={true}
|
||||||
|
/>
|
||||||
|
<View style={[a.px_sm, a.py_md]}>
|
||||||
|
<Card starterPack={starterPack} />
|
||||||
|
</View>
|
||||||
|
</Link>
|
||||||
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
import {AppBskyFeedPost, BskyAgent} from '@atproto/api'
|
import {AppBskyFeedPost, AppBskyGraphStarterpack, BskyAgent} from '@atproto/api'
|
||||||
|
|
||||||
|
import {useFetchDid} from '#/state/queries/handle'
|
||||||
|
import {useGetPost} from '#/state/queries/post'
|
||||||
import * as apilib from 'lib/api/index'
|
import * as apilib from 'lib/api/index'
|
||||||
import {LikelyType, LinkMeta} from './link-meta'
|
import {
|
||||||
|
createStarterPackUri,
|
||||||
|
parseStarterPackUri,
|
||||||
|
} from 'lib/strings/starter-pack'
|
||||||
|
import {ComposerOptsQuote} from 'state/shell/composer'
|
||||||
// import {match as matchRoute} from 'view/routes'
|
// import {match as matchRoute} from 'view/routes'
|
||||||
import {convertBskyAppUrlIfNeeded, makeRecordUri} from '../strings/url-helpers'
|
import {convertBskyAppUrlIfNeeded, makeRecordUri} from '../strings/url-helpers'
|
||||||
import {ComposerOptsQuote} from 'state/shell/composer'
|
import {LikelyType, LinkMeta} from './link-meta'
|
||||||
import {useGetPost} from '#/state/queries/post'
|
|
||||||
import {useFetchDid} from '#/state/queries/handle'
|
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
// import {Home} from 'view/screens/Home'
|
// import {Home} from 'view/screens/Home'
|
||||||
|
@ -174,3 +179,39 @@ export async function getListAsEmbed(
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getStarterPackAsEmbed(
|
||||||
|
agent: BskyAgent,
|
||||||
|
fetchDid: ReturnType<typeof useFetchDid>,
|
||||||
|
url: string,
|
||||||
|
): Promise<apilib.ExternalEmbedDraft> {
|
||||||
|
const parsed = parseStarterPackUri(url)
|
||||||
|
if (!parsed) {
|
||||||
|
throw new Error(
|
||||||
|
'Unexepectedly called getStarterPackAsEmbed with a non-starterpack url',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
const did = await fetchDid(parsed.name)
|
||||||
|
const starterPack = createStarterPackUri({did, rkey: parsed.rkey})
|
||||||
|
const res = await agent.app.bsky.graph.getStarterPack({starterPack})
|
||||||
|
const record = res.data.starterPack.record
|
||||||
|
return {
|
||||||
|
isLoading: false,
|
||||||
|
uri: starterPack,
|
||||||
|
meta: {
|
||||||
|
url: starterPack,
|
||||||
|
likelyType: LikelyType.AtpData,
|
||||||
|
// Validation here should never fail
|
||||||
|
title: AppBskyGraphStarterpack.isRecord(record)
|
||||||
|
? record.name
|
||||||
|
: 'Starter Pack',
|
||||||
|
},
|
||||||
|
embed: {
|
||||||
|
$type: 'app.bsky.embed.record',
|
||||||
|
record: {
|
||||||
|
uri: res.data.starterPack.uri,
|
||||||
|
cid: res.data.starterPack.cid,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -96,7 +96,7 @@ export function createStarterPackUri({
|
||||||
}: {
|
}: {
|
||||||
did: string
|
did: string
|
||||||
rkey: string
|
rkey: string
|
||||||
}): string | null {
|
}): string {
|
||||||
return new AtUri(`at://${did}/app.bsky.graph.starterpack/${rkey}`).toString()
|
return new AtUri(`at://${did}/app.bsky.graph.starterpack/${rkey}`).toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -152,6 +152,30 @@ export function isBskyListUrl(url: string): boolean {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isBskyStartUrl(url: string): boolean {
|
||||||
|
if (isBskyAppUrl(url)) {
|
||||||
|
try {
|
||||||
|
const urlp = new URL(url)
|
||||||
|
return /start\/(?<name>[^/]+)\/(?<rkey>[^/]+)/i.test(urlp.pathname)
|
||||||
|
} catch {
|
||||||
|
console.error('Unexpected error in isBskyStartUrl()', url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isBskyStarterPackUrl(url: string): boolean {
|
||||||
|
if (isBskyAppUrl(url)) {
|
||||||
|
try {
|
||||||
|
const urlp = new URL(url)
|
||||||
|
return /starter-pack\/(?<name>[^/]+)\/(?<rkey>[^/]+)/i.test(urlp.pathname)
|
||||||
|
} catch {
|
||||||
|
console.error('Unexpected error in isBskyStartUrl()', url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
export function isBskyDownloadUrl(url: string): boolean {
|
export function isBskyDownloadUrl(url: string): boolean {
|
||||||
if (isExternalUrl(url)) {
|
if (isExternalUrl(url)) {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -3,7 +3,6 @@ import {View} from 'react-native'
|
||||||
import {Image} from 'expo-image'
|
import {Image} from 'expo-image'
|
||||||
import {
|
import {
|
||||||
AppBskyGraphDefs,
|
AppBskyGraphDefs,
|
||||||
AppBskyGraphGetList,
|
|
||||||
AppBskyGraphStarterpack,
|
AppBskyGraphStarterpack,
|
||||||
AtUri,
|
AtUri,
|
||||||
ModerationOpts,
|
ModerationOpts,
|
||||||
|
@ -14,11 +13,7 @@ import {msg, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {useNavigation} from '@react-navigation/native'
|
import {useNavigation} from '@react-navigation/native'
|
||||||
import {NativeStackScreenProps} from '@react-navigation/native-stack'
|
import {NativeStackScreenProps} from '@react-navigation/native-stack'
|
||||||
import {
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
InfiniteData,
|
|
||||||
UseInfiniteQueryResult,
|
|
||||||
useQueryClient,
|
|
||||||
} from '@tanstack/react-query'
|
|
||||||
|
|
||||||
import {cleanError} from '#/lib/strings/errors'
|
import {cleanError} from '#/lib/strings/errors'
|
||||||
import {logger} from '#/logger'
|
import {logger} from '#/logger'
|
||||||
|
@ -33,7 +28,6 @@ import {getStarterPackOgCard} from 'lib/strings/starter-pack'
|
||||||
import {isWeb} from 'platform/detection'
|
import {isWeb} from 'platform/detection'
|
||||||
import {updateProfileShadow} from 'state/cache/profile-shadow'
|
import {updateProfileShadow} from 'state/cache/profile-shadow'
|
||||||
import {useModerationOpts} from 'state/preferences/moderation-opts'
|
import {useModerationOpts} from 'state/preferences/moderation-opts'
|
||||||
import {useListMembersQuery} from 'state/queries/list-members'
|
|
||||||
import {useResolvedStarterPackShortLink} from 'state/queries/resolve-short-link'
|
import {useResolvedStarterPackShortLink} from 'state/queries/resolve-short-link'
|
||||||
import {useResolveDidQuery} from 'state/queries/resolve-uri'
|
import {useResolveDidQuery} from 'state/queries/resolve-uri'
|
||||||
import {useShortenLink} from 'state/queries/shorten-link'
|
import {useShortenLink} from 'state/queries/shorten-link'
|
||||||
|
@ -123,7 +117,6 @@ export function StarterPackScreenInner({
|
||||||
isLoading: isLoadingStarterPack,
|
isLoading: isLoadingStarterPack,
|
||||||
isError: isErrorStarterPack,
|
isError: isErrorStarterPack,
|
||||||
} = useStarterPackQuery({did, rkey})
|
} = useStarterPackQuery({did, rkey})
|
||||||
const listMembersQuery = useListMembersQuery(starterPack?.list?.uri, 50)
|
|
||||||
|
|
||||||
const isValid =
|
const isValid =
|
||||||
starterPack &&
|
starterPack &&
|
||||||
|
@ -134,12 +127,7 @@ export function StarterPackScreenInner({
|
||||||
if (!did || !starterPack || !isValid || !moderationOpts) {
|
if (!did || !starterPack || !isValid || !moderationOpts) {
|
||||||
return (
|
return (
|
||||||
<ListMaybePlaceholder
|
<ListMaybePlaceholder
|
||||||
isLoading={
|
isLoading={isLoadingDid || isLoadingStarterPack || !moderationOpts}
|
||||||
isLoadingDid ||
|
|
||||||
isLoadingStarterPack ||
|
|
||||||
listMembersQuery.isLoading ||
|
|
||||||
!moderationOpts
|
|
||||||
}
|
|
||||||
isError={isErrorDid || isErrorStarterPack || !isValid}
|
isError={isErrorDid || isErrorStarterPack || !isValid}
|
||||||
errorMessage={_(msg`That starter pack could not be found.`)}
|
errorMessage={_(msg`That starter pack could not be found.`)}
|
||||||
emptyMessage={_(msg`That starter pack could not be found.`)}
|
emptyMessage={_(msg`That starter pack could not be found.`)}
|
||||||
|
@ -155,7 +143,6 @@ export function StarterPackScreenInner({
|
||||||
<StarterPackScreenLoaded
|
<StarterPackScreenLoaded
|
||||||
starterPack={starterPack}
|
starterPack={starterPack}
|
||||||
routeParams={routeParams}
|
routeParams={routeParams}
|
||||||
listMembersQuery={listMembersQuery}
|
|
||||||
moderationOpts={moderationOpts}
|
moderationOpts={moderationOpts}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
@ -164,14 +151,10 @@ export function StarterPackScreenInner({
|
||||||
function StarterPackScreenLoaded({
|
function StarterPackScreenLoaded({
|
||||||
starterPack,
|
starterPack,
|
||||||
routeParams,
|
routeParams,
|
||||||
listMembersQuery,
|
|
||||||
moderationOpts,
|
moderationOpts,
|
||||||
}: {
|
}: {
|
||||||
starterPack: AppBskyGraphDefs.StarterPackView
|
starterPack: AppBskyGraphDefs.StarterPackView
|
||||||
routeParams: StarterPackScreeProps['route']['params']
|
routeParams: StarterPackScreeProps['route']['params']
|
||||||
listMembersQuery: UseInfiniteQueryResult<
|
|
||||||
InfiniteData<AppBskyGraphGetList.OutputSchema>
|
|
||||||
>
|
|
||||||
moderationOpts: ModerationOpts
|
moderationOpts: ModerationOpts
|
||||||
}) {
|
}) {
|
||||||
const showPeopleTab = Boolean(starterPack.list)
|
const showPeopleTab = Boolean(starterPack.list)
|
||||||
|
@ -242,7 +225,6 @@ function StarterPackScreenLoaded({
|
||||||
headerHeight={headerHeight}
|
headerHeight={headerHeight}
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
scrollElRef={scrollElRef}
|
scrollElRef={scrollElRef}
|
||||||
listMembersQuery={listMembersQuery}
|
|
||||||
moderationOpts={moderationOpts}
|
moderationOpts={moderationOpts}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
|
@ -347,3 +347,36 @@ async function whenAppViewReady(
|
||||||
() => agent.app.bsky.graph.getStarterPack({starterPack: uri}),
|
() => agent.app.bsky.graph.getStarterPack({starterPack: uri}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function precacheStarterPack(
|
||||||
|
queryClient: QueryClient,
|
||||||
|
starterPack:
|
||||||
|
| AppBskyGraphDefs.StarterPackViewBasic
|
||||||
|
| AppBskyGraphDefs.StarterPackView,
|
||||||
|
) {
|
||||||
|
if (!AppBskyGraphStarterpack.isRecord(starterPack.record)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let starterPackView: AppBskyGraphDefs.StarterPackView | undefined
|
||||||
|
if (AppBskyGraphDefs.isStarterPackView(starterPack)) {
|
||||||
|
starterPackView = starterPack
|
||||||
|
} else if (AppBskyGraphDefs.isStarterPackViewBasic(starterPack)) {
|
||||||
|
const listView: AppBskyGraphDefs.ListViewBasic = {
|
||||||
|
uri: starterPack.record.list,
|
||||||
|
// This will be populated once the data from server is fetched
|
||||||
|
cid: '',
|
||||||
|
name: starterPack.record.name,
|
||||||
|
purpose: 'app.bsky.graph.defs#referencelist',
|
||||||
|
}
|
||||||
|
starterPackView = {
|
||||||
|
...starterPack,
|
||||||
|
$type: 'app.bsky.graph.defs#starterPackView',
|
||||||
|
list: listView,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (starterPackView) {
|
||||||
|
queryClient.setQueryData(RQKEY({uri: starterPack.uri}), starterPackView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
getFeedAsEmbed,
|
getFeedAsEmbed,
|
||||||
getListAsEmbed,
|
getListAsEmbed,
|
||||||
getPostAsQuote,
|
getPostAsQuote,
|
||||||
|
getStarterPackAsEmbed,
|
||||||
} from 'lib/link-meta/bsky'
|
} from 'lib/link-meta/bsky'
|
||||||
import {getLinkMeta} from 'lib/link-meta/link-meta'
|
import {getLinkMeta} from 'lib/link-meta/link-meta'
|
||||||
import {resolveShortLink} from 'lib/link-meta/resolve-short-link'
|
import {resolveShortLink} from 'lib/link-meta/resolve-short-link'
|
||||||
|
@ -18,6 +19,8 @@ import {
|
||||||
isBskyCustomFeedUrl,
|
isBskyCustomFeedUrl,
|
||||||
isBskyListUrl,
|
isBskyListUrl,
|
||||||
isBskyPostUrl,
|
isBskyPostUrl,
|
||||||
|
isBskyStarterPackUrl,
|
||||||
|
isBskyStartUrl,
|
||||||
isShortLink,
|
isShortLink,
|
||||||
} from 'lib/strings/url-helpers'
|
} from 'lib/strings/url-helpers'
|
||||||
import {ImageModel} from 'state/models/media/image'
|
import {ImageModel} from 'state/models/media/image'
|
||||||
|
@ -96,6 +99,23 @@ export function useExternalLinkFetch({
|
||||||
setExtLink(undefined)
|
setExtLink(undefined)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
} else if (
|
||||||
|
isBskyStartUrl(extLink.uri) ||
|
||||||
|
isBskyStarterPackUrl(extLink.uri)
|
||||||
|
) {
|
||||||
|
getStarterPackAsEmbed(agent, fetchDid, extLink.uri).then(
|
||||||
|
({embed, meta}) => {
|
||||||
|
if (aborted) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setExtLink({
|
||||||
|
uri: extLink.uri,
|
||||||
|
isLoading: false,
|
||||||
|
meta,
|
||||||
|
embed,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
} else if (isShortLink(extLink.uri)) {
|
} else if (isShortLink(extLink.uri)) {
|
||||||
if (isShortLink(extLink.uri)) {
|
if (isShortLink(extLink.uri)) {
|
||||||
resolveShortLink(extLink.uri).then(res => {
|
resolveShortLink(extLink.uri).then(res => {
|
||||||
|
|
|
@ -30,6 +30,7 @@ import {ListEmbed} from './ListEmbed'
|
||||||
import {MaybeQuoteEmbed} from './QuoteEmbed'
|
import {MaybeQuoteEmbed} from './QuoteEmbed'
|
||||||
import hairlineWidth = StyleSheet.hairlineWidth
|
import hairlineWidth = StyleSheet.hairlineWidth
|
||||||
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
|
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
|
||||||
|
import {Embed as StarterPackCard} from '#/components/StarterPack/StarterPackCard'
|
||||||
|
|
||||||
type Embed =
|
type Embed =
|
||||||
| AppBskyEmbedRecord.View
|
| AppBskyEmbedRecord.View
|
||||||
|
@ -90,6 +91,10 @@ export function PostEmbeds({
|
||||||
return <ListEmbed item={embed.record} />
|
return <ListEmbed item={embed.record} />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (AppBskyGraphDefs.isStarterPackViewBasic(embed.record)) {
|
||||||
|
return <StarterPackCard starterPack={embed.record} />
|
||||||
|
}
|
||||||
|
|
||||||
// quote post
|
// quote post
|
||||||
// =
|
// =
|
||||||
return (
|
return (
|
||||||
|
|
Loading…
Reference in New Issue