fix: 🐛

This commit is contained in:
codytseng
2025-05-16 23:35:12 +08:00
parent fa5e198b8a
commit 4b09276943
4 changed files with 122 additions and 82 deletions

View File

@@ -31,7 +31,8 @@ export default function NoteList({
className, className,
filterMutedNotes = true, filterMutedNotes = true,
needCheckAlgoRelay = false, needCheckAlgoRelay = false,
isMainFeed = false isMainFeed = false,
topSpace = 0
}: { }: {
relayUrls?: string[] relayUrls?: string[]
filter?: Filter filter?: Filter
@@ -40,6 +41,7 @@ export default function NoteList({
filterMutedNotes?: boolean filterMutedNotes?: boolean
needCheckAlgoRelay?: boolean needCheckAlgoRelay?: boolean
isMainFeed?: boolean isMainFeed?: boolean
topSpace?: number
}) { }) {
const { t } = useTranslation() const { t } = useTranslation()
const { isLargeScreen } = useScreenSize() const { isLargeScreen } = useScreenSize()
@@ -315,6 +317,7 @@ export default function NoteList({
topRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' }) topRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' })
}, 0) }, 0)
}} }}
threshold={Math.max(800, topSpace)}
/> />
{filteredNewEvents.length > 0 && ( {filteredNewEvents.length > 0 && (
<NewNotesButton newEvents={filteredNewEvents} onClick={showNewEvents} /> <NewNotesButton newEvents={filteredNewEvents} onClick={showNewEvents} />

View File

@@ -26,7 +26,7 @@ export default function ScrollToTopButton({
return ( return (
<div <div
className={cn( className={cn(
`fixed sm:sticky z-20 flex justify-end w-full pr-3 pointer-events-none transition-opacity duration-700 ${visible ? '' : 'opacity-0'}`, `fixed sm:sticky z-30 flex justify-end w-full pr-3 pointer-events-none transition-opacity duration-700 ${visible ? '' : 'opacity-0'}`,
className className
)} )}
style={{ style={{

View File

@@ -12,12 +12,14 @@ export default function TabSwitcher({
tabs, tabs,
value, value,
className, className,
onTabChange onTabChange,
threshold = 800
}: { }: {
tabs: TabDefinition[] tabs: TabDefinition[]
value: string value: string
className?: string className?: string
onTabChange?: (tab: string) => void onTabChange?: (tab: string) => void
threshold?: number
}) { }) {
const { t } = useTranslation() const { t } = useTranslation()
const { deepBrowsing, lastScrollTop } = useDeepBrowsing() const { deepBrowsing, lastScrollTop } = useDeepBrowsing()
@@ -27,7 +29,7 @@ export default function TabSwitcher({
<div <div
className={cn( className={cn(
'sticky top-12 bg-background z-30 w-full transition-transform', 'sticky top-12 bg-background z-30 w-full transition-transform',
deepBrowsing && lastScrollTop > 800 ? '-translate-y-[calc(100%+12rem)]' : '', deepBrowsing && lastScrollTop > threshold ? '-translate-y-[calc(100%+12rem)]' : '',
className className
)} )}
> >

View File

@@ -1,3 +1,4 @@
import Collapsible from '@/components/Collapsible'
import FollowButton from '@/components/FollowButton' import FollowButton from '@/components/FollowButton'
import Nip05 from '@/components/Nip05' import Nip05 from '@/components/Nip05'
import NoteList from '@/components/NoteList' import NoteList from '@/components/NoteList'
@@ -18,12 +19,11 @@ import { SecondaryPageLink, useSecondaryPage } from '@/PageManager'
import { useMuteList } from '@/providers/MuteListProvider' import { useMuteList } from '@/providers/MuteListProvider'
import { useNostr } from '@/providers/NostrProvider' import { useNostr } from '@/providers/NostrProvider'
import { Link, Zap } from 'lucide-react' import { Link, Zap } from 'lucide-react'
import { forwardRef, useMemo } from 'react' import { forwardRef, useCallback, useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import NotFoundPage from '../NotFoundPage' import NotFoundPage from '../NotFoundPage'
import Followings from './Followings' import Followings from './Followings'
import Relays from './Relays' import Relays from './Relays'
import Collapsible from '@/components/Collapsible'
const ProfilePage = forwardRef(({ id, index }: { id?: string; index?: number }, ref) => { const ProfilePage = forwardRef(({ id, index }: { id?: string; index?: number }, ref) => {
const { t } = useTranslation() const { t } = useTranslation()
@@ -41,7 +41,35 @@ const ProfilePage = forwardRef(({ id, index }: { id?: string; index?: number },
() => (profile?.pubkey ? generateImageByPubkey(profile?.pubkey) : ''), () => (profile?.pubkey ? generateImageByPubkey(profile?.pubkey) : ''),
[profile] [profile]
) )
const [topContainerHeight, setTopContainerHeight] = useState(0)
const isSelf = accountPubkey === profile?.pubkey const isSelf = accountPubkey === profile?.pubkey
const [topContainer, setTopContainer] = useState<HTMLDivElement | null>(null)
const topContainerRef = useCallback((node: HTMLDivElement | null) => {
if (node) {
setTopContainer(node)
}
}, [])
useEffect(() => {
if (!topContainer) return
const checkHeight = () => {
console.log('checkHeight', topContainer.scrollHeight)
setTopContainerHeight(topContainer.scrollHeight)
}
checkHeight()
const observer = new ResizeObserver(() => {
checkHeight()
})
observer.observe(topContainer)
return () => {
observer.disconnect()
}
}, [topContainer])
if (!profile && isFetching) { if (!profile && isFetching) {
return ( return (
@@ -64,90 +92,97 @@ const ProfilePage = forwardRef(({ id, index }: { id?: string; index?: number },
const { banner, username, about, avatar, pubkey, website, lightningAddress } = profile const { banner, username, about, avatar, pubkey, website, lightningAddress } = profile
return ( return (
<SecondaryPageLayout index={index} title={username} displayScrollToTopButton ref={ref}> <SecondaryPageLayout index={index} title={username} displayScrollToTopButton ref={ref}>
<div className="sm:px-4"> <div ref={topContainerRef}>
<div className="relative bg-cover bg-center mb-2"> <div className="sm:px-4">
<ProfileBanner <div className="relative bg-cover bg-center mb-2">
banner={banner} <ProfileBanner
pubkey={pubkey} banner={banner}
className="w-full aspect-video sm:rounded-lg" pubkey={pubkey}
/> className="w-full aspect-video sm:rounded-lg"
<Avatar className="w-24 h-24 absolute left-3 bottom-0 translate-y-1/2 border-4 border-background"> />
<AvatarImage src={avatar} className="object-cover object-center" /> <Avatar className="w-24 h-24 absolute left-3 bottom-0 translate-y-1/2 border-4 border-background">
<AvatarFallback> <AvatarImage src={avatar} className="object-cover object-center" />
<img src={defaultImage} /> <AvatarFallback>
</AvatarFallback> <img src={defaultImage} />
</Avatar> </AvatarFallback>
</Avatar>
</div>
</div> </div>
</div> <div className="px-4">
<div className="px-4"> <div className="flex justify-end h-8 gap-2 items-center max-sm:translate-x-2">
<div className="flex justify-end h-8 gap-2 items-center max-sm:translate-x-2"> <ProfileOptions pubkey={pubkey} />
<ProfileOptions pubkey={pubkey} /> {isSelf ? (
{isSelf ? ( <Button
<Button className="w-20 min-w-20 rounded-full"
className="w-20 min-w-20 rounded-full" variant="secondary"
variant="secondary" onClick={() => push(toProfileEditor())}
onClick={() => push(toProfileEditor())} >
> {t('Edit')}
{t('Edit')} </Button>
</Button> ) : (
) : ( <>
<> {!!lightningAddress && <ProfileZapButton pubkey={pubkey} />}
{!!lightningAddress && <ProfileZapButton pubkey={pubkey} />} <FollowButton pubkey={pubkey} />
<FollowButton pubkey={pubkey} /> </>
</> )}
)} </div>
</div> <div className="pt-2">
<div className="pt-2"> <div className="flex gap-2 items-center">
<div className="flex gap-2 items-center"> <div className="text-xl font-semibold truncate select-text">{username}</div>
<div className="text-xl font-semibold truncate select-text">{username}</div> {isFollowingYou && (
{isFollowingYou && ( <div className="text-muted-foreground rounded-full bg-muted text-xs h-fit px-2 shrink-0">
<div className="text-muted-foreground rounded-full bg-muted text-xs h-fit px-2 shrink-0"> {t('Follows you')}
{t('Follows you')} </div>
)}
</div>
<Nip05 pubkey={pubkey} />
{lightningAddress && (
<div className="text-sm text-yellow-400 flex gap-1 items-center select-text">
<Zap className="size-4 shrink-0" />
<div className="flex-1 max-w-fit w-0 truncate">{lightningAddress}</div>
</div> </div>
)} )}
</div> <div className="flex gap-1 mt-1">
<Nip05 pubkey={pubkey} /> <PubkeyCopy pubkey={pubkey} />
{lightningAddress && ( <QrCodePopover pubkey={pubkey} />
<div className="text-sm text-yellow-400 flex gap-1 items-center select-text">
<Zap className="size-4 shrink-0" />
<div className="flex-1 max-w-fit w-0 truncate">{lightningAddress}</div>
</div> </div>
)} <Collapsible>
<div className="flex gap-1 mt-1"> <ProfileAbout
<PubkeyCopy pubkey={pubkey} /> about={about}
<QrCodePopover pubkey={pubkey} /> className="text-wrap break-words whitespace-pre-wrap mt-2 select-text"
</div> />
<Collapsible> </Collapsible>
<ProfileAbout {website && (
about={about} <div className="flex gap-1 items-center text-primary mt-2 truncate select-text">
className="text-wrap break-words whitespace-pre-wrap mt-2 select-text" <Link size={14} className="shrink-0" />
/> <a
</Collapsible> href={website}
{website && ( target="_blank"
<div className="flex gap-1 items-center text-primary mt-2 truncate select-text"> className="hover:underline truncate flex-1 max-w-fit w-0"
<Link size={14} className="shrink-0" /> >
<a {website}
href={website} </a>
target="_blank" </div>
className="hover:underline truncate flex-1 max-w-fit w-0"
>
{website}
</a>
</div>
)}
<div className="flex gap-4 items-center mt-2 text-sm">
<Followings pubkey={pubkey} />
<Relays pubkey={pubkey} />
{isSelf && (
<SecondaryPageLink to={toMuteList()} className="flex gap-1 hover:underline w-fit">
{mutePubkeys.length}
<div className="text-muted-foreground">{t('Muted')}</div>
</SecondaryPageLink>
)} )}
<div className="flex gap-4 items-center mt-2 text-sm">
<Followings pubkey={pubkey} />
<Relays pubkey={pubkey} />
{isSelf && (
<SecondaryPageLink to={toMuteList()} className="flex gap-1 hover:underline w-fit">
{mutePubkeys.length}
<div className="text-muted-foreground">{t('Muted')}</div>
</SecondaryPageLink>
)}
</div>
</div> </div>
</div> </div>
</div> </div>
<NoteList author={pubkey} className="mt-2" filterMutedNotes={false} /> <NoteList
author={pubkey}
className="mt-2"
filterMutedNotes={false}
topSpace={topContainerHeight + 100}
/>
</SecondaryPageLayout> </SecondaryPageLayout>
) )
}) })