Show quote posts (#4865)
* show quote posts * fix filter * fix keyExtractor * move likedby and repostedby to new file structure * use modern list component * remove relative imports * update quotes count after quoting * call `onPost` after updating quote count * Revert "update quotes count after quoting" This reverts commit 1f1887730a210c57c1e5a0eb0f47c42c42cf1b4b. * implement * update like count in quotes list * only add `onPostReply` where needed * Filter quotes with detached embeds * Bump SDK * Don't show error for no results --------- Co-authored-by: Samuel Newman <10959775+mozzius@users.noreply.github.com> Co-authored-by: Hailey <me@haileyok.com> Co-authored-by: Eric Bailey <git@esb.lol>zio/stable
parent
ddb0b80017
commit
56ab5e177f
|
@ -255,6 +255,7 @@ func serve(cctx *cli.Context) error {
|
||||||
e.GET("/profile/:handleOrDID/post/:rkey", server.WebPost)
|
e.GET("/profile/:handleOrDID/post/:rkey", server.WebPost)
|
||||||
e.GET("/profile/:handleOrDID/post/:rkey/liked-by", server.WebGeneric)
|
e.GET("/profile/:handleOrDID/post/:rkey/liked-by", server.WebGeneric)
|
||||||
e.GET("/profile/:handleOrDID/post/:rkey/reposted-by", server.WebGeneric)
|
e.GET("/profile/:handleOrDID/post/:rkey/reposted-by", server.WebGeneric)
|
||||||
|
e.GET("/profile/:handleOrDID/post/:rkey/quotes", server.WebGeneric)
|
||||||
|
|
||||||
// starter packs
|
// starter packs
|
||||||
e.GET("/starter-pack/:handleOrDID/:rkey", server.WebStarterPack)
|
e.GET("/starter-pack/:handleOrDID/:rkey", server.WebStarterPack)
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
"open-analyzer": "EXPO_PUBLIC_OPEN_ANALYZER=1 yarn build-web"
|
"open-analyzer": "EXPO_PUBLIC_OPEN_ANALYZER=1 yarn build-web"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@atproto/api": "0.13.0",
|
"@atproto/api": "^0.13.2",
|
||||||
"@bam.tech/react-native-image-resizer": "^3.0.4",
|
"@bam.tech/react-native-image-resizer": "^3.0.4",
|
||||||
"@braintree/sanitize-url": "^6.0.2",
|
"@braintree/sanitize-url": "^6.0.2",
|
||||||
"@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet",
|
"@discord/bottom-sheet": "bluesky-social/react-native-bottom-sheet",
|
||||||
|
|
|
@ -15,10 +15,12 @@ import {
|
||||||
StackActions,
|
StackActions,
|
||||||
} from '@react-navigation/native'
|
} from '@react-navigation/native'
|
||||||
|
|
||||||
import {timeout} from 'lib/async/timeout'
|
import {init as initAnalytics} from '#/lib/analytics/analytics'
|
||||||
import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
|
import {timeout} from '#/lib/async/timeout'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {useColorSchemeStyle} from '#/lib/hooks/useColorSchemeStyle'
|
||||||
import {buildStateObject} from 'lib/routes/helpers'
|
import {usePalette} from '#/lib/hooks/usePalette'
|
||||||
|
import {useWebScrollRestoration} from '#/lib/hooks/useWebScrollRestoration'
|
||||||
|
import {buildStateObject} from '#/lib/routes/helpers'
|
||||||
import {
|
import {
|
||||||
AllNavigatorParams,
|
AllNavigatorParams,
|
||||||
BottomTabNavigatorParams,
|
BottomTabNavigatorParams,
|
||||||
|
@ -28,20 +30,62 @@ import {
|
||||||
MyProfileTabNavigatorParams,
|
MyProfileTabNavigatorParams,
|
||||||
NotificationsTabNavigatorParams,
|
NotificationsTabNavigatorParams,
|
||||||
SearchTabNavigatorParams,
|
SearchTabNavigatorParams,
|
||||||
} from 'lib/routes/types'
|
} from '#/lib/routes/types'
|
||||||
import {RouteParams, State} from 'lib/routes/types'
|
import {RouteParams, State} from '#/lib/routes/types'
|
||||||
import {bskyTitle} from 'lib/strings/headings'
|
import {attachRouteToLogEvents, logEvent} from '#/lib/statsig/statsig'
|
||||||
import {isAndroid, isNative, isWeb} from 'platform/detection'
|
import {bskyTitle} from '#/lib/strings/headings'
|
||||||
|
import {isAndroid, isNative, isWeb} from '#/platform/detection'
|
||||||
|
import {useModalControls} from '#/state/modals'
|
||||||
|
import {useUnreadNotifications} from '#/state/queries/notifications/unread'
|
||||||
|
import {useSession} from '#/state/session'
|
||||||
|
import {
|
||||||
|
shouldRequestEmailConfirmation,
|
||||||
|
snoozeEmailConfirmationPrompt,
|
||||||
|
} from '#/state/shell/reminders'
|
||||||
|
import {AccessibilitySettingsScreen} from '#/view/screens/AccessibilitySettings'
|
||||||
|
import {AppPasswords} from '#/view/screens/AppPasswords'
|
||||||
|
import {CommunityGuidelinesScreen} from '#/view/screens/CommunityGuidelines'
|
||||||
|
import {CopyrightPolicyScreen} from '#/view/screens/CopyrightPolicy'
|
||||||
|
import {DebugModScreen} from '#/view/screens/DebugMod'
|
||||||
|
import {FeedsScreen} from '#/view/screens/Feeds'
|
||||||
|
import {HomeScreen} from '#/view/screens/Home'
|
||||||
|
import {LanguageSettingsScreen} from '#/view/screens/LanguageSettings'
|
||||||
|
import {ListsScreen} from '#/view/screens/Lists'
|
||||||
|
import {LogScreen} from '#/view/screens/Log'
|
||||||
|
import {ModerationBlockedAccounts} from '#/view/screens/ModerationBlockedAccounts'
|
||||||
|
import {ModerationModlistsScreen} from '#/view/screens/ModerationModlists'
|
||||||
|
import {ModerationMutedAccounts} from '#/view/screens/ModerationMutedAccounts'
|
||||||
|
import {NotFoundScreen} from '#/view/screens/NotFound'
|
||||||
|
import {NotificationsScreen} from '#/view/screens/Notifications'
|
||||||
|
import {NotificationsSettingsScreen} from '#/view/screens/NotificationsSettings'
|
||||||
|
import {PostThreadScreen} from '#/view/screens/PostThread'
|
||||||
import {PreferencesExternalEmbeds} from '#/view/screens/PreferencesExternalEmbeds'
|
import {PreferencesExternalEmbeds} from '#/view/screens/PreferencesExternalEmbeds'
|
||||||
import {AppPasswords} from 'view/screens/AppPasswords'
|
import {PreferencesFollowingFeed} from '#/view/screens/PreferencesFollowingFeed'
|
||||||
import {ModerationBlockedAccounts} from 'view/screens/ModerationBlockedAccounts'
|
import {PreferencesThreads} from '#/view/screens/PreferencesThreads'
|
||||||
import {ModerationMutedAccounts} from 'view/screens/ModerationMutedAccounts'
|
import {PrivacyPolicyScreen} from '#/view/screens/PrivacyPolicy'
|
||||||
import {PreferencesFollowingFeed} from 'view/screens/PreferencesFollowingFeed'
|
import {ProfileScreen} from '#/view/screens/Profile'
|
||||||
import {PreferencesThreads} from 'view/screens/PreferencesThreads'
|
import {ProfileFeedScreen} from '#/view/screens/ProfileFeed'
|
||||||
import {SavedFeeds} from 'view/screens/SavedFeeds'
|
import {ProfileFeedLikedByScreen} from '#/view/screens/ProfileFeedLikedBy'
|
||||||
|
import {ProfileFollowersScreen} from '#/view/screens/ProfileFollowers'
|
||||||
|
import {ProfileFollowsScreen} from '#/view/screens/ProfileFollows'
|
||||||
|
import {ProfileListScreen} from '#/view/screens/ProfileList'
|
||||||
|
import {SavedFeeds} from '#/view/screens/SavedFeeds'
|
||||||
|
import {SearchScreen} from '#/view/screens/Search'
|
||||||
|
import {SettingsScreen} from '#/view/screens/Settings'
|
||||||
|
import {Storybook} from '#/view/screens/Storybook'
|
||||||
|
import {SupportScreen} from '#/view/screens/Support'
|
||||||
|
import {TermsOfServiceScreen} from '#/view/screens/TermsOfService'
|
||||||
|
import {BottomBar} from '#/view/shell/bottom-bar/BottomBar'
|
||||||
|
import {createNativeStackNavigatorWithAuth} from '#/view/shell/createNativeStackNavigatorWithAuth'
|
||||||
import {SharedPreferencesTesterScreen} from '#/screens/E2E/SharedPreferencesTesterScreen'
|
import {SharedPreferencesTesterScreen} from '#/screens/E2E/SharedPreferencesTesterScreen'
|
||||||
import HashtagScreen from '#/screens/Hashtag'
|
import HashtagScreen from '#/screens/Hashtag'
|
||||||
|
import {MessagesConversationScreen} from '#/screens/Messages/Conversation'
|
||||||
|
import {MessagesScreen} from '#/screens/Messages/List'
|
||||||
|
import {MessagesSettingsScreen} from '#/screens/Messages/Settings'
|
||||||
import {ModerationScreen} from '#/screens/Moderation'
|
import {ModerationScreen} from '#/screens/Moderation'
|
||||||
|
import {PostLikedByScreen} from '#/screens/Post/PostLikedBy'
|
||||||
|
import {PostQuotesScreen} from '#/screens/Post/PostQuotes'
|
||||||
|
import {PostRepostedByScreen} from '#/screens/Post/PostRepostedBy'
|
||||||
import {ProfileKnownFollowersScreen} from '#/screens/Profile/KnownFollowers'
|
import {ProfileKnownFollowersScreen} from '#/screens/Profile/KnownFollowers'
|
||||||
import {ProfileLabelerLikedByScreen} from '#/screens/Profile/ProfileLabelerLikedBy'
|
import {ProfileLabelerLikedByScreen} from '#/screens/Profile/ProfileLabelerLikedBy'
|
||||||
import {AppearanceSettingsScreen} from '#/screens/Settings/AppearanceSettings'
|
import {AppearanceSettingsScreen} from '#/screens/Settings/AppearanceSettings'
|
||||||
|
@ -50,51 +94,8 @@ import {
|
||||||
StarterPackScreenShort,
|
StarterPackScreenShort,
|
||||||
} from '#/screens/StarterPack/StarterPackScreen'
|
} from '#/screens/StarterPack/StarterPackScreen'
|
||||||
import {Wizard} from '#/screens/StarterPack/Wizard'
|
import {Wizard} from '#/screens/StarterPack/Wizard'
|
||||||
|
import {router} from '#/routes'
|
||||||
import {Referrer} from '../modules/expo-bluesky-swiss-army'
|
import {Referrer} from '../modules/expo-bluesky-swiss-army'
|
||||||
import {init as initAnalytics} from './lib/analytics/analytics'
|
|
||||||
import {useWebScrollRestoration} from './lib/hooks/useWebScrollRestoration'
|
|
||||||
import {attachRouteToLogEvents, logEvent} from './lib/statsig/statsig'
|
|
||||||
import {router} from './routes'
|
|
||||||
import {MessagesConversationScreen} from './screens/Messages/Conversation'
|
|
||||||
import {MessagesScreen} from './screens/Messages/List'
|
|
||||||
import {MessagesSettingsScreen} from './screens/Messages/Settings'
|
|
||||||
import {useModalControls} from './state/modals'
|
|
||||||
import {useUnreadNotifications} from './state/queries/notifications/unread'
|
|
||||||
import {useSession} from './state/session'
|
|
||||||
import {
|
|
||||||
shouldRequestEmailConfirmation,
|
|
||||||
snoozeEmailConfirmationPrompt,
|
|
||||||
} from './state/shell/reminders'
|
|
||||||
import {AccessibilitySettingsScreen} from './view/screens/AccessibilitySettings'
|
|
||||||
import {CommunityGuidelinesScreen} from './view/screens/CommunityGuidelines'
|
|
||||||
import {CopyrightPolicyScreen} from './view/screens/CopyrightPolicy'
|
|
||||||
import {DebugModScreen} from './view/screens/DebugMod'
|
|
||||||
import {FeedsScreen} from './view/screens/Feeds'
|
|
||||||
import {HomeScreen} from './view/screens/Home'
|
|
||||||
import {LanguageSettingsScreen} from './view/screens/LanguageSettings'
|
|
||||||
import {ListsScreen} from './view/screens/Lists'
|
|
||||||
import {LogScreen} from './view/screens/Log'
|
|
||||||
import {ModerationModlistsScreen} from './view/screens/ModerationModlists'
|
|
||||||
import {NotFoundScreen} from './view/screens/NotFound'
|
|
||||||
import {NotificationsScreen} from './view/screens/Notifications'
|
|
||||||
import {NotificationsSettingsScreen} from './view/screens/NotificationsSettings'
|
|
||||||
import {PostLikedByScreen} from './view/screens/PostLikedBy'
|
|
||||||
import {PostRepostedByScreen} from './view/screens/PostRepostedBy'
|
|
||||||
import {PostThreadScreen} from './view/screens/PostThread'
|
|
||||||
import {PrivacyPolicyScreen} from './view/screens/PrivacyPolicy'
|
|
||||||
import {ProfileScreen} from './view/screens/Profile'
|
|
||||||
import {ProfileFeedScreen} from './view/screens/ProfileFeed'
|
|
||||||
import {ProfileFeedLikedByScreen} from './view/screens/ProfileFeedLikedBy'
|
|
||||||
import {ProfileFollowersScreen} from './view/screens/ProfileFollowers'
|
|
||||||
import {ProfileFollowsScreen} from './view/screens/ProfileFollows'
|
|
||||||
import {ProfileListScreen} from './view/screens/ProfileList'
|
|
||||||
import {SearchScreen} from './view/screens/Search'
|
|
||||||
import {SettingsScreen} from './view/screens/Settings'
|
|
||||||
import {Storybook} from './view/screens/Storybook'
|
|
||||||
import {SupportScreen} from './view/screens/Support'
|
|
||||||
import {TermsOfServiceScreen} from './view/screens/TermsOfService'
|
|
||||||
import {BottomBar} from './view/shell/bottom-bar/BottomBar'
|
|
||||||
import {createNativeStackNavigatorWithAuth} from './view/shell/createNativeStackNavigatorWithAuth'
|
|
||||||
|
|
||||||
const navigationRef = createNavigationContainerRef<AllNavigatorParams>()
|
const navigationRef = createNavigationContainerRef<AllNavigatorParams>()
|
||||||
|
|
||||||
|
@ -212,6 +213,13 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) {
|
||||||
title: title(msg`Post by @${route.params.name}`),
|
title: title(msg`Post by @${route.params.name}`),
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="PostQuotes"
|
||||||
|
getComponent={() => PostQuotesScreen}
|
||||||
|
options={({route}) => ({
|
||||||
|
title: title(msg`Post by @${route.params.name}`),
|
||||||
|
})}
|
||||||
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="ProfileFeed"
|
name="ProfileFeed"
|
||||||
getComponent={() => ProfileFeedScreen}
|
getComponent={() => ProfileFeedScreen}
|
||||||
|
|
|
@ -20,6 +20,7 @@ export type CommonNavigatorParams = {
|
||||||
PostThread: {name: string; rkey: string}
|
PostThread: {name: string; rkey: string}
|
||||||
PostLikedBy: {name: string; rkey: string}
|
PostLikedBy: {name: string; rkey: string}
|
||||||
PostRepostedBy: {name: string; rkey: string}
|
PostRepostedBy: {name: string; rkey: string}
|
||||||
|
PostQuotes: {name: string; rkey: string}
|
||||||
ProfileFeed: {name: string; rkey: string}
|
ProfileFeed: {name: string; rkey: string}
|
||||||
ProfileFeedLikedBy: {name: string; rkey: string}
|
ProfileFeedLikedBy: {name: string; rkey: string}
|
||||||
ProfileLabelerLikedBy: {name: string}
|
ProfileLabelerLikedBy: {name: string}
|
||||||
|
|
|
@ -21,6 +21,7 @@ export const router = new Router({
|
||||||
PostThread: '/profile/:name/post/:rkey',
|
PostThread: '/profile/:name/post/:rkey',
|
||||||
PostLikedBy: '/profile/:name/post/:rkey/liked-by',
|
PostLikedBy: '/profile/:name/post/:rkey/liked-by',
|
||||||
PostRepostedBy: '/profile/:name/post/:rkey/reposted-by',
|
PostRepostedBy: '/profile/:name/post/:rkey/reposted-by',
|
||||||
|
PostQuotes: '/profile/:name/post/:rkey/quotes',
|
||||||
ProfileFeed: '/profile/:name/feed/:rkey',
|
ProfileFeed: '/profile/:name/feed/:rkey',
|
||||||
ProfileFeedLikedBy: '/profile/:name/feed/:rkey/liked-by',
|
ProfileFeedLikedBy: '/profile/:name/feed/:rkey/liked-by',
|
||||||
ProfileLabelerLikedBy: '/profile/:name/labeler/liked-by',
|
ProfileLabelerLikedBy: '/profile/:name/labeler/liked-by',
|
||||||
|
|
|
@ -4,11 +4,12 @@ import {msg} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {useFocusEffect} from '@react-navigation/native'
|
import {useFocusEffect} from '@react-navigation/native'
|
||||||
|
|
||||||
|
import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
|
||||||
|
import {makeRecordUri} from '#/lib/strings/url-helpers'
|
||||||
import {useSetMinimalShellMode} from '#/state/shell'
|
import {useSetMinimalShellMode} from '#/state/shell'
|
||||||
import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types'
|
import {PostLikedBy as PostLikedByComponent} from '#/view/com/post-thread/PostLikedBy'
|
||||||
import {makeRecordUri} from 'lib/strings/url-helpers'
|
import {ViewHeader} from '#/view/com/util/ViewHeader'
|
||||||
import {PostLikedBy as PostLikedByComponent} from '../com/post-thread/PostLikedBy'
|
import {atoms as a} from '#/alf'
|
||||||
import {ViewHeader} from '../com/util/ViewHeader'
|
|
||||||
|
|
||||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostLikedBy'>
|
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostLikedBy'>
|
||||||
export const PostLikedByScreen = ({route}: Props) => {
|
export const PostLikedByScreen = ({route}: Props) => {
|
||||||
|
@ -24,7 +25,7 @@ export const PostLikedByScreen = ({route}: Props) => {
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={{flex: 1}}>
|
<View style={a.flex_1}>
|
||||||
<ViewHeader title={_(msg`Liked By`)} />
|
<ViewHeader title={_(msg`Liked By`)} />
|
||||||
<PostLikedByComponent uri={uri} />
|
<PostLikedByComponent uri={uri} />
|
||||||
</View>
|
</View>
|
|
@ -0,0 +1,33 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {View} from 'react-native'
|
||||||
|
import {msg} from '@lingui/macro'
|
||||||
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {useFocusEffect} from '@react-navigation/native'
|
||||||
|
|
||||||
|
import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
|
||||||
|
import {makeRecordUri} from '#/lib/strings/url-helpers'
|
||||||
|
import {useSetMinimalShellMode} from '#/state/shell'
|
||||||
|
import {PostQuotes as PostQuotesComponent} from '#/view/com/post-thread/PostQuotes'
|
||||||
|
import {ViewHeader} from '#/view/com/util/ViewHeader'
|
||||||
|
import {atoms as a} from '#/alf'
|
||||||
|
|
||||||
|
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostQuotes'>
|
||||||
|
export const PostQuotesScreen = ({route}: Props) => {
|
||||||
|
const setMinimalShellMode = useSetMinimalShellMode()
|
||||||
|
const {name, rkey} = route.params
|
||||||
|
const uri = makeRecordUri(name, 'app.bsky.feed.post', rkey)
|
||||||
|
const {_} = useLingui()
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
React.useCallback(() => {
|
||||||
|
setMinimalShellMode(false)
|
||||||
|
}, [setMinimalShellMode]),
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={a.flex_1}>
|
||||||
|
<ViewHeader title={_(msg`Quotes`)} />
|
||||||
|
<PostQuotesComponent uri={uri} />
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
|
@ -4,11 +4,12 @@ import {msg} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {useFocusEffect} from '@react-navigation/native'
|
import {useFocusEffect} from '@react-navigation/native'
|
||||||
|
|
||||||
|
import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
|
||||||
|
import {makeRecordUri} from '#/lib/strings/url-helpers'
|
||||||
import {useSetMinimalShellMode} from '#/state/shell'
|
import {useSetMinimalShellMode} from '#/state/shell'
|
||||||
import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types'
|
import {PostRepostedBy as PostRepostedByComponent} from '#/view/com/post-thread/PostRepostedBy'
|
||||||
import {makeRecordUri} from 'lib/strings/url-helpers'
|
import {ViewHeader} from '#/view/com/util/ViewHeader'
|
||||||
import {PostRepostedBy as PostRepostedByComponent} from '../com/post-thread/PostRepostedBy'
|
import {atoms as a} from '#/alf'
|
||||||
import {ViewHeader} from '../com/util/ViewHeader'
|
|
||||||
|
|
||||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostRepostedBy'>
|
type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostRepostedBy'>
|
||||||
export const PostRepostedByScreen = ({route}: Props) => {
|
export const PostRepostedByScreen = ({route}: Props) => {
|
||||||
|
@ -24,7 +25,7 @@ export const PostRepostedByScreen = ({route}: Props) => {
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={{flex: 1}}>
|
<View style={a.flex_1}>
|
||||||
<ViewHeader title={_(msg`Reposted By`)} />
|
<ViewHeader title={_(msg`Reposted By`)} />
|
||||||
<PostRepostedByComponent uri={uri} />
|
<PostRepostedByComponent uri={uri} />
|
||||||
</View>
|
</View>
|
|
@ -6,6 +6,7 @@ import EventEmitter from 'eventemitter3'
|
||||||
import {batchedUpdates} from '#/lib/batchedUpdates'
|
import {batchedUpdates} from '#/lib/batchedUpdates'
|
||||||
import {findAllPostsInQueryData as findAllPostsInNotifsQueryData} from '../queries/notifications/feed'
|
import {findAllPostsInQueryData as findAllPostsInNotifsQueryData} from '../queries/notifications/feed'
|
||||||
import {findAllPostsInQueryData as findAllPostsInFeedQueryData} from '../queries/post-feed'
|
import {findAllPostsInQueryData as findAllPostsInFeedQueryData} from '../queries/post-feed'
|
||||||
|
import {findAllPostsInQueryData as findAllPostsInQuoteQueryData} from '../queries/post-quotes'
|
||||||
import {findAllPostsInQueryData as findAllPostsInThreadQueryData} from '../queries/post-thread'
|
import {findAllPostsInQueryData as findAllPostsInThreadQueryData} from '../queries/post-thread'
|
||||||
import {findAllPostsInQueryData as findAllPostsInSearchQueryData} from '../queries/search-posts'
|
import {findAllPostsInQueryData as findAllPostsInSearchQueryData} from '../queries/search-posts'
|
||||||
import {castAsShadow, Shadow} from './types'
|
import {castAsShadow, Shadow} from './types'
|
||||||
|
@ -130,4 +131,7 @@ function* findPostsInCache(
|
||||||
for (let post of findAllPostsInSearchQueryData(queryClient, uri)) {
|
for (let post of findAllPostsInSearchQueryData(queryClient, uri)) {
|
||||||
yield post
|
yield post
|
||||||
}
|
}
|
||||||
|
for (let post of findAllPostsInQuoteQueryData(queryClient, uri)) {
|
||||||
|
yield post
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {findAllProfilesInQueryData as findAllProfilesInMyBlockedAccountsQueryDat
|
||||||
import {findAllProfilesInQueryData as findAllProfilesInMyMutedAccountsQueryData} from '../queries/my-muted-accounts'
|
import {findAllProfilesInQueryData as findAllProfilesInMyMutedAccountsQueryData} from '../queries/my-muted-accounts'
|
||||||
import {findAllProfilesInQueryData as findAllProfilesInFeedsQueryData} from '../queries/post-feed'
|
import {findAllProfilesInQueryData as findAllProfilesInFeedsQueryData} from '../queries/post-feed'
|
||||||
import {findAllProfilesInQueryData as findAllProfilesInPostLikedByQueryData} from '../queries/post-liked-by'
|
import {findAllProfilesInQueryData as findAllProfilesInPostLikedByQueryData} from '../queries/post-liked-by'
|
||||||
|
import {findAllProfilesInQueryData as findAllProfilesInPostQuotesQueryData} from '../queries/post-quotes'
|
||||||
import {findAllProfilesInQueryData as findAllProfilesInPostRepostedByQueryData} from '../queries/post-reposted-by'
|
import {findAllProfilesInQueryData as findAllProfilesInPostRepostedByQueryData} from '../queries/post-reposted-by'
|
||||||
import {findAllProfilesInQueryData as findAllProfilesInPostThreadQueryData} from '../queries/post-thread'
|
import {findAllProfilesInQueryData as findAllProfilesInPostThreadQueryData} from '../queries/post-thread'
|
||||||
import {findAllProfilesInQueryData as findAllProfilesInProfileQueryData} from '../queries/profile'
|
import {findAllProfilesInQueryData as findAllProfilesInProfileQueryData} from '../queries/profile'
|
||||||
|
@ -104,6 +105,7 @@ function* findProfilesInCache(
|
||||||
yield* findAllProfilesInMyMutedAccountsQueryData(queryClient, did)
|
yield* findAllProfilesInMyMutedAccountsQueryData(queryClient, did)
|
||||||
yield* findAllProfilesInPostLikedByQueryData(queryClient, did)
|
yield* findAllProfilesInPostLikedByQueryData(queryClient, did)
|
||||||
yield* findAllProfilesInPostRepostedByQueryData(queryClient, did)
|
yield* findAllProfilesInPostRepostedByQueryData(queryClient, did)
|
||||||
|
yield* findAllProfilesInPostQuotesQueryData(queryClient, did)
|
||||||
yield* findAllProfilesInProfileQueryData(queryClient, did)
|
yield* findAllProfilesInProfileQueryData(queryClient, did)
|
||||||
yield* findAllProfilesInProfileFollowersQueryData(queryClient, did)
|
yield* findAllProfilesInProfileFollowersQueryData(queryClient, did)
|
||||||
yield* findAllProfilesInProfileFollowsQueryData(queryClient, did)
|
yield* findAllProfilesInProfileFollowsQueryData(queryClient, did)
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
import {
|
||||||
|
AppBskyActorDefs,
|
||||||
|
AppBskyEmbedRecord,
|
||||||
|
AppBskyFeedDefs,
|
||||||
|
AppBskyFeedGetQuotes,
|
||||||
|
AtUri,
|
||||||
|
} from '@atproto/api'
|
||||||
|
import {
|
||||||
|
InfiniteData,
|
||||||
|
QueryClient,
|
||||||
|
QueryKey,
|
||||||
|
useInfiniteQuery,
|
||||||
|
} from '@tanstack/react-query'
|
||||||
|
|
||||||
|
import {useAgent} from '#/state/session'
|
||||||
|
import {
|
||||||
|
didOrHandleUriMatches,
|
||||||
|
embedViewRecordToPostView,
|
||||||
|
getEmbeddedPost,
|
||||||
|
} from './util'
|
||||||
|
|
||||||
|
const PAGE_SIZE = 30
|
||||||
|
type RQPageParam = string | undefined
|
||||||
|
|
||||||
|
const RQKEY_ROOT = 'post-quotes'
|
||||||
|
export const RQKEY = (resolvedUri: string) => [RQKEY_ROOT, resolvedUri]
|
||||||
|
|
||||||
|
export function usePostQuotesQuery(resolvedUri: string | undefined) {
|
||||||
|
const agent = useAgent()
|
||||||
|
return useInfiniteQuery<
|
||||||
|
AppBskyFeedGetQuotes.OutputSchema,
|
||||||
|
Error,
|
||||||
|
InfiniteData<AppBskyFeedGetQuotes.OutputSchema>,
|
||||||
|
QueryKey,
|
||||||
|
RQPageParam
|
||||||
|
>({
|
||||||
|
queryKey: RQKEY(resolvedUri || ''),
|
||||||
|
async queryFn({pageParam}: {pageParam: RQPageParam}) {
|
||||||
|
const res = await agent.api.app.bsky.feed.getQuotes({
|
||||||
|
uri: resolvedUri || '',
|
||||||
|
limit: PAGE_SIZE,
|
||||||
|
cursor: pageParam,
|
||||||
|
})
|
||||||
|
return res.data
|
||||||
|
},
|
||||||
|
initialPageParam: undefined,
|
||||||
|
getNextPageParam: lastPage => lastPage.cursor,
|
||||||
|
enabled: !!resolvedUri,
|
||||||
|
select: data => {
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
pages: data.pages.map(page => {
|
||||||
|
return {
|
||||||
|
...page,
|
||||||
|
posts: page.posts.filter(post => {
|
||||||
|
if (post.embed && AppBskyEmbedRecord.isView(post.embed)) {
|
||||||
|
if (AppBskyEmbedRecord.isViewDetached(post.embed.record)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* findAllProfilesInQueryData(
|
||||||
|
queryClient: QueryClient,
|
||||||
|
did: string,
|
||||||
|
): Generator<AppBskyActorDefs.ProfileView, void> {
|
||||||
|
const queryDatas = queryClient.getQueriesData<
|
||||||
|
InfiniteData<AppBskyFeedGetQuotes.OutputSchema>
|
||||||
|
>({
|
||||||
|
queryKey: [RQKEY_ROOT],
|
||||||
|
})
|
||||||
|
for (const [_queryKey, queryData] of queryDatas) {
|
||||||
|
if (!queryData?.pages) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for (const page of queryData?.pages) {
|
||||||
|
for (const item of page.posts) {
|
||||||
|
if (item.author.did === did) {
|
||||||
|
yield item.author
|
||||||
|
}
|
||||||
|
const quotedPost = getEmbeddedPost(item.embed)
|
||||||
|
if (quotedPost?.author.did === did) {
|
||||||
|
yield quotedPost.author
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* findAllPostsInQueryData(
|
||||||
|
queryClient: QueryClient,
|
||||||
|
uri: string,
|
||||||
|
): Generator<AppBskyFeedDefs.PostView, undefined> {
|
||||||
|
const queryDatas = queryClient.getQueriesData<
|
||||||
|
InfiniteData<AppBskyFeedGetQuotes.OutputSchema>
|
||||||
|
>({
|
||||||
|
queryKey: [RQKEY_ROOT],
|
||||||
|
})
|
||||||
|
const atUri = new AtUri(uri)
|
||||||
|
for (const [_queryKey, queryData] of queryDatas) {
|
||||||
|
if (!queryData?.pages) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for (const page of queryData?.pages) {
|
||||||
|
for (const post of page.posts) {
|
||||||
|
if (didOrHandleUriMatches(atUri, post)) {
|
||||||
|
yield post
|
||||||
|
}
|
||||||
|
|
||||||
|
const quotedPost = getEmbeddedPost(post.embed)
|
||||||
|
if (quotedPost && didOrHandleUriMatches(atUri, quotedPost)) {
|
||||||
|
yield embedViewRecordToPostView(quotedPost)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,6 +34,7 @@ export interface ComposerOpts {
|
||||||
replyTo?: ComposerOptsPostRef
|
replyTo?: ComposerOptsPostRef
|
||||||
onPost?: (postUri: string | undefined) => void
|
onPost?: (postUri: string | undefined) => void
|
||||||
quote?: ComposerOptsQuote
|
quote?: ComposerOptsQuote
|
||||||
|
quoteCount?: number
|
||||||
mention?: string // handle of user to mention
|
mention?: string // handle of user to mention
|
||||||
openPicker?: (pos: DOMRect | undefined) => void
|
openPicker?: (pos: DOMRect | undefined) => void
|
||||||
text?: string
|
text?: string
|
||||||
|
|
|
@ -116,6 +116,7 @@ export const ComposePost = observer(function ComposePost({
|
||||||
replyTo,
|
replyTo,
|
||||||
onPost,
|
onPost,
|
||||||
quote: initQuote,
|
quote: initQuote,
|
||||||
|
quoteCount,
|
||||||
mention: initMention,
|
mention: initMention,
|
||||||
openPicker,
|
openPicker,
|
||||||
text: initText,
|
text: initText,
|
||||||
|
@ -392,7 +393,22 @@ export const ComposePost = observer(function ComposePost({
|
||||||
emitPostCreated()
|
emitPostCreated()
|
||||||
}
|
}
|
||||||
setLangPrefs.savePostLanguageToHistory()
|
setLangPrefs.savePostLanguageToHistory()
|
||||||
onPost?.(postUri)
|
if (quote) {
|
||||||
|
// We want to wait for the quote count to update before we call `onPost`, which will refetch data
|
||||||
|
whenAppViewReady(agent, quote.uri, res => {
|
||||||
|
const thread = res.data.thread
|
||||||
|
if (
|
||||||
|
AppBskyFeedDefs.isThreadViewPost(thread) &&
|
||||||
|
thread.post.quoteCount !== quoteCount
|
||||||
|
) {
|
||||||
|
onPost?.(postUri)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
onPost?.(postUri)
|
||||||
|
}
|
||||||
onClose()
|
onClose()
|
||||||
Toast.show(
|
Toast.show(
|
||||||
replyTo
|
replyTo
|
||||||
|
|
|
@ -8,13 +8,13 @@ import {logger} from '#/logger'
|
||||||
import {useLikedByQuery} from '#/state/queries/post-liked-by'
|
import {useLikedByQuery} from '#/state/queries/post-liked-by'
|
||||||
import {useResolveUriQuery} from '#/state/queries/resolve-uri'
|
import {useResolveUriQuery} from '#/state/queries/resolve-uri'
|
||||||
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
||||||
|
import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
|
||||||
|
import {List} from '#/view/com/util/List'
|
||||||
import {
|
import {
|
||||||
ListFooter,
|
ListFooter,
|
||||||
ListHeaderDesktop,
|
ListHeaderDesktop,
|
||||||
ListMaybePlaceholder,
|
ListMaybePlaceholder,
|
||||||
} from '#/components/Lists'
|
} from '#/components/Lists'
|
||||||
import {ProfileCardWithFollowBtn} from '../profile/ProfileCard'
|
|
||||||
import {List} from '../util/List'
|
|
||||||
|
|
||||||
function renderItem({item}: {item: GetLikes.Like}) {
|
function renderItem({item}: {item: GetLikes.Like}) {
|
||||||
return <ProfileCardWithFollowBtn key={item.actor.did} profile={item.actor} />
|
return <ProfileCardWithFollowBtn key={item.actor.did} profile={item.actor} />
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
import React, {useCallback, useState} from 'react'
|
||||||
|
import {
|
||||||
|
AppBskyFeedDefs,
|
||||||
|
AppBskyFeedPost,
|
||||||
|
ModerationDecision,
|
||||||
|
} from '@atproto/api'
|
||||||
|
import {msg} from '@lingui/macro'
|
||||||
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
|
import {moderatePost_wrapped as moderatePost} from '#/lib/moderatePost_wrapped'
|
||||||
|
import {cleanError} from '#/lib/strings/errors'
|
||||||
|
import {logger} from '#/logger'
|
||||||
|
import {isWeb} from '#/platform/detection'
|
||||||
|
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||||
|
import {usePostQuotesQuery} from '#/state/queries/post-quotes'
|
||||||
|
import {useResolveUriQuery} from '#/state/queries/resolve-uri'
|
||||||
|
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
||||||
|
import {Post} from 'view/com/post/Post'
|
||||||
|
import {
|
||||||
|
ListFooter,
|
||||||
|
ListHeaderDesktop,
|
||||||
|
ListMaybePlaceholder,
|
||||||
|
} from '#/components/Lists'
|
||||||
|
import {List} from '../util/List'
|
||||||
|
|
||||||
|
function renderItem({
|
||||||
|
item,
|
||||||
|
index,
|
||||||
|
}: {
|
||||||
|
item: {
|
||||||
|
post: AppBskyFeedDefs.PostView
|
||||||
|
moderation: ModerationDecision
|
||||||
|
record: AppBskyFeedPost.Record
|
||||||
|
}
|
||||||
|
index: number
|
||||||
|
}) {
|
||||||
|
return <Post post={item.post} hideTopBorder={index === 0 && !isWeb} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function keyExtractor(item: {
|
||||||
|
post: AppBskyFeedDefs.PostView
|
||||||
|
moderation: ModerationDecision
|
||||||
|
record: AppBskyFeedPost.Record
|
||||||
|
}) {
|
||||||
|
return item.post.uri
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PostQuotes({uri}: {uri: string}) {
|
||||||
|
const {_} = useLingui()
|
||||||
|
const initialNumToRender = useInitialNumToRender()
|
||||||
|
|
||||||
|
const [isPTRing, setIsPTRing] = useState(false)
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: resolvedUri,
|
||||||
|
error: resolveError,
|
||||||
|
isLoading: isLoadingUri,
|
||||||
|
} = useResolveUriQuery(uri)
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isLoading: isLoadingQuotes,
|
||||||
|
isFetchingNextPage,
|
||||||
|
hasNextPage,
|
||||||
|
fetchNextPage,
|
||||||
|
error,
|
||||||
|
refetch,
|
||||||
|
} = usePostQuotesQuery(resolvedUri?.uri)
|
||||||
|
|
||||||
|
const moderationOpts = useModerationOpts()
|
||||||
|
|
||||||
|
const isError = Boolean(resolveError || error)
|
||||||
|
|
||||||
|
const quotes =
|
||||||
|
data?.pages
|
||||||
|
.flatMap(page =>
|
||||||
|
page.posts.map(post => {
|
||||||
|
if (!AppBskyFeedPost.isRecord(post.record) || !moderationOpts) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const moderation = moderatePost(post, moderationOpts)
|
||||||
|
return {post, record: post.record, moderation}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.filter(item => item !== null) ?? []
|
||||||
|
|
||||||
|
const onRefresh = useCallback(async () => {
|
||||||
|
setIsPTRing(true)
|
||||||
|
try {
|
||||||
|
await refetch()
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Failed to refresh quotes', {message: err})
|
||||||
|
}
|
||||||
|
setIsPTRing(false)
|
||||||
|
}, [refetch, setIsPTRing])
|
||||||
|
|
||||||
|
const onEndReached = useCallback(async () => {
|
||||||
|
if (isFetchingNextPage || !hasNextPage || isError) return
|
||||||
|
try {
|
||||||
|
await fetchNextPage()
|
||||||
|
} catch (err) {
|
||||||
|
logger.error('Failed to load more quotes', {message: err})
|
||||||
|
}
|
||||||
|
}, [isFetchingNextPage, hasNextPage, isError, fetchNextPage])
|
||||||
|
|
||||||
|
if (isLoadingUri || isLoadingQuotes || isError) {
|
||||||
|
return (
|
||||||
|
<ListMaybePlaceholder
|
||||||
|
isLoading={isLoadingUri || isLoadingQuotes}
|
||||||
|
isError={isError}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// loaded
|
||||||
|
// =
|
||||||
|
return (
|
||||||
|
<List
|
||||||
|
data={quotes}
|
||||||
|
renderItem={renderItem}
|
||||||
|
keyExtractor={keyExtractor}
|
||||||
|
refreshing={isPTRing}
|
||||||
|
onRefresh={onRefresh}
|
||||||
|
onEndReached={onEndReached}
|
||||||
|
onEndReachedThreshold={4}
|
||||||
|
ListHeaderComponent={<ListHeaderDesktop title={_(msg`Quotes`)} />}
|
||||||
|
ListFooterComponent={
|
||||||
|
<ListFooter
|
||||||
|
isFetchingNextPage={isFetchingNextPage}
|
||||||
|
error={cleanError(error)}
|
||||||
|
onRetry={fetchNextPage}
|
||||||
|
showEndMessage
|
||||||
|
endMessageText={_(msg`That's all, folks!`)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
// @ts-ignore our .web version only -prf
|
||||||
|
desktopFixedHeight
|
||||||
|
initialNumToRender={initialNumToRender}
|
||||||
|
windowSize={11}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
|
@ -8,13 +8,13 @@ import {logger} from '#/logger'
|
||||||
import {usePostRepostedByQuery} from '#/state/queries/post-reposted-by'
|
import {usePostRepostedByQuery} from '#/state/queries/post-reposted-by'
|
||||||
import {useResolveUriQuery} from '#/state/queries/resolve-uri'
|
import {useResolveUriQuery} from '#/state/queries/resolve-uri'
|
||||||
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
import {useInitialNumToRender} from 'lib/hooks/useInitialNumToRender'
|
||||||
|
import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
|
||||||
|
import {List} from '#/view/com/util/List'
|
||||||
import {
|
import {
|
||||||
ListFooter,
|
ListFooter,
|
||||||
ListHeaderDesktop,
|
ListHeaderDesktop,
|
||||||
ListMaybePlaceholder,
|
ListMaybePlaceholder,
|
||||||
} from '#/components/Lists'
|
} from '#/components/Lists'
|
||||||
import {ProfileCardWithFollowBtn} from '../profile/ProfileCard'
|
|
||||||
import {List} from '../util/List'
|
|
||||||
|
|
||||||
function renderItem({item}: {item: ActorDefs.ProfileViewBasic}) {
|
function renderItem({item}: {item: ActorDefs.ProfileViewBasic}) {
|
||||||
return <ProfileCardWithFollowBtn key={item.did} profile={item} />
|
return <ProfileCardWithFollowBtn key={item.did} profile={item} />
|
||||||
|
|
|
@ -199,6 +199,11 @@ let PostThreadItemLoaded = ({
|
||||||
return makeProfileLink(post.author, 'post', urip.rkey, 'reposted-by')
|
return makeProfileLink(post.author, 'post', urip.rkey, 'reposted-by')
|
||||||
}, [post.uri, post.author])
|
}, [post.uri, post.author])
|
||||||
const repostsTitle = _(msg`Reposts of this post`)
|
const repostsTitle = _(msg`Reposts of this post`)
|
||||||
|
const quotesHref = React.useMemo(() => {
|
||||||
|
const urip = new AtUri(post.uri)
|
||||||
|
return makeProfileLink(post.author, 'post', urip.rkey, 'quotes')
|
||||||
|
}, [post.uri, post.author])
|
||||||
|
const quotesTitle = _(msg`Quotes of this post`)
|
||||||
|
|
||||||
const translatorUrl = getTranslatorLink(
|
const translatorUrl = getTranslatorLink(
|
||||||
record?.text || '',
|
record?.text || '',
|
||||||
|
@ -343,7 +348,9 @@ let PostThreadItemLoaded = ({
|
||||||
translatorUrl={translatorUrl}
|
translatorUrl={translatorUrl}
|
||||||
needsTranslation={needsTranslation}
|
needsTranslation={needsTranslation}
|
||||||
/>
|
/>
|
||||||
{post.repostCount !== 0 || post.likeCount !== 0 ? (
|
{post.repostCount !== 0 ||
|
||||||
|
post.likeCount !== 0 ||
|
||||||
|
post.quoteCount !== 0 ? (
|
||||||
// Show this section unless we're *sure* it has no engagement.
|
// Show this section unless we're *sure* it has no engagement.
|
||||||
<View style={[styles.expandedInfo, pal.border]}>
|
<View style={[styles.expandedInfo, pal.border]}>
|
||||||
{post.repostCount != null && post.repostCount !== 0 ? (
|
{post.repostCount != null && post.repostCount !== 0 ? (
|
||||||
|
@ -382,6 +389,26 @@ let PostThreadItemLoaded = ({
|
||||||
</Text>
|
</Text>
|
||||||
</Link>
|
</Link>
|
||||||
) : null}
|
) : null}
|
||||||
|
{post.quoteCount != null && post.quoteCount !== 0 ? (
|
||||||
|
<Link
|
||||||
|
style={styles.expandedInfoItem}
|
||||||
|
href={quotesHref}
|
||||||
|
title={quotesTitle}>
|
||||||
|
<Text
|
||||||
|
testID="quoteCount-expanded"
|
||||||
|
type="lg"
|
||||||
|
style={pal.textLight}>
|
||||||
|
<Text type="xl-bold" style={pal.text}>
|
||||||
|
{formatCount(post.quoteCount)}
|
||||||
|
</Text>{' '}
|
||||||
|
<Plural
|
||||||
|
value={post.quoteCount}
|
||||||
|
one="quote"
|
||||||
|
other="quotes"
|
||||||
|
/>
|
||||||
|
</Text>
|
||||||
|
</Link>
|
||||||
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
) : null}
|
) : null}
|
||||||
<View style={[s.pl10, s.pr10]}>
|
<View style={[s.pl10, s.pr10]}>
|
||||||
|
@ -391,6 +418,7 @@ let PostThreadItemLoaded = ({
|
||||||
record={record}
|
record={record}
|
||||||
richText={richText}
|
richText={richText}
|
||||||
onPressReply={onPressReply}
|
onPressReply={onPressReply}
|
||||||
|
onPostReply={onPostReply}
|
||||||
logContext="PostThreadItem"
|
logContext="PostThreadItem"
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -58,6 +58,7 @@ let PostCtrls = ({
|
||||||
feedContext,
|
feedContext,
|
||||||
style,
|
style,
|
||||||
onPressReply,
|
onPressReply,
|
||||||
|
onPostReply,
|
||||||
logContext,
|
logContext,
|
||||||
}: {
|
}: {
|
||||||
big?: boolean
|
big?: boolean
|
||||||
|
@ -67,6 +68,7 @@ let PostCtrls = ({
|
||||||
feedContext?: string | undefined
|
feedContext?: string | undefined
|
||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
onPressReply: () => void
|
onPressReply: () => void
|
||||||
|
onPostReply?: (postUri: string | undefined) => void
|
||||||
logContext: 'FeedItem' | 'PostThreadItem' | 'Post'
|
logContext: 'FeedItem' | 'PostThreadItem' | 'Post'
|
||||||
}): React.ReactNode => {
|
}): React.ReactNode => {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
|
@ -169,16 +171,20 @@ let PostCtrls = ({
|
||||||
author: post.author,
|
author: post.author,
|
||||||
indexedAt: post.indexedAt,
|
indexedAt: post.indexedAt,
|
||||||
},
|
},
|
||||||
|
quoteCount: post.quoteCount,
|
||||||
|
onPost: onPostReply,
|
||||||
})
|
})
|
||||||
}, [
|
}, [
|
||||||
openComposer,
|
sendInteraction,
|
||||||
post.uri,
|
post.uri,
|
||||||
post.cid,
|
post.cid,
|
||||||
post.author,
|
post.author,
|
||||||
post.indexedAt,
|
post.indexedAt,
|
||||||
record.text,
|
post.quoteCount,
|
||||||
sendInteraction,
|
|
||||||
feedContext,
|
feedContext,
|
||||||
|
openComposer,
|
||||||
|
record.text,
|
||||||
|
onPostReply,
|
||||||
])
|
])
|
||||||
|
|
||||||
const onShare = useCallback(() => {
|
const onShare = useCallback(() => {
|
||||||
|
|
|
@ -33,6 +33,7 @@ export const Composer = observer(function ComposerImpl({}: {
|
||||||
replyTo={state?.replyTo}
|
replyTo={state?.replyTo}
|
||||||
onPost={state?.onPost}
|
onPost={state?.onPost}
|
||||||
quote={state?.quote}
|
quote={state?.quote}
|
||||||
|
quoteCount={state?.quoteCount}
|
||||||
mention={state?.mention}
|
mention={state?.mention}
|
||||||
text={state?.text}
|
text={state?.text}
|
||||||
imageUris={state?.imageUris}
|
imageUris={state?.imageUris}
|
||||||
|
|
|
@ -55,6 +55,7 @@ export const Composer = observer(function ComposerImpl({
|
||||||
replyTo={state.replyTo}
|
replyTo={state.replyTo}
|
||||||
onPost={state.onPost}
|
onPost={state.onPost}
|
||||||
quote={state.quote}
|
quote={state.quote}
|
||||||
|
quoteCount={state.quoteCount}
|
||||||
mention={state.mention}
|
mention={state.mention}
|
||||||
text={state.text}
|
text={state.text}
|
||||||
imageUris={state.imageUris}
|
imageUris={state.imageUris}
|
||||||
|
|
|
@ -58,6 +58,7 @@ export function Composer({}: {winHeight: number}) {
|
||||||
<ComposePost
|
<ComposePost
|
||||||
replyTo={state.replyTo}
|
replyTo={state.replyTo}
|
||||||
quote={state.quote}
|
quote={state.quote}
|
||||||
|
quoteCount={state?.quoteCount}
|
||||||
onPost={state.onPost}
|
onPost={state.onPost}
|
||||||
mention={state.mention}
|
mention={state.mention}
|
||||||
openPicker={onOpenPicker}
|
openPicker={onOpenPicker}
|
||||||
|
|
15
yarn.lock
15
yarn.lock
|
@ -72,7 +72,7 @@
|
||||||
resolved "https://registry.yarnpkg.com/@atproto-labs/simple-store/-/simple-store-0.1.1.tgz#e743a2722b5d8732166f0a72aca8bd10e9bff106"
|
resolved "https://registry.yarnpkg.com/@atproto-labs/simple-store/-/simple-store-0.1.1.tgz#e743a2722b5d8732166f0a72aca8bd10e9bff106"
|
||||||
integrity sha512-WKILW2b3QbAYKh+w5U2x6p5FqqLl0nAeLwGeDY+KjX01K4Dq3vQTR9b/qNp0jZm48CabPQVrqCv0PPU9LgRRRg==
|
integrity sha512-WKILW2b3QbAYKh+w5U2x6p5FqqLl0nAeLwGeDY+KjX01K4Dq3vQTR9b/qNp0jZm48CabPQVrqCv0PPU9LgRRRg==
|
||||||
|
|
||||||
"@atproto/api@0.13.0", "@atproto/api@^0.13.0":
|
"@atproto/api@^0.13.0":
|
||||||
version "0.13.0"
|
version "0.13.0"
|
||||||
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.13.0.tgz#d1c65a407f1c3c6aba5be9425f4f739a01419bd8"
|
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.13.0.tgz#d1c65a407f1c3c6aba5be9425f4f739a01419bd8"
|
||||||
integrity sha512-04kzIDkoEVSP7zMVOT5ezCVQcOrbXWjGYO2YBc3/tBvQ90V1pl9I+mLyz1uUHE+wRE1IRWKACcWhAz8SrYz3pA==
|
integrity sha512-04kzIDkoEVSP7zMVOT5ezCVQcOrbXWjGYO2YBc3/tBvQ90V1pl9I+mLyz1uUHE+wRE1IRWKACcWhAz8SrYz3pA==
|
||||||
|
@ -85,6 +85,19 @@
|
||||||
multiformats "^9.9.0"
|
multiformats "^9.9.0"
|
||||||
tlds "^1.234.0"
|
tlds "^1.234.0"
|
||||||
|
|
||||||
|
"@atproto/api@^0.13.2":
|
||||||
|
version "0.13.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.13.2.tgz#392c7e37d03f28a9d3bc53b003f2d90cea4f1863"
|
||||||
|
integrity sha512-AkCr+GbSJu+TSJzML/Ggh7CC61TKi4cQEOGmFHeI/0x9sa110UAAWHHRKom2vV09+cW5p/FMAtWvA05YR+v4jw==
|
||||||
|
dependencies:
|
||||||
|
"@atproto/common-web" "^0.3.0"
|
||||||
|
"@atproto/lexicon" "^0.4.1"
|
||||||
|
"@atproto/syntax" "^0.3.0"
|
||||||
|
"@atproto/xrpc" "^0.6.0"
|
||||||
|
await-lock "^2.2.2"
|
||||||
|
multiformats "^9.9.0"
|
||||||
|
tlds "^1.234.0"
|
||||||
|
|
||||||
"@atproto/aws@^0.2.2":
|
"@atproto/aws@^0.2.2":
|
||||||
version "0.2.2"
|
version "0.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/@atproto/aws/-/aws-0.2.2.tgz#703e5e06f288bcf61c6d99a990738f1e7299e653"
|
resolved "https://registry.yarnpkg.com/@atproto/aws/-/aws-0.2.2.tgz#703e5e06f288bcf61c6d99a990738f1e7299e653"
|
||||||
|
|
Loading…
Reference in New Issue