feat: update layout

This commit is contained in:
codytseng 2025-08-27 21:56:46 +08:00
parent f41536a793
commit 8b1c2ebe3f
30 changed files with 230 additions and 250 deletions

View file

@ -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')}

View file

@ -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

View file

@ -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

View file

@ -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" />
) : (

View file

@ -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>

View file

@ -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>

View file

@ -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>
)

View file

@ -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

View file

@ -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">

View file

@ -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>

View file

@ -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')}

View file

@ -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 />