PWI: Profile (#1982)

* PWI: Profile

* Show replies conditionally

* Dismiss modals on auth action
This commit is contained in:
dan 2023-11-23 00:30:49 +00:00 committed by GitHub
parent edf3114e47
commit 4272d291a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 129 additions and 105 deletions

View file

@ -9,6 +9,7 @@ import {PUBLIC_BSKY_AGENT} from '#/state/queries'
import {IS_PROD} from '#/lib/constants' import {IS_PROD} from '#/lib/constants'
import {emitSessionLoaded, emitSessionDropped} from '../events' import {emitSessionLoaded, emitSessionDropped} from '../events'
import {useLoggedOutViewControls} from '#/state/shell/logged-out' import {useLoggedOutViewControls} from '#/state/shell/logged-out'
import {useCloseAllActiveElements} from '#/state/util'
let __globalAgent: BskyAgent = PUBLIC_BSKY_AGENT let __globalAgent: BskyAgent = PUBLIC_BSKY_AGENT
@ -520,15 +521,17 @@ export function useSessionApi() {
export function useRequireAuth() { export function useRequireAuth() {
const {hasSession} = useSession() const {hasSession} = useSession()
const {setShowLoggedOut} = useLoggedOutViewControls() const {setShowLoggedOut} = useLoggedOutViewControls()
const closeAll = useCloseAllActiveElements()
return React.useCallback( return React.useCallback(
(fn: () => void) => { (fn: () => void) => {
if (hasSession) { if (hasSession) {
fn() fn()
} else { } else {
closeAll()
setShowLoggedOut(true) setShowLoggedOut(true)
} }
}, },
[hasSession, setShowLoggedOut], [hasSession, setShowLoggedOut, closeAll],
) )
} }

View file

@ -51,6 +51,7 @@ import {s, colors} from 'lib/styles'
import {logger} from '#/logger' import {logger} from '#/logger'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
import {Shadow} from '#/state/cache/types' import {Shadow} from '#/state/cache/types'
import {useRequireAuth} from '#/state/session'
interface Props { interface Props {
profile: Shadow<AppBskyActorDefs.ProfileViewDetailed> | null profile: Shadow<AppBskyActorDefs.ProfileViewDetailed> | null
@ -113,7 +114,8 @@ let ProfileHeaderLoaded = ({
}: LoadedProps): React.ReactNode => { }: LoadedProps): React.ReactNode => {
const pal = usePalette('default') const pal = usePalette('default')
const palInverted = usePalette('inverted') const palInverted = usePalette('inverted')
const {currentAccount} = useSession() const {currentAccount, hasSession} = useSession()
const requireAuth = useRequireAuth()
const {_} = useLingui() const {_} = useLingui()
const {openModal} = useModalControls() const {openModal} = useModalControls()
const {openLightbox} = useLightboxControls() const {openLightbox} = useLightboxControls()
@ -150,38 +152,42 @@ let ProfileHeaderLoaded = ({
} }
}, [openLightbox, profile, moderation]) }, [openLightbox, profile, moderation])
const onPressFollow = async () => { const onPressFollow = () => {
try { requireAuth(async () => {
track('ProfileHeader:FollowButtonClicked') try {
await queueFollow() track('ProfileHeader:FollowButtonClicked')
Toast.show( await queueFollow()
`Following ${sanitizeDisplayName( Toast.show(
profile.displayName || profile.handle, `Following ${sanitizeDisplayName(
)}`, profile.displayName || profile.handle,
) )}`,
} catch (e: any) { )
if (e?.name !== 'AbortError') { } catch (e: any) {
logger.error('Failed to follow', {error: String(e)}) if (e?.name !== 'AbortError') {
Toast.show(`There was an issue! ${e.toString()}`) logger.error('Failed to follow', {error: String(e)})
Toast.show(`There was an issue! ${e.toString()}`)
}
} }
} })
} }
const onPressUnfollow = async () => { const onPressUnfollow = () => {
try { requireAuth(async () => {
track('ProfileHeader:UnfollowButtonClicked') try {
await queueUnfollow() track('ProfileHeader:UnfollowButtonClicked')
Toast.show( await queueUnfollow()
`No longer following ${sanitizeDisplayName( Toast.show(
profile.displayName || profile.handle, `No longer following ${sanitizeDisplayName(
)}`, profile.displayName || profile.handle,
) )}`,
} catch (e: any) { )
if (e?.name !== 'AbortError') { } catch (e: any) {
logger.error('Failed to unfollow', {error: String(e)}) if (e?.name !== 'AbortError') {
Toast.show(`There was an issue! ${e.toString()}`) logger.error('Failed to unfollow', {error: String(e)})
Toast.show(`There was an issue! ${e.toString()}`)
}
} }
} })
} }
const onPressEditProfile = React.useCallback(() => { const onPressEditProfile = React.useCallback(() => {
@ -303,72 +309,75 @@ let ProfileHeaderLoaded = ({
}, },
}, },
] ]
items.push({label: 'separator'}) if (hasSession) {
items.push({ items.push({label: 'separator'})
testID: 'profileHeaderDropdownListAddRemoveBtn',
label: _(msg`Add to Lists`),
onPress: onPressAddRemoveLists,
icon: {
ios: {
name: 'list.bullet',
},
android: 'ic_menu_add',
web: 'list',
},
})
if (!isMe) {
if (!profile.viewer?.blocking) {
items.push({
testID: 'profileHeaderDropdownMuteBtn',
label: profile.viewer?.muted
? _(msg`Unmute Account`)
: _(msg`Mute Account`),
onPress: profile.viewer?.muted
? onPressUnmuteAccount
: onPressMuteAccount,
icon: {
ios: {
name: 'speaker.slash',
},
android: 'ic_lock_silent_mode',
web: 'comment-slash',
},
})
}
if (!profile.viewer?.blockingByList) {
items.push({
testID: 'profileHeaderDropdownBlockBtn',
label: profile.viewer?.blocking
? _(msg`Unblock Account`)
: _(msg`Block Account`),
onPress: profile.viewer?.blocking
? onPressUnblockAccount
: onPressBlockAccount,
icon: {
ios: {
name: 'person.fill.xmark',
},
android: 'ic_menu_close_clear_cancel',
web: 'user-slash',
},
})
}
items.push({ items.push({
testID: 'profileHeaderDropdownReportBtn', testID: 'profileHeaderDropdownListAddRemoveBtn',
label: _(msg`Report Account`), label: _(msg`Add to Lists`),
onPress: onPressReportAccount, onPress: onPressAddRemoveLists,
icon: { icon: {
ios: { ios: {
name: 'exclamationmark.triangle', name: 'list.bullet',
}, },
android: 'ic_menu_report_image', android: 'ic_menu_add',
web: 'circle-exclamation', web: 'list',
}, },
}) })
if (!isMe) {
if (!profile.viewer?.blocking) {
items.push({
testID: 'profileHeaderDropdownMuteBtn',
label: profile.viewer?.muted
? _(msg`Unmute Account`)
: _(msg`Mute Account`),
onPress: profile.viewer?.muted
? onPressUnmuteAccount
: onPressMuteAccount,
icon: {
ios: {
name: 'speaker.slash',
},
android: 'ic_lock_silent_mode',
web: 'comment-slash',
},
})
}
if (!profile.viewer?.blockingByList) {
items.push({
testID: 'profileHeaderDropdownBlockBtn',
label: profile.viewer?.blocking
? _(msg`Unblock Account`)
: _(msg`Block Account`),
onPress: profile.viewer?.blocking
? onPressUnblockAccount
: onPressBlockAccount,
icon: {
ios: {
name: 'person.fill.xmark',
},
android: 'ic_menu_close_clear_cancel',
web: 'user-slash',
},
})
}
items.push({
testID: 'profileHeaderDropdownReportBtn',
label: _(msg`Report Account`),
onPress: onPressReportAccount,
icon: {
ios: {
name: 'exclamationmark.triangle',
},
android: 'ic_menu_report_image',
web: 'circle-exclamation',
},
})
}
} }
return items return items
}, [ }, [
isMe, isMe,
hasSession,
profile.viewer?.muted, profile.viewer?.muted,
profile.viewer?.blocking, profile.viewer?.blocking,
profile.viewer?.blockingByList, profile.viewer?.blockingByList,
@ -421,7 +430,7 @@ let ProfileHeaderLoaded = ({
) )
) : !profile.viewer?.blockedBy ? ( ) : !profile.viewer?.blockedBy ? (
<> <>
{!isProfilePreview && ( {!isProfilePreview && hasSession && (
<TouchableOpacity <TouchableOpacity
testID="suggestedFollowsBtn" testID="suggestedFollowsBtn"
onPress={() => setShowSuggestedFollows(!showSuggestedFollows)} onPress={() => setShowSuggestedFollows(!showSuggestedFollows)}

View file

@ -155,23 +155,27 @@ function ProfileScreenLoaded({
) )
const isMe = profile.did === currentAccount?.did const isMe = profile.did === currentAccount?.did
const showRepliesTab = hasSession
const showLikesTab = isMe const showLikesTab = isMe
const showFeedsTab = isMe || extraInfoQuery.data?.hasFeedgens const showFeedsTab = hasSession && (isMe || extraInfoQuery.data?.hasFeedgens)
const showListsTab = isMe || extraInfoQuery.data?.hasLists const showListsTab = hasSession && (isMe || extraInfoQuery.data?.hasLists)
const sectionTitles = useMemo<string[]>(() => { const sectionTitles = useMemo<string[]>(() => {
return [ return [
'Posts', 'Posts',
'Posts & Replies', showRepliesTab ? 'Posts & Replies' : undefined,
'Media', 'Media',
showLikesTab ? 'Likes' : undefined, showLikesTab ? 'Likes' : undefined,
showFeedsTab ? 'Feeds' : undefined, showFeedsTab ? 'Feeds' : undefined,
showListsTab ? 'Lists' : undefined, showListsTab ? 'Lists' : undefined,
].filter(Boolean) as string[] ].filter(Boolean) as string[]
}, [showLikesTab, showFeedsTab, showListsTab]) }, [showRepliesTab, showLikesTab, showFeedsTab, showListsTab])
let nextIndex = 0 let nextIndex = 0
const postsIndex = nextIndex++ const postsIndex = nextIndex++
const repliesIndex = nextIndex++ let repliesIndex: number | null = null
if (showRepliesTab) {
repliesIndex = nextIndex++
}
const mediaIndex = nextIndex++ const mediaIndex = nextIndex++
let likesIndex: number | null = null let likesIndex: number | null = null
if (showLikesTab) { if (showLikesTab) {
@ -282,19 +286,27 @@ function ProfileScreenLoaded({
} }
/> />
)} )}
{({onScroll, headerHeight, isFocused, isScrolledDown, scrollElRef}) => ( {showRepliesTab
<FeedSection ? ({
ref={repliesSectionRef} onScroll,
feed={`author|${profile.did}|posts_with_replies`} headerHeight,
onScroll={onScroll} isFocused,
headerHeight={headerHeight} isScrolledDown,
isFocused={isFocused} scrollElRef,
isScrolledDown={isScrolledDown} }) => (
scrollElRef={ <FeedSection
scrollElRef as React.MutableRefObject<FlatList<any> | null> ref={repliesSectionRef}
} feed={`author|${profile.did}|posts_with_replies`}
/> onScroll={onScroll}
)} headerHeight={headerHeight}
isFocused={isFocused}
isScrolledDown={isScrolledDown}
scrollElRef={
scrollElRef as React.MutableRefObject<FlatList<any> | null>
}
/>
)
: null}
{({onScroll, headerHeight, isFocused, isScrolledDown, scrollElRef}) => ( {({onScroll, headerHeight, isFocused, isScrolledDown, scrollElRef}) => (
<FeedSection <FeedSection
ref={mediaSectionRef} ref={mediaSectionRef}