feat: update layout
This commit is contained in:
parent
f41536a793
commit
8b1c2ebe3f
30 changed files with 230 additions and 250 deletions
|
|
@ -33,7 +33,12 @@ const MePage = forwardRef((_, ref) => {
|
|||
|
||||
if (!pubkey) {
|
||||
return (
|
||||
<PrimaryPageLayout ref={ref} pageName="home" titlebar={<MePageTitlebar />}>
|
||||
<PrimaryPageLayout
|
||||
ref={ref}
|
||||
pageName="home"
|
||||
titlebar={<MePageTitlebar />}
|
||||
hideTitlebarBottomBorder
|
||||
>
|
||||
<div className="flex flex-col p-4 gap-4 overflow-auto">
|
||||
<AccountManager />
|
||||
</div>
|
||||
|
|
@ -42,7 +47,12 @@ const MePage = forwardRef((_, ref) => {
|
|||
}
|
||||
|
||||
return (
|
||||
<PrimaryPageLayout ref={ref} pageName="home" titlebar={<MePageTitlebar />}>
|
||||
<PrimaryPageLayout
|
||||
ref={ref}
|
||||
pageName="home"
|
||||
titlebar={<MePageTitlebar />}
|
||||
hideTitlebarBottomBorder
|
||||
>
|
||||
<div className="flex gap-4 items-center p-4">
|
||||
<SimpleUserAvatar userId={pubkey} size="big" />
|
||||
<div className="space-y-1 flex-1 w-0">
|
||||
|
|
|
|||
|
|
@ -34,7 +34,11 @@ export default function FeedButton({ className }: { className?: string }) {
|
|||
<PopoverTrigger asChild>
|
||||
<FeedSwitcherTrigger className={className} />
|
||||
</PopoverTrigger>
|
||||
<PopoverContent side="bottom" className="w-96 p-4 max-h-[80vh] overflow-auto">
|
||||
<PopoverContent
|
||||
sideOffset={0}
|
||||
side="bottom"
|
||||
className="w-96 p-4 max-h-[80vh] overflow-auto scrollbar-hide"
|
||||
>
|
||||
<FeedSwitcher close={() => setOpen(false)} />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ const NoteListPage = forwardRef((_, ref) => {
|
|||
|
||||
useEffect(() => {
|
||||
if (layoutRef.current) {
|
||||
layoutRef.current.scrollToTop()
|
||||
layoutRef.current.scrollToTop('instant')
|
||||
}
|
||||
}, [JSON.stringify(relayUrls), feedInfo])
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ const GeneralSettingsPage = forwardRef(({ index }: { index?: number }, ref) => {
|
|||
|
||||
return (
|
||||
<SecondaryPageLayout ref={ref} index={index} title={t('General')}>
|
||||
<div className="space-y-4 mt-2">
|
||||
<div className="space-y-4 mt-3">
|
||||
<SettingItem>
|
||||
<Label htmlFor="languages" className="text-base font-normal">
|
||||
{t('Languages')}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ const HomePage = forwardRef(({ index }: { index?: number }, ref) => {
|
|||
|
||||
if (!recommendedRelayInfos.length) {
|
||||
return (
|
||||
<SecondaryPageLayout ref={ref} index={index} hideBackButton>
|
||||
<SecondaryPageLayout ref={ref} index={index} hideBackButton hideTitlebarBottomBorder>
|
||||
<div className="text-muted-foreground w-full h-screen flex items-center justify-center">
|
||||
{t('Welcome! 🥳')}
|
||||
</div>
|
||||
|
|
@ -49,8 +49,9 @@ const HomePage = forwardRef(({ index }: { index?: number }, ref) => {
|
|||
</>
|
||||
}
|
||||
hideBackButton
|
||||
hideTitlebarBottomBorder
|
||||
>
|
||||
<div className="px-4">
|
||||
<div className="px-4 pt-2">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
{recommendedRelayInfos.map((relayInfo) => (
|
||||
<RelaySimpleInfo
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
import SecondaryPageLayout from '@/layouts/SecondaryPageLayout'
|
||||
import { forwardRef } from 'react'
|
||||
|
||||
const LoadingPage = forwardRef(({ title, index }: { title?: string; index?: number }, ref) => {
|
||||
return (
|
||||
<SecondaryPageLayout ref={ref} index={index} title={title}>
|
||||
<div className="text-muted-foreground text-center">
|
||||
<div>Loading...</div>
|
||||
</div>
|
||||
</SecondaryPageLayout>
|
||||
)
|
||||
})
|
||||
LoadingPage.displayName = 'LoadingPage'
|
||||
export default LoadingPage
|
||||
|
|
@ -35,7 +35,7 @@ const NotePage = forwardRef(({ id, index }: { id?: string; index?: number }, ref
|
|||
if (!event && isFetching) {
|
||||
return (
|
||||
<SecondaryPageLayout ref={ref} index={index} title={t('Note')}>
|
||||
<div className="px-4">
|
||||
<div className="px-4 pt-3">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Skeleton className="w-10 h-10 rounded-full" />
|
||||
<div className={`flex-1 w-0`}>
|
||||
|
|
@ -69,7 +69,7 @@ const NotePage = forwardRef(({ id, index }: { id?: string; index?: number }, ref
|
|||
|
||||
return (
|
||||
<SecondaryPageLayout ref={ref} index={index} title={t('Note')} displayScrollToTopButton>
|
||||
<div className="px-4">
|
||||
<div className="px-4 pt-3">
|
||||
{rootITag && <ExternalRoot value={rootITag[1]} />}
|
||||
{rootEventId !== parentEventId && (
|
||||
<ParentNote
|
||||
|
|
@ -132,12 +132,12 @@ function ParentNote({
|
|||
if (isFetching) {
|
||||
return (
|
||||
<div>
|
||||
<Card className="flex space-x-1 px-1.5 py-1 items-center clickable text-sm text-muted-foreground">
|
||||
<div className="flex space-x-1 px-[0.4375rem] py-1 items-center rounded-full border clickable text-sm text-muted-foreground">
|
||||
<Skeleton className="shrink w-4 h-4 rounded-full" />
|
||||
<div className="py-1 flex-1">
|
||||
<Skeleton className="h-3" />
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
<div className="ml-5 w-px h-3 bg-border" />
|
||||
</div>
|
||||
)
|
||||
|
|
@ -146,9 +146,9 @@ function ParentNote({
|
|||
|
||||
return (
|
||||
<div>
|
||||
<Card
|
||||
<div
|
||||
className={cn(
|
||||
'flex space-x-1 px-1.5 py-1 items-center clickable text-sm text-muted-foreground',
|
||||
'flex space-x-1 px-[0.4375rem] py-1 items-center rounded-full border clickable text-sm text-muted-foreground',
|
||||
event && 'hover:text-foreground'
|
||||
)}
|
||||
onClick={() => {
|
||||
|
|
@ -158,7 +158,7 @@ function ParentNote({
|
|||
>
|
||||
{event && <UserAvatar userId={event.pubkey} size="tiny" className="shrink-0" />}
|
||||
<ContentPreview className="truncate" event={event} />
|
||||
</Card>
|
||||
</div>
|
||||
{isConsecutive ? (
|
||||
<div className="ml-5 w-px h-3 bg-border" />
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ const RelaySettingsPage = forwardRef(({ id, index }: { id?: string; index?: numb
|
|||
index={index}
|
||||
title={t("username's used relays", { username: profile.username })}
|
||||
>
|
||||
<div className="px-4">
|
||||
<div className="px-4 pt-3">
|
||||
<OthersRelayList userId={id} />
|
||||
</div>
|
||||
</SecondaryPageLayout>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ const PostSettingsPage = forwardRef(({ index }: { index?: number }, ref) => {
|
|||
|
||||
return (
|
||||
<SecondaryPageLayout ref={ref} index={index} title={t('Post settings')}>
|
||||
<div className="px-4 pt-2 space-y-4">
|
||||
<div className="px-4 pt-3 space-y-4">
|
||||
<MediaUploadServiceSetting />
|
||||
</div>
|
||||
</SecondaryPageLayout>
|
||||
|
|
|
|||
|
|
@ -124,112 +124,106 @@ const ProfileEditorPage = forwardRef(({ index }: { index?: number }, ref) => {
|
|||
|
||||
return (
|
||||
<SecondaryPageLayout ref={ref} index={index} title={profile.username} controls={controls}>
|
||||
<div className="px-4">
|
||||
<div className="relative bg-cover bg-center rounded-lg mb-2">
|
||||
<Uploader
|
||||
onUploadSuccess={onBannerUploadSuccess}
|
||||
onUploadStart={() => setUploadingBanner(true)}
|
||||
onUploadEnd={() => setUploadingBanner(false)}
|
||||
className="w-full relative cursor-pointer"
|
||||
>
|
||||
<ProfileBanner
|
||||
banner={banner}
|
||||
pubkey={account.pubkey}
|
||||
className="w-full aspect-[3/1] object-cover rounded-lg"
|
||||
/>
|
||||
<div className="absolute top-0 bg-muted/30 w-full h-full rounded-lg flex flex-col justify-center items-center">
|
||||
{uploadingBanner ? (
|
||||
<Loader size={36} className="animate-spin" />
|
||||
) : (
|
||||
<Upload size={36} />
|
||||
)}
|
||||
</div>
|
||||
</Uploader>
|
||||
<Uploader
|
||||
onUploadSuccess={onAvatarUploadSuccess}
|
||||
onUploadStart={() => setUploadingAvatar(true)}
|
||||
onUploadEnd={() => setUploadingAvatar(false)}
|
||||
className="w-24 h-24 absolute bottom-0 left-4 translate-y-1/2 border-4 border-background cursor-pointer rounded-full"
|
||||
>
|
||||
<Avatar className="w-full h-full">
|
||||
<AvatarImage src={avatar} className="object-cover object-center" />
|
||||
<AvatarFallback>
|
||||
<img src={defaultImage} />
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="absolute top-0 bg-muted/30 w-full h-full rounded-full flex flex-col justify-center items-center">
|
||||
{uploadingAvatar ? <Loader className="animate-spin" /> : <Upload />}
|
||||
</div>
|
||||
</Uploader>
|
||||
</div>
|
||||
<div className="pt-14 flex flex-col gap-4">
|
||||
<Item>
|
||||
<Label htmlFor="profile-username-input">{t('Display Name')}</Label>
|
||||
<Input
|
||||
id="profile-username-input"
|
||||
value={username}
|
||||
onChange={(e) => {
|
||||
setUsername(e.target.value)
|
||||
setHasChanged(true)
|
||||
}}
|
||||
/>
|
||||
</Item>
|
||||
<Item>
|
||||
<Label htmlFor="profile-about-textarea">{t('Bio')}</Label>
|
||||
<Textarea
|
||||
id="profile-about-textarea"
|
||||
className="h-44"
|
||||
value={about}
|
||||
onChange={(e) => {
|
||||
setAbout(e.target.value)
|
||||
setHasChanged(true)
|
||||
}}
|
||||
/>
|
||||
</Item>
|
||||
<Item>
|
||||
<Label htmlFor="profile-website-input">{t('Website')}</Label>
|
||||
<Input
|
||||
id="profile-website-input"
|
||||
value={website}
|
||||
onChange={(e) => {
|
||||
setWebsite(e.target.value)
|
||||
setHasChanged(true)
|
||||
}}
|
||||
/>
|
||||
</Item>
|
||||
<Item>
|
||||
<Label htmlFor="profile-nip05-input">{t('Nostr Address (NIP-05)')}</Label>
|
||||
<Input
|
||||
id="profile-nip05-input"
|
||||
value={nip05}
|
||||
onChange={(e) => {
|
||||
setNip05Error('')
|
||||
setNip05(e.target.value)
|
||||
setHasChanged(true)
|
||||
}}
|
||||
className={nip05Error ? 'border-destructive' : ''}
|
||||
/>
|
||||
{nip05Error && <div className="text-xs text-destructive pl-3">{nip05Error}</div>}
|
||||
</Item>
|
||||
<Item>
|
||||
<Label htmlFor="profile-lightning-address-input">
|
||||
{t('Lightning Address (or LNURL)')}
|
||||
</Label>
|
||||
<Input
|
||||
id="profile-lightning-address-input"
|
||||
value={lightningAddress}
|
||||
onChange={(e) => {
|
||||
setLightningAddressError('')
|
||||
setLightningAddress(e.target.value)
|
||||
setHasChanged(true)
|
||||
}}
|
||||
className={lightningAddressError ? 'border-destructive' : ''}
|
||||
/>
|
||||
{lightningAddressError && (
|
||||
<div className="text-xs text-destructive pl-3">{lightningAddressError}</div>
|
||||
)}
|
||||
</Item>
|
||||
</div>
|
||||
<div className="relative bg-cover bg-center mb-2">
|
||||
<Uploader
|
||||
onUploadSuccess={onBannerUploadSuccess}
|
||||
onUploadStart={() => setUploadingBanner(true)}
|
||||
onUploadEnd={() => setUploadingBanner(false)}
|
||||
className="w-full relative cursor-pointer"
|
||||
>
|
||||
<ProfileBanner
|
||||
banner={banner}
|
||||
pubkey={account.pubkey}
|
||||
className="w-full aspect-[3/1] object-cover"
|
||||
/>
|
||||
<div className="absolute top-0 bg-muted/30 w-full h-full flex flex-col justify-center items-center">
|
||||
{uploadingBanner ? <Loader size={36} className="animate-spin" /> : <Upload size={36} />}
|
||||
</div>
|
||||
</Uploader>
|
||||
<Uploader
|
||||
onUploadSuccess={onAvatarUploadSuccess}
|
||||
onUploadStart={() => setUploadingAvatar(true)}
|
||||
onUploadEnd={() => setUploadingAvatar(false)}
|
||||
className="w-24 h-24 absolute bottom-0 left-4 translate-y-1/2 border-4 border-background cursor-pointer rounded-full"
|
||||
>
|
||||
<Avatar className="w-full h-full">
|
||||
<AvatarImage src={avatar} className="object-cover object-center" />
|
||||
<AvatarFallback>
|
||||
<img src={defaultImage} />
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="absolute top-0 bg-muted/30 w-full h-full rounded-full flex flex-col justify-center items-center">
|
||||
{uploadingAvatar ? <Loader className="animate-spin" /> : <Upload />}
|
||||
</div>
|
||||
</Uploader>
|
||||
</div>
|
||||
<div className="pt-14 px-4 flex flex-col gap-4">
|
||||
<Item>
|
||||
<Label htmlFor="profile-username-input">{t('Display Name')}</Label>
|
||||
<Input
|
||||
id="profile-username-input"
|
||||
value={username}
|
||||
onChange={(e) => {
|
||||
setUsername(e.target.value)
|
||||
setHasChanged(true)
|
||||
}}
|
||||
/>
|
||||
</Item>
|
||||
<Item>
|
||||
<Label htmlFor="profile-about-textarea">{t('Bio')}</Label>
|
||||
<Textarea
|
||||
id="profile-about-textarea"
|
||||
className="h-44"
|
||||
value={about}
|
||||
onChange={(e) => {
|
||||
setAbout(e.target.value)
|
||||
setHasChanged(true)
|
||||
}}
|
||||
/>
|
||||
</Item>
|
||||
<Item>
|
||||
<Label htmlFor="profile-website-input">{t('Website')}</Label>
|
||||
<Input
|
||||
id="profile-website-input"
|
||||
value={website}
|
||||
onChange={(e) => {
|
||||
setWebsite(e.target.value)
|
||||
setHasChanged(true)
|
||||
}}
|
||||
/>
|
||||
</Item>
|
||||
<Item>
|
||||
<Label htmlFor="profile-nip05-input">{t('Nostr Address (NIP-05)')}</Label>
|
||||
<Input
|
||||
id="profile-nip05-input"
|
||||
value={nip05}
|
||||
onChange={(e) => {
|
||||
setNip05Error('')
|
||||
setNip05(e.target.value)
|
||||
setHasChanged(true)
|
||||
}}
|
||||
className={nip05Error ? 'border-destructive' : ''}
|
||||
/>
|
||||
{nip05Error && <div className="text-xs text-destructive pl-3">{nip05Error}</div>}
|
||||
</Item>
|
||||
<Item>
|
||||
<Label htmlFor="profile-lightning-address-input">
|
||||
{t('Lightning Address (or LNURL)')}
|
||||
</Label>
|
||||
<Input
|
||||
id="profile-lightning-address-input"
|
||||
value={lightningAddress}
|
||||
onChange={(e) => {
|
||||
setLightningAddressError('')
|
||||
setLightningAddress(e.target.value)
|
||||
setHasChanged(true)
|
||||
}}
|
||||
className={lightningAddressError ? 'border-destructive' : ''}
|
||||
/>
|
||||
{lightningAddressError && (
|
||||
<div className="text-xs text-destructive pl-3">{lightningAddressError}</div>
|
||||
)}
|
||||
</Item>
|
||||
</div>
|
||||
</SecondaryPageLayout>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -87,9 +87,9 @@ const ProfilePage = forwardRef(({ id, index }: { id?: string; index?: number },
|
|||
if (!profile && isFetching) {
|
||||
return (
|
||||
<SecondaryPageLayout index={index} ref={ref}>
|
||||
<div className="sm:px-4">
|
||||
<div>
|
||||
<div className="relative bg-cover bg-center mb-2">
|
||||
<Skeleton className="w-full aspect-[3/1] sm:rounded-lg" />
|
||||
<Skeleton className="w-full aspect-[3/1] rounded-none" />
|
||||
<Skeleton className="w-24 h-24 absolute bottom-0 left-3 translate-y-1/2 border-4 border-background rounded-full" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -106,23 +106,17 @@ const ProfilePage = forwardRef(({ id, index }: { id?: string; index?: number },
|
|||
return (
|
||||
<SecondaryPageLayout index={index} title={username} displayScrollToTopButton ref={ref}>
|
||||
<div ref={topContainerRef}>
|
||||
<div className="sm:px-4">
|
||||
<div className="relative bg-cover bg-center mb-2">
|
||||
<ProfileBanner
|
||||
banner={banner}
|
||||
pubkey={pubkey}
|
||||
className="w-full aspect-[3/1] 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" />
|
||||
<AvatarFallback>
|
||||
<img src={defaultImage} />
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
</div>
|
||||
<div className="relative bg-cover bg-center mb-2">
|
||||
<ProfileBanner banner={banner} pubkey={pubkey} className="w-full aspect-[3/1]" />
|
||||
<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" />
|
||||
<AvatarFallback>
|
||||
<img src={defaultImage} />
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
</div>
|
||||
<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">
|
||||
<ProfileOptions pubkey={pubkey} />
|
||||
{isSelf ? (
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ const RelayPage = forwardRef(({ url, index }: { url?: string; index?: number },
|
|||
controls={<RelayPageControls url={normalizedUrl} />}
|
||||
displayScrollToTopButton
|
||||
>
|
||||
<div className="h-3 w-full" />
|
||||
<RelayInfo url={normalizedUrl} />
|
||||
{relayInfo?.supported_nips?.includes(50) && (
|
||||
<div className="px-4 py-2">
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ const RelaySettingsPage = forwardRef(({ index }: { index?: number }, ref) => {
|
|||
|
||||
return (
|
||||
<SecondaryPageLayout ref={ref} index={index} title={t('Relay settings')}>
|
||||
<Tabs value={tabValue} onValueChange={setTabValue} className="px-4 pb-4 space-y-4">
|
||||
<Tabs value={tabValue} onValueChange={setTabValue} className="px-4 py-3 space-y-4">
|
||||
<TabsList>
|
||||
<TabsTrigger value="favorite-relays">{t('Favorite Relays')}</TabsTrigger>
|
||||
<TabsTrigger value="mailbox">{t('Read & Write Relays')}</TabsTrigger>
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ const TranslationPage = forwardRef(({ index }: { index?: number }, ref) => {
|
|||
|
||||
return (
|
||||
<SecondaryPageLayout ref={ref} index={index} title={t('Translation')}>
|
||||
<div className="px-4 pt-2 space-y-4">
|
||||
<div className="px-4 pt-3 space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="languages" className="text-base font-medium">
|
||||
{t('Languages')}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ const WalletPage = forwardRef(({ index }: { index?: number }, ref) => {
|
|||
|
||||
return (
|
||||
<SecondaryPageLayout ref={ref} index={index} title={t('Wallet')}>
|
||||
<div className="px-4 pt-2 space-y-4">
|
||||
<div className="px-4 pt-3 space-y-4">
|
||||
<BcButton />
|
||||
<LightningAddressInput />
|
||||
<DefaultZapAmountInput />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue