fix: 🐛
This commit is contained in:
@@ -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} />
|
||||||
|
|||||||
@@ -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={{
|
||||||
|
|||||||
@@ -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
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user