wip: port more component to new ui

This commit is contained in:
Ren Amamiya 2023-08-02 14:48:51 +07:00
parent a85bcf917b
commit babcd8698e
20 changed files with 410 additions and 559 deletions

View File

@ -102,13 +102,12 @@ export function ResetScreen() {
return ( return (
<div className="flex h-full w-full items-center justify-center"> <div className="flex h-full w-full items-center justify-center">
<div className="mx-auto w-full max-w-md"> <div className="mx-auto w-full max-w-md">
<div className="mb-8 text-center"> <div className="mb-6 text-center">
<h1 className="text-xl font-semibold text-zinc-100">Reset unlock password</h1> <h1 className="text-2xl font-semibold text-white">Reset unlock password</h1>
</div> </div>
<div className="flex flex-col gap-4">
<form onSubmit={handleSubmit(onSubmit)} className="mb-0 flex flex-col gap-3"> <form onSubmit={handleSubmit(onSubmit)} className="mb-0 flex flex-col gap-3">
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<label htmlFor="privkey" className="font-medium text-zinc-200"> <label htmlFor="privkey" className="font-medium text-white/50">
Private key Private key
</label> </label>
<div className="relative"> <div className="relative">
@ -116,12 +115,12 @@ export function ResetScreen() {
{...register('privkey', { required: true })} {...register('privkey', { required: true })}
type="text" type="text"
placeholder="nsec..." placeholder="nsec..."
className="relative w-full rounded-lg bg-zinc-800 px-3.5 py-3 text-zinc-100 !outline-none placeholder:text-zinc-400" className="relative h-12 w-full rounded-lg bg-white/10 px-3.5 py-1 text-white !outline-none placeholder:text-white/10"
/> />
</div> </div>
</div> </div>
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<label htmlFor="password" className="font-medium text-zinc-200"> <label htmlFor="password" className="font-medium text-white/50">
Set a new password to protect your key Set a new password to protect your key
</label> </label>
<div className="relative"> <div className="relative">
@ -129,25 +128,17 @@ export function ResetScreen() {
{...register('password', { required: true })} {...register('password', { required: true })}
type={passwordInput} type={passwordInput}
placeholder="min. 4 characters" placeholder="min. 4 characters"
className="relative w-full rounded-lg bg-zinc-800 py-3 pl-3.5 pr-11 text-zinc-100 !outline-none placeholder:text-zinc-400" className="relative h-12 w-full rounded-lg bg-white/10 px-3.5 py-1 text-white !outline-none placeholder:text-white/10"
/> />
<button <button
type="button" type="button"
onClick={() => showPassword()} onClick={() => showPassword()}
className="group absolute right-2 top-1/2 -translate-y-1/2 transform rounded p-1 hover:bg-zinc-700" className="group absolute right-2 top-1/2 -translate-y-1/2 transform rounded p-1 hover:bg-white/10"
> >
{passwordInput === 'password' ? ( {passwordInput === 'password' ? (
<EyeOffIcon <EyeOffIcon className="h-5 w-5 text-white/50 group-hover:text-white" />
width={20}
height={20}
className="text-zinc-500 group-hover:text-zinc-100"
/>
) : ( ) : (
<EyeOnIcon <EyeOnIcon className="h-5 w-5 text-white/50 group-hover:text-white" />
width={20}
height={20}
className="text-zinc-500 group-hover:text-zinc-100"
/>
)} )}
</button> </button>
</div> </div>
@ -159,10 +150,10 @@ export function ResetScreen() {
<button <button
type="submit" type="submit"
disabled={!isDirty || !isValid} disabled={!isDirty || !isValid}
className="mt-3 inline-flex h-11 w-full items-center justify-center rounded-md bg-fuchsia-500 font-medium text-zinc-100 hover:bg-fuchsia-600 disabled:pointer-events-none disabled:opacity-50" className="inline-flex h-12 w-full items-center justify-center rounded-md bg-fuchsia-500 font-medium text-white hover:bg-fuchsia-600 disabled:pointer-events-none disabled:opacity-50"
> >
{loading ? ( {loading ? (
<LoaderIcon className="h-4 w-4 animate-spin text-black dark:text-zinc-100" /> <LoaderIcon className="h-4 w-4 animate-spin text-white" />
) : ( ) : (
'Continue →' 'Continue →'
)} )}
@ -171,6 +162,5 @@ export function ResetScreen() {
</form> </form>
</div> </div>
</div> </div>
</div>
); );
} }

View File

@ -84,7 +84,7 @@ export function UnlockScreen() {
<div className="mb-6 text-center"> <div className="mb-6 text-center">
<h1 className="text-2xl font-semibold text-white">Enter password to unlock</h1> <h1 className="text-2xl font-semibold text-white">Enter password to unlock</h1>
</div> </div>
<form onSubmit={handleSubmit(onSubmit)} className="flex flex-col gap-3"> <form onSubmit={handleSubmit(onSubmit)} className="mb-0 flex flex-col gap-3">
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<div className="relative"> <div className="relative">
<input <input
@ -98,17 +98,9 @@ export function UnlockScreen() {
className="group absolute right-2 top-1/2 -translate-y-1/2 transform rounded p-1 hover:bg-white/10" className="group absolute right-2 top-1/2 -translate-y-1/2 transform rounded p-1 hover:bg-white/10"
> >
{passwordInput === 'password' ? ( {passwordInput === 'password' ? (
<EyeOffIcon <EyeOffIcon className="h-5 w-5 text-white/50 group-hover:text-white" />
width={20}
height={20}
className="text-white/50 group-hover:text-white"
/>
) : ( ) : (
<EyeOnIcon <EyeOnIcon className="h-5 w-5 text-white/50 group-hover:text-white" />
width={20}
height={20}
className="text-white/50 group-hover:text-white"
/>
)} )}
</button> </button>
</div> </div>

View File

@ -1,5 +1,5 @@
import { Dialog, Transition } from '@headlessui/react'; import * as Dialog from '@radix-ui/react-dialog';
import { Fragment, useState } from 'react'; import { useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { User } from '@app/auth/components/user'; import { User } from '@app/auth/components/user';
@ -10,29 +10,22 @@ import { useAccount } from '@utils/hooks/useAccount';
export function NewMessageModal() { export function NewMessageModal() {
const navigate = useNavigate(); const navigate = useNavigate();
const [isOpen, setIsOpen] = useState(false);
const [open, setOpen] = useState(false);
const { status, account } = useAccount(); const { status, account } = useAccount();
const follows = account ? JSON.parse(account.follows as string) : []; const follows = account ? JSON.parse(account.follows as string) : [];
const closeModal = () => {
setIsOpen(false);
};
const openModal = () => {
setIsOpen(true);
};
const openChat = (pubkey: string) => { const openChat = (pubkey: string) => {
closeModal(); setOpen(false);
navigate(`/app/chats/${pubkey}`); navigate(`/app/chats/${pubkey}`);
}; };
return ( return (
<> <Dialog.Root open={open} onOpenChange={setOpen}>
<Dialog.Trigger asChild>
<button <button
type="button" type="button"
onClick={() => openModal()}
className="inline-flex h-9 items-center gap-2.5 rounded-md px-2" className="inline-flex h-9 items-center gap-2.5 rounded-md px-2"
> >
<div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded bg-white/10"> <div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded bg-white/10">
@ -42,69 +35,43 @@ export function NewMessageModal() {
<h5 className="text-white/50">New chat</h5> <h5 className="text-white/50">New chat</h5>
</div> </div>
</button> </button>
<Transition appear show={isOpen} as={Fragment}> </Dialog.Trigger>
<Dialog as="div" className="relative z-10" onClose={closeModal}> <Dialog.Portal className="relative z-10">
<Transition.Child <Dialog.Overlay className="fixed inset-0 z-50 bg-black/80 backdrop-blur-xl" />
as={Fragment} <Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
enter="ease-out duration-300" <div className="relative h-min w-full max-w-xl rounded-xl bg-white/10">
enterFrom="opacity-0" <div className="h-min w-full shrink-0 border-b border-white/10 bg-white/5 px-5 py-5">
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 z-50 bg-black bg-opacity-30 backdrop-blur-md" />
</Transition.Child>
<div className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className="relative flex h-min w-full max-w-lg flex-col gap-2 rounded-lg border-t border-zinc-800/50 bg-zinc-900">
<div className="h-min w-full shrink-0 border-b border-zinc-800 px-5 py-5">
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<Dialog.Title <Dialog.Title className="text-lg font-semibold leading-none text-zinc-100">
as="h3"
className="text-lg font-semibold leading-none text-zinc-100"
>
New chat New chat
</Dialog.Title> </Dialog.Title>
<button <Dialog.Close className="inline-flex h-6 w-6 items-center justify-center rounded-md hover:bg-white/10">
type="button" <CancelIcon className="h-4 w-4 text-white/50" />
onClick={closeModal} </Dialog.Close>
className="inline-flex h-5 w-5 items-center justify-center rounded hover:bg-zinc-800"
>
<CancelIcon className="h-4 w-4 text-zinc-300" />
</button>
</div> </div>
<Dialog.Description className="text-sm leading-tight text-zinc-400"> <Dialog.Description className="text-sm leading-none text-zinc-400">
All messages will be encrypted, but anyone can see who you chat All messages will be encrypted, but anyone can see who you chat
</Dialog.Description> </Dialog.Description>
</div> </div>
</div> </div>
<div className="flex h-[500px] flex-col overflow-y-auto overflow-x-hidden pb-5"> <div className="flex h-[500px] flex-col overflow-y-auto overflow-x-hidden pb-2 pt-2">
{status === 'loading' ? ( {status === 'loading' ? (
<div className="inline-flex items-center justify-center px-4 py-3"> <div className="inline-flex items-center justify-center px-4 py-3">
<LoaderIcon className="h-5 w-5 animate-spin text-black dark:text-zinc-100" /> <LoaderIcon className="h-5 w-5 animate-spin text-white" />
</div> </div>
) : ( ) : (
follows.map((follow) => ( follows.map((follow) => (
<div <div
key={follow} key={follow}
className="group flex items-center justify-between px-4 py-3 hover:bg-zinc-800" className="group flex items-center justify-between px-4 py-2 hover:bg-white/10"
> >
<User pubkey={follow} /> <User pubkey={follow} />
<div> <div>
<button <button
type="button" type="button"
onClick={() => openChat(follow)} onClick={() => openChat(follow)}
className="hidden w-max rounded-md bg-zinc-700 px-3 py-1 text-sm font-medium hover:bg-fuchsia-500 group-hover:inline-flex" className="hidden w-max rounded bg-white/10 px-3 py-1 text-sm font-medium hover:bg-fuchsia-500 group-hover:inline-flex"
> >
Chat Chat
</button> </button>
@ -113,11 +80,9 @@ export function NewMessageModal() {
)) ))
)} )}
</div> </div>
</Dialog.Panel>
</Transition.Child>
</div> </div>
</Dialog> </Dialog.Content>
</Transition> </Dialog.Portal>
</> </Dialog.Root>
); );
} }

View File

@ -1,103 +1,71 @@
import { Dialog, Transition } from '@headlessui/react'; import * as Dialog from '@radix-ui/react-dialog';
import { Fragment, useState } from 'react'; import { useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { User } from '@app/auth/components/user'; import { User } from '@app/auth/components/user';
import { CancelIcon, StrangersIcon } from '@shared/icons'; import { CancelIcon, PlusIcon } from '@shared/icons';
import { compactNumber } from '@utils/number'; import { compactNumber } from '@utils/number';
import { Chats } from '@utils/types'; import { Chats } from '@utils/types';
export function UnknownsModal({ data }: { data: Chats[] }) { export function UnknownsModal({ data }: { data: Chats[] }) {
const navigate = useNavigate(); const navigate = useNavigate();
const [isOpen, setIsOpen] = useState(false); const [open, setOpen] = useState(false);
const closeModal = () => {
setIsOpen(false);
};
const openModal = () => {
setIsOpen(true);
};
const openChat = (pubkey: string) => { const openChat = (pubkey: string) => {
closeModal(); setOpen(false);
navigate(`/app/chats/${pubkey}`); navigate(`/app/chats/${pubkey}`);
}; };
return ( return (
<> <Dialog.Root open={open} onOpenChange={setOpen}>
<Dialog.Trigger asChild>
<button <button
type="button" type="button"
onClick={() => openModal()}
className="inline-flex h-9 items-center gap-2.5 rounded-md px-2" className="inline-flex h-9 items-center gap-2.5 rounded-md px-2"
> >
<div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded bg-white/10"> <div className="inline-flex h-6 w-6 shrink-0 items-center justify-center rounded bg-white/10">
<StrangersIcon className="h-3 w-3 text-white" /> <PlusIcon className="h-3 w-3 text-white" />
</div> </div>
<div> <div>
<h5 className="text-white/50">{compactNumber.format(data.length)} unknowns</h5> <h5 className="text-white/50">
{compactNumber.format(data.length)} unknowns
</h5>
</div> </div>
</button> </button>
<Transition appear show={isOpen} as={Fragment}> </Dialog.Trigger>
<Dialog as="div" className="relative z-10" onClose={closeModal}> <Dialog.Portal className="relative z-10">
<Transition.Child <Dialog.Overlay className="fixed inset-0 z-50 bg-black/80 backdrop-blur-xl" />
as={Fragment} <Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
enter="ease-out duration-300" <div className="relative h-min w-full max-w-xl rounded-xl bg-white/10">
enterFrom="opacity-0" <div className="h-min w-full shrink-0 border-b border-white/10 bg-white/5 px-5 py-5">
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 z-50 bg-black bg-opacity-30 backdrop-blur-md" />
</Transition.Child>
<div className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className="relative flex h-min w-full max-w-lg flex-col gap-2 rounded-lg border-t border-zinc-800/50 bg-zinc-900">
<div className="h-min w-full shrink-0 border-b border-zinc-800 px-5 py-5">
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<Dialog.Title <Dialog.Title className="text-lg font-semibold leading-none text-zinc-100">
as="h3"
className="text-lg font-semibold leading-none text-zinc-100"
>
{data.length} unknowns {data.length} unknowns
</Dialog.Title> </Dialog.Title>
<button <Dialog.Close className="inline-flex h-6 w-6 items-center justify-center rounded-md hover:bg-white/10">
type="button" <CancelIcon className="h-4 w-4 text-white/50" />
onClick={closeModal} </Dialog.Close>
className="inline-flex h-5 w-5 items-center justify-center rounded hover:bg-zinc-800"
>
<CancelIcon className="h-4 w-4 text-zinc-300" />
</button>
</div> </div>
<Dialog.Description className="text-sm leading-tight text-zinc-400"> <Dialog.Description className="text-sm leading-none text-zinc-400">
All messages from people you not follow All messages from people you not follow
</Dialog.Description> </Dialog.Description>
</div> </div>
</div> </div>
<div className="flex h-[500px] flex-col overflow-y-auto overflow-x-hidden pb-5"> <div className="flex h-[500px] flex-col overflow-y-auto overflow-x-hidden pb-2 pt-2">
{data.map((user) => ( {data.map((user) => (
<div <div
key={user.sender_pubkey} key={user.sender_pubkey}
className="group flex items-center justify-between px-4 py-3 hover:bg-zinc-800" className="group flex items-center justify-between px-4 py-2 hover:bg-white/10"
> >
<User pubkey={user.sender_pubkey} /> <User pubkey={user.sender_pubkey} />
<div> <div>
<button <button
type="button" type="button"
onClick={() => openChat(user.sender_pubkey)} onClick={() => openChat(user.sender_pubkey)}
className="hidden w-max rounded-md bg-zinc-700 px-3 py-1 text-sm font-medium hover:bg-fuchsia-500 group-hover:inline-flex" className="hidden w-max rounded bg-white/10 px-3 py-1 text-sm font-medium hover:bg-fuchsia-500 group-hover:inline-flex"
> >
Chat Chat
</button> </button>
@ -105,11 +73,9 @@ export function UnknownsModal({ data }: { data: Chats[] }) {
</div> </div>
))} ))}
</div> </div>
</Dialog.Panel>
</Transition.Child>
</div> </div>
</Dialog> </Dialog.Content>
</Transition> </Dialog.Portal>
</> </Dialog.Root>
); );
} }

View File

@ -85,7 +85,7 @@ export function SpaceScreen() {
<AddBlock /> <AddBlock />
</div> </div>
</div> </div>
<div className="w-[350px] shrink-0" /> <div className="w-[250px] shrink-0" />
</div> </div>
); );
} }

View File

@ -58,21 +58,19 @@ export function Profile({ data }: { data: any }) {
); );
return ( return (
<div className="rounded-xl border-t border-zinc-800/50 bg-zinc-900 px-5 py-5"> <div className="rounded-xl bg-white/10 px-5 py-5">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="inline-flex items-center gap-2"> <div className="inline-flex items-center gap-2">
<div className="h-11 w-11 shrink-0">
<Image <Image
src={profile.picture} src={profile.picture}
fallback={DEFAULT_AVATAR} fallback={DEFAULT_AVATAR}
className="h-11 w-11 rounded-lg object-cover" className="h-11 w-11 shrink-0 rounded-lg object-cover"
/> />
</div>
<div className="inline-flex flex-col gap-1"> <div className="inline-flex flex-col gap-1">
<h3 className="max-w-[15rem] truncate font-semibold leading-none text-zinc-100"> <h3 className="max-w-[15rem] truncate font-semibold leading-none text-white">
{profile.display_name || profile.name} {profile.display_name || profile.name}
</h3> </h3>
<p className="max-w-[10rem] truncate text-sm leading-none text-zinc-400"> <p className="max-w-[10rem] truncate text-sm leading-none text-white/50">
{profile.nip05 || shortenKey(data.pubkey)} {profile.nip05 || shortenKey(data.pubkey)}
</p> </p>
</div> </div>
@ -81,15 +79,15 @@ export function Profile({ data }: { data: any }) {
{socialStatus === 'loading' ? ( {socialStatus === 'loading' ? (
<button <button
type="button" type="button"
className="inline-flex h-8 w-8 items-center justify-center rounded-md bg-zinc-900 hover:bg-fuchsia-500" className="inline-flex h-8 w-8 items-center justify-center rounded-md bg-white/10 hover:bg-fuchsia-500"
> >
<LoaderIcon className="h-4 w-4 animate-spin text-black dark:text-zinc-100" /> <LoaderIcon className="h-4 w-4 animate-spin text-white" />
</button> </button>
) : followed ? ( ) : followed ? (
<button <button
type="button" type="button"
onClick={() => unfollowUser(data.pubkey)} onClick={() => unfollowUser(data.pubkey)}
className="inline-flex h-8 w-8 items-center justify-center rounded-md bg-zinc-800 text-zinc-400 hover:bg-fuchsia-500 hover:text-white" className="inline-flex h-8 w-8 items-center justify-center rounded-md bg-white/10 text-white hover:bg-fuchsia-500 hover:text-white"
> >
<UnfollowIcon className="h-4 w-4" /> <UnfollowIcon className="h-4 w-4" />
</button> </button>
@ -97,7 +95,7 @@ export function Profile({ data }: { data: any }) {
<button <button
type="button" type="button"
onClick={() => followUser(data.pubkey)} onClick={() => followUser(data.pubkey)}
className="inline-flex h-8 w-8 items-center justify-center rounded-md bg-zinc-800 text-zinc-400 hover:bg-fuchsia-500 hover:text-white" className="inline-flex h-8 w-8 items-center justify-center rounded-md bg-white/10 text-white hover:bg-fuchsia-500 hover:text-white"
> >
<FollowIcon className="h-4 w-4" /> <FollowIcon className="h-4 w-4" />
</button> </button>
@ -105,7 +103,7 @@ export function Profile({ data }: { data: any }) {
</div> </div>
</div> </div>
<div className="mt-2"> <div className="mt-2">
<p className="whitespace-pre-line break-words text-zinc-100"> <p className="whitespace-pre-line break-words text-white">
{profile.about || profile.bio} {profile.about || profile.bio}
</p> </p>
</div> </div>
@ -115,26 +113,26 @@ export function Profile({ data }: { data: any }) {
) : ( ) : (
<div className="flex w-full items-center gap-8"> <div className="flex w-full items-center gap-8">
<div className="inline-flex flex-col gap-1"> <div className="inline-flex flex-col gap-1">
<span className="font-semibold leading-none text-zinc-100"> <span className="font-semibold leading-none text-white">
{userStats.stats[data.pubkey].followers_pubkey_count ?? 0} {userStats.stats[data.pubkey].followers_pubkey_count ?? 0}
</span> </span>
<span className="text-sm leading-none text-zinc-400">Followers</span> <span className="text-sm leading-none text-white/50">Followers</span>
</div> </div>
<div className="inline-flex flex-col gap-1"> <div className="inline-flex flex-col gap-1">
<span className="font-semibold leading-none text-zinc-100"> <span className="font-semibold leading-none text-white">
{userStats.stats[data.pubkey].pub_following_pubkey_count ?? 0} {userStats.stats[data.pubkey].pub_following_pubkey_count ?? 0}
</span> </span>
<span className="text-sm leading-none text-zinc-400">Following</span> <span className="text-sm leading-none text-white/50">Following</span>
</div> </div>
<div className="inline-flex flex-col gap-1"> <div className="inline-flex flex-col gap-1">
<span className="font-semibold leading-none text-zinc-100"> <span className="font-semibold leading-none text-white">
{userStats.stats[data.pubkey].zaps_received {userStats.stats[data.pubkey].zaps_received
? compactNumber.format( ? compactNumber.format(
userStats.stats[data.pubkey].zaps_received.msats / 1000 userStats.stats[data.pubkey].zaps_received.msats / 1000
) )
: 0} : 0}
</span> </span>
<span className="text-sm leading-none text-zinc-400">Zaps received</span> <span className="text-sm leading-none text-white/50">Zaps received</span>
</div> </div>
</div> </div>
)} )}

View File

@ -14,18 +14,18 @@ export function TrendingNotes() {
}); });
return ( return (
<div className="flex w-[360px] shrink-0 flex-col border-r border-zinc-900"> <div className="scrollbar-hide relative h-full w-[400px] shrink-0 overflow-y-auto bg-white/10 pb-20">
<TitleBar title="Trending Posts" /> <TitleBar title="Trending Posts" />
<div className="scrollbar-hide flex h-full w-full flex-col justify-between gap-1.5 overflow-y-auto pb-20 pt-1.5"> <div className="h-full">
{error && <p>Failed to fetch</p>} {error && <p>Failed to fetch</p>}
{status === 'loading' ? ( {status === 'loading' ? (
<div className="px-3 py-1.5"> <div className="px-3 py-1.5">
<div className="shadow-input rounded-md bg-zinc-900 px-3 py-3 shadow-black/20"> <div className="shadow-input rounded-md px-3 py-3">
<NoteSkeleton /> <NoteSkeleton />
</div> </div>
</div> </div>
) : ( ) : (
<div className="relative flex w-full flex-col pt-1.5"> <div className="relative flex w-full flex-col">
{data.notes.map((item) => ( {data.notes.map((item) => (
<NoteKind_1 key={item.id} event={item.event} skipMetadata={true} /> <NoteKind_1 key={item.id} event={item.event} skipMetadata={true} />
))} ))}

View File

@ -15,9 +15,9 @@ export function TrendingProfiles() {
}); });
return ( return (
<div className="flex w-[360px] shrink-0 flex-col border-r border-zinc-900"> <div className="scrollbar-hide relative h-full w-[400px] shrink-0 overflow-y-auto bg-white/10 pb-20">
<TitleBar title="Trending Profiles" /> <TitleBar title="Trending Profiles" />
<div className="scrollbar-hide flex h-full w-full flex-col justify-between gap-1.5 overflow-y-auto pb-20 pt-1.5"> <div className="h-full">
{error && <p>Failed to fetch</p>} {error && <p>Failed to fetch</p>}
{status === 'loading' ? ( {status === 'loading' ? (
<div className="px-3 py-1.5"> <div className="px-3 py-1.5">
@ -26,7 +26,7 @@ export function TrendingProfiles() {
</div> </div>
</div> </div>
) : ( ) : (
<div className="relative flex w-full flex-col gap-3 px-3 pt-3"> <div className="relative flex w-full flex-col gap-3 px-3 pt-1.5">
{data.profiles.map((item) => ( {data.profiles.map((item) => (
<Profile key={item.pubkey} data={item} /> <Profile key={item.pubkey} data={item} />
))} ))}

View File

@ -3,7 +3,7 @@ import { TrendingProfiles } from '@app/trending/components/trendingProfiles';
export function TrendingScreen() { export function TrendingScreen() {
return ( return (
<div className="scrollbar-hide flex h-full w-full flex-nowrap overflow-x-auto overflow-y-hidden"> <div className="scrollbar-hide flex h-full w-full flex-nowrap divide-x divide-white/5 overflow-x-auto overflow-y-hidden">
<TrendingProfiles /> <TrendingProfiles />
<TrendingNotes /> <TrendingNotes />
</div> </div>

View File

@ -19,7 +19,7 @@ button {
} }
.ProseMirror p.is-empty::before { .ProseMirror p.is-empty::before {
@apply text-zinc-500; @apply text-white/50;
content: attr(data-placeholder); content: attr(data-placeholder);
float: left; float: left;
height: 0; height: 0;

View File

@ -14,7 +14,6 @@ import { CancelIcon, LoaderIcon, PlusCircleIcon } from '@shared/icons';
import { MentionNote } from '@shared/notes'; import { MentionNote } from '@shared/notes';
import { useComposer } from '@stores/composer'; import { useComposer } from '@stores/composer';
import { FULL_RELAYS } from '@stores/constants';
import { usePublish } from '@utils/hooks/usePublish'; import { usePublish } from '@utils/hooks/usePublish';
import { useImageUploader } from '@utils/hooks/useUploader'; import { useImageUploader } from '@utils/hooks/useUploader';
@ -45,7 +44,7 @@ export function Composer() {
Image.configure({ Image.configure({
HTMLAttributes: { HTMLAttributes: {
class: class:
'rounded-lg w-2/3 h-auto border border-zinc-800 outline outline-2 outline-offset-0 outline-zinc-700 ml-1', 'rounded-lg w-2/3 h-auto border border-white/10 outline outline-2 outline-offset-0 outline-white/20 ml-1',
}, },
}), }),
], ],
@ -54,7 +53,7 @@ export function Composer() {
attributes: { attributes: {
class: twMerge( class: twMerge(
'scrollbar-hide markdown break-all max-h-[500px] overflow-y-auto outline-none pr-2', 'scrollbar-hide markdown break-all max-h-[500px] overflow-y-auto outline-none pr-2',
`${reply.id ? '!min-h-42' : '!min-h-[100px]'}` `${reply.id ? '!min-h-42' : '!min-h-[120px]'}`
), ),
}, },
}, },
@ -88,13 +87,13 @@ export function Composer() {
if (reply.id && reply.pubkey) { if (reply.id && reply.pubkey) {
if (reply.root && reply.root.length > 1) { if (reply.root && reply.root.length > 1) {
tags = [ tags = [
['e', reply.root, FULL_RELAYS[0], 'root'], ['e', reply.root, 'wss://relayable.org', 'root'],
['e', reply.id, FULL_RELAYS[0], 'reply'], ['e', reply.id, 'wss://relayable.org', 'reply'],
['p', reply.pubkey], ['p', reply.pubkey],
]; ];
} else { } else {
tags = [ tags = [
['e', reply.id, FULL_RELAYS[0], 'reply'], ['e', reply.id, 'wss://relayable.org', 'reply'],
['p', reply.pubkey], ['p', reply.pubkey],
]; ];
} }
@ -131,7 +130,7 @@ export function Composer() {
<div className="flex h-full flex-col px-4 pb-4"> <div className="flex h-full flex-col px-4 pb-4">
<div className="flex h-full w-full gap-3"> <div className="flex h-full w-full gap-3">
<div className="flex w-8 shrink-0 items-center justify-center"> <div className="flex w-8 shrink-0 items-center justify-center">
<div className="h-full w-[2px] bg-zinc-800" /> <div className="h-full w-[2px] bg-white/10" />
</div> </div>
<div className="w-full"> <div className="w-full">
<EditorContent <EditorContent
@ -147,9 +146,9 @@ export function Composer() {
<button <button
type="button" type="button"
onClick={() => clearReply()} onClick={() => clearReply()}
className="absolute right-3 top-3 inline-flex h-6 w-6 items-center justify-center gap-2 rounded bg-zinc-800 px-2 hover:bg-zinc-700" className="absolute right-3 top-3 inline-flex h-6 w-6 items-center justify-center rounded bg-white/10 px-2"
> >
<CancelIcon className="h-4 w-4 text-zinc-100" /> <CancelIcon className="h-4 w-4 text-white" />
</button> </button>
</div> </div>
)} )}
@ -159,15 +158,15 @@ export function Composer() {
<button <button
type="button" type="button"
onClick={() => uploadImage()} onClick={() => uploadImage()}
className="inline-flex h-8 w-8 items-center justify-center rounded-md hover:bg-zinc-800" className="inline-flex h-8 w-8 items-center justify-center rounded-md hover:bg-white/10"
> >
<PlusCircleIcon className="h-5 w-5 text-zinc-500" /> <PlusCircleIcon className="h-5 w-5 text-white/50" />
</button> </button>
<Button onClick={() => submit()} preset="publish"> <Button onClick={() => submit()} preset="publish">
{status === 'loading' ? ( {status === 'loading' ? (
<LoaderIcon className="h-4 w-4 animate-spin text-zinc-100" /> <LoaderIcon className="h-4 w-4 animate-spin text-white" />
) : ( ) : (
'Publish' 'Postr'
)} )}
</Button> </Button>
</div> </div>

View File

@ -1,5 +1,4 @@
import { Dialog, Transition } from '@headlessui/react'; import * as Dialog from '@radix-ui/react-dialog';
import { Fragment } from 'react';
import { useHotkeys } from 'react-hotkeys-hook'; import { useHotkeys } from 'react-hotkeys-hook';
import { Button } from '@shared/button'; import { Button } from '@shared/button';
@ -27,64 +26,39 @@ export function ComposerModal() {
useHotkeys(COMPOSE_SHORTCUT, () => toggle(true)); useHotkeys(COMPOSE_SHORTCUT, () => toggle(true));
return ( return (
<> <Dialog.Root open={open}>
<Button onClick={() => toggle(true)} preset="small"> <Dialog.Trigger asChild>
<Button preset="small">
<ComposeIcon className="h-4 w-4" /> <ComposeIcon className="h-4 w-4" />
Postr Postr
</Button> </Button>
<Transition appear show={open} as={Fragment}> </Dialog.Trigger>
<Dialog as="div" className="relative z-10" onClose={closeModal}> <Dialog.Portal className="relative z-10">
<Transition.Child <Dialog.Overlay className="fixed inset-0 z-50 bg-black/80 backdrop-blur-xl" />
as={Fragment} <Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
enter="ease-out duration-300" <div className="relative h-min w-full max-w-2xl rounded-xl bg-white/10">
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 z-50 bg-black bg-opacity-30 backdrop-blur-md" />
</Transition.Child>
<div className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className="relative h-min w-full max-w-xl rounded-xl border-t border-zinc-800/50 bg-zinc-900">
<div className="flex items-center justify-between px-4 py-4"> <div className="flex items-center justify-between px-4 py-4">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{account && <ComposerUser pubkey={account.pubkey} />} {account && <ComposerUser pubkey={account.pubkey} />}
<span> <span>
<ChevronRightIcon <ChevronRightIcon className="h-4 w-4 text-white/50" />
width={14}
height={14}
className="text-zinc-500"
/>
</span> </span>
<div className="inline-flex h-7 w-max items-center justify-center gap-0.5 rounded bg-zinc-800 pl-3 pr-1.5 text-sm font-medium text-zinc-400"> <div className="inline-flex h-7 w-max items-center justify-center gap-0.5 rounded bg-white/10 pl-3 pr-1.5 text-sm font-medium text-white">
New Post New Post
<ChevronDownIcon className="h-4 w-4" /> <ChevronDownIcon className="h-4 w-4" />
</div> </div>
</div> </div>
<button <Dialog.Close
onClick={closeModal} onClick={() => closeModal()}
type="button" className="inline-flex h-8 w-8 items-center justify-center rounded-md hover:bg-zinc-800"
className="inline-flex h-5 w-5 items-center justify-center rounded hover:bg-zinc-800"
> >
<CancelIcon width={16} height={16} className="text-zinc-500" /> <CancelIcon className="h-5 w-5 text-white/50" />
</button> </Dialog.Close>
</div> </div>
<Composer /> <Composer />
</Dialog.Panel>
</Transition.Child>
</div> </div>
</Dialog> </Dialog.Content>
</Transition> </Dialog.Portal>
</> </Dialog.Root>
); );
} }

View File

@ -9,15 +9,13 @@ export function ComposerUser({ pubkey }: { pubkey: string }) {
return ( return (
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className="h-8 w-8 shrink-0 overflow-hidden rounded-md bg-zinc-900">
<Image <Image
src={user?.picture || user?.image} src={user?.picture || user?.image}
fallback={DEFAULT_AVATAR} fallback={DEFAULT_AVATAR}
alt={pubkey} alt={pubkey}
className="h-8 w-8 object-cover" className="h-8 w-8 shrink-0 rounded-md object-cover"
/> />
</div> <h5 className="text-base font-semibold leading-none text-white">
<h5 className="text-base font-semibold leading-none text-zinc-100">
{user?.nip05 || user?.name || ( {user?.nip05 || user?.name || (
<div className="h-3 w-20 animate-pulse rounded-sm bg-zinc-700" /> <div className="h-3 w-20 animate-pulse rounded-sm bg-zinc-700" />
)} )}

View File

@ -1,4 +1,4 @@
import { Dialog, Transition } from '@headlessui/react'; import * as Dialog from '@radix-ui/react-dialog';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import { relaunch } from '@tauri-apps/plugin-process'; import { relaunch } from '@tauri-apps/plugin-process';
import { Fragment, useState } from 'react'; import { Fragment, useState } from 'react';
@ -29,60 +29,34 @@ export function Logout() {
}; };
return ( return (
<> <Dialog.Root>
<Dialog.Trigger asChild>
<button <button
type="button" type="button"
onClick={() => openModal()}
aria-label="Logout" aria-label="Logout"
className="inline-flex h-9 w-9 transform items-center justify-center rounded-md bg-white/20 active:translate-y-1" className="inline-flex h-9 w-9 transform items-center justify-center rounded-md bg-white/20 active:translate-y-1"
> >
<LogoutIcon className="h-4 w-4 text-white" /> <LogoutIcon className="h-4 w-4 text-white" />
</button> </button>
<Transition appear show={isOpen} as={Fragment}> </Dialog.Trigger>
<Dialog as="div" className="relative z-10" onClose={closeModal}> <Dialog.Portal className="relative z-10">
<Transition.Child <Dialog.Overlay className="fixed inset-0 z-50 bg-black/80 backdrop-blur-xl" />
as={Fragment} <Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
enter="ease-out duration-300" <div className="relative h-min w-full max-w-xl rounded-xl bg-white/10">
enterFrom="opacity-0" <div className="h-min w-full shrink-0 border-b border-white/10 bg-white/5 px-5 py-6">
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 z-50 bg-black bg-opacity-30 backdrop-blur-md" />
</Transition.Child>
<div className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<Dialog.Panel className="relative flex h-min w-full max-w-lg flex-col rounded-xl border-t border-zinc-800/50 bg-zinc-900">
<div className="h-min w-full shrink-0 border-b border-zinc-800 px-5 py-6">
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<Dialog.Title <Dialog.Title className="text-lg font-semibold leading-none text-white">
as="h3"
className="text-lg font-semibold leading-none text-zinc-100"
>
Are you sure! Are you sure!
</Dialog.Title> </Dialog.Title>
<button <Dialog.Close className="inline-flex h-6 w-6 items-center justify-center rounded-md hover:bg-white/10">
type="button" <CancelIcon className="h-4 w-4 text-white/50" />
onClick={closeModal} </Dialog.Close>
className="inline-flex h-5 w-5 items-center justify-center rounded hover:bg-zinc-900"
>
<CancelIcon width={20} height={20} className="text-zinc-300" />
</button>
</div> </div>
<Dialog.Description className="text-sm leading-tight text-zinc-400"> <Dialog.Description className="text-sm leading-tight text-white/50">
<p className="mb-2"> <p className="mb-2">
When logout, all local data will be wiped, and restart app then When logout, all local data will be wiped, and restart app then you
you need to start onboarding process again when you log in. need to start onboarding process again when you log in.
</p> </p>
<p> <p>
In the next version, Lume will support multi account, then you can In the next version, Lume will support multi account, then you can
@ -96,24 +70,22 @@ export function Logout() {
<button <button
type="button" type="button"
onClick={closeModal} onClick={closeModal}
className="inline-flex h-9 items-center justify-center rounded-md px-3 text-sm font-medium text-zinc-400 hover:bg-zinc-800 hover:text-zinc-100" className="inline-flex h-9 items-center justify-center rounded-md px-3 text-sm font-medium text-white/50 hover:bg-white/10"
> >
Cancel Cancel
</button> </button>
<button <button
type="button" type="button"
onClick={() => logout()} onClick={() => logout()}
className="inline-flex h-9 items-center justify-center rounded-md bg-red-500 px-3 text-sm font-medium text-zinc-100 hover:bg-red-600" className="inline-flex h-9 items-center justify-center rounded-md bg-red-500 px-3 text-sm font-medium text-white hover:bg-red-600"
> >
Confirm Confirm
</button> </button>
</div> </div>
</div> </div>
</Dialog.Panel>
</Transition.Child>
</div> </div>
</Dialog> </Dialog.Content>
</Transition> </Dialog.Portal>
</> </Dialog.Root>
); );
} }

View File

@ -23,6 +23,7 @@ export function Navigation() {
return ( return (
<div className="relative h-full w-[232px] bg-black/80"> <div className="relative h-full w-[232px] bg-black/80">
<div className="absolute left-0 top-0 h-8 w-full" data-tauri-drag-region />
<div className="scrollbar-hide flex flex-col gap-5 overflow-y-auto pb-20"> <div className="scrollbar-hide flex flex-col gap-5 overflow-y-auto pb-20">
<div className="inline-flex h-8 items-center justify-between px-2 pb-4 pt-14"> <div className="inline-flex h-8 items-center justify-between px-2 pb-4 pt-14">
<ComposerModal /> <ComposerModal />
@ -53,7 +54,7 @@ export function Navigation() {
open ? '' : 'rotate-180' open ? '' : 'rotate-180'
)} )}
> >
<NavArrowDownIcon width={12} height={12} className="text-white/50" /> <NavArrowDownIcon className="h-3 w-3 text-white/50" />
</div> </div>
<h3 className="text-[11px] font-bold uppercase tracking-widest text-white/50"> <h3 className="text-[11px] font-bold uppercase tracking-widest text-white/50">
Feeds Feeds
@ -106,7 +107,7 @@ export function Navigation() {
open ? '' : 'rotate-180' open ? '' : 'rotate-180'
)} )}
> >
<NavArrowDownIcon width={12} height={12} className="text-white/50" /> <NavArrowDownIcon className="h-3 w-3 text-white/50" />
</div> </div>
<h3 className="text-[11px] font-bold uppercase tracking-widest text-white/50"> <h3 className="text-[11px] font-bold uppercase tracking-widest text-white/50">
Chats Chats

View File

@ -80,7 +80,7 @@ export function NoteReaction({ id, pubkey }: { id: string; pubkey: string }) {
<button <button
type="button" type="button"
onClick={() => react('👏')} onClick={() => react('👏')}
className="inline-flex h-8 w-8 items-center justify-center rounded hover:bg-zinc-600" className="inline-flex h-8 w-8 items-center justify-center rounded hover:bg-white/10"
> >
<img <img
src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Hand%20gestures/Clapping%20Hands.png" src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Hand%20gestures/Clapping%20Hands.png"
@ -91,7 +91,7 @@ export function NoteReaction({ id, pubkey }: { id: string; pubkey: string }) {
<button <button
type="button" type="button"
onClick={() => react('🤪')} onClick={() => react('🤪')}
className="inline-flex h-7 w-7 items-center justify-center rounded hover:bg-zinc-600" className="inline-flex h-7 w-7 items-center justify-center rounded hover:bg-white/10"
> >
<img <img
src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Smilies/Face%20with%20Tongue.png" src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Smilies/Face%20with%20Tongue.png"
@ -102,7 +102,7 @@ export function NoteReaction({ id, pubkey }: { id: string; pubkey: string }) {
<button <button
type="button" type="button"
onClick={() => react('😮')} onClick={() => react('😮')}
className="inline-flex h-7 w-7 items-center justify-center rounded hover:bg-zinc-600" className="inline-flex h-7 w-7 items-center justify-center rounded hover:bg-white/10"
> >
<img <img
src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Smilies/Face%20with%20Open%20Mouth.png" src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Smilies/Face%20with%20Open%20Mouth.png"
@ -113,7 +113,7 @@ export function NoteReaction({ id, pubkey }: { id: string; pubkey: string }) {
<button <button
type="button" type="button"
onClick={() => react('😢')} onClick={() => react('😢')}
className="inline-flex h-7 w-7 items-center justify-center rounded hover:bg-zinc-600" className="inline-flex h-7 w-7 items-center justify-center rounded hover:bg-white/10"
> >
<img <img
src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Smilies/Crying%20Face.png" src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Smilies/Crying%20Face.png"
@ -124,7 +124,7 @@ export function NoteReaction({ id, pubkey }: { id: string; pubkey: string }) {
<button <button
type="button" type="button"
onClick={() => react('🤡')} onClick={() => react('🤡')}
className="inline-flex h-7 w-7 items-center justify-center rounded hover:bg-zinc-600" className="inline-flex h-7 w-7 items-center justify-center rounded hover:bg-white/10"
> >
<img <img
src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Smilies/Clown%20Face.png" src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Smilies/Clown%20Face.png"

View File

@ -29,7 +29,7 @@ export const MentionNote = memo(function MentionNote({ id }: { id: string }) {
onKeyDown={(e) => openThread(e, id)} onKeyDown={(e) => openThread(e, id)}
role="button" role="button"
tabIndex={0} tabIndex={0}
className="mb-2 mt-3 cursor-default rounded-lg border-t border-zinc-700/50 bg-zinc-800/50 px-3 py-3" className="mb-2 mt-3 cursor-default rounded-lg bg-white/10 px-3 py-3"
> >
{status === 'loading' ? ( {status === 'loading' ? (
<NoteSkeleton /> <NoteSkeleton />

View File

@ -27,10 +27,11 @@ export function LinkPreview({ urls }: { urls: string[] }) {
rel="noreferrer" rel="noreferrer"
> >
{error ? ( {error ? (
<div className="px-3 py-3"> <div className="flex flex-col gap-2 px-3 py-3">
<p className="line-clamp-3 break-all text-sm text-zinc-400"> <p className="text-sm text-white/50">
Can&apos;t fetch open graph, click to open webpage Can&apos;t fetch open graph, click to open webpage
</p> </p>
<span className="text-sm leading-none text-white">{domain.hostname}</span>
</div> </div>
) : ( ) : (
<> <>

View File

@ -57,10 +57,10 @@ export function NotificationModal({ pubkey }: { pubkey: string }) {
</button> </button>
</Dialog.Trigger> </Dialog.Trigger>
<Dialog.Portal className="relative z-10"> <Dialog.Portal className="relative z-10">
<Dialog.Overlay className="fixed inset-0 z-[1000px] bg-black bg-opacity-30 backdrop-blur-md data-[state=open]:animate-overlayShow" /> <Dialog.Overlay className="fixed inset-0 z-50 bg-black/80 backdrop-blur-xl" />
<div className="fixed inset-0 z-50 flex min-h-full items-center justify-center data-[state=open]:animate-contentShow"> <Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
<Dialog.Content className="relative flex h-min w-full max-w-lg flex-col gap-2 rounded-lg border-t border-zinc-800/50 bg-zinc-900"> <div className="relative h-min w-full max-w-xl rounded-xl bg-white/10">
<div className="h-min w-full shrink-0 border-b border-zinc-800 px-5 py-5"> <div className="h-min w-full shrink-0 border-b border-white/10 bg-white/5 px-5 py-5">
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<Dialog.Title className="text-lg font-semibold leading-none text-zinc-100"> <Dialog.Title className="text-lg font-semibold leading-none text-zinc-100">
@ -96,8 +96,8 @@ export function NotificationModal({ pubkey }: { pubkey: string }) {
data.map((event) => renderItem(event)) data.map((event) => renderItem(event))
)} )}
</div> </div>
</Dialog.Content>
</div> </div>
</Dialog.Content>
</Dialog.Portal> </Dialog.Portal>
</Dialog.Root> </Dialog.Root>
); );

View File

@ -1,6 +1,6 @@
import { Popover, Transition } from '@headlessui/react'; import * as Popover from '@radix-ui/react-popover';
import { Fragment } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { twMerge } from 'tailwind-merge';
import { VerticalDotsIcon } from '@shared/icons'; import { VerticalDotsIcon } from '@shared/icons';
import { Image } from '@shared/image'; import { Image } from '@shared/image';
@ -9,7 +9,7 @@ import { DEFAULT_AVATAR } from '@stores/constants';
import { formatCreatedAt } from '@utils/createdAt'; import { formatCreatedAt } from '@utils/createdAt';
import { useProfile } from '@utils/hooks/useProfile'; import { useProfile } from '@utils/hooks/useProfile';
import { shortenKey } from '@utils/shortenKey'; import { displayNpub, shortenKey } from '@utils/shortenKey';
export function User({ export function User({
pubkey, pubkey,
@ -50,58 +50,56 @@ export function User({
} }
return ( return (
<Popover <Popover.Root>
className={`flex ${size === 'small' ? 'items-center gap-2' : 'items-start gap-3'}`} <div
className={twMerge(
'relative flex',
size === 'small' ? 'items-center gap-2' : 'items-start gap-3'
)}
> >
<Popover.Button <Popover.Trigger asChild>
className={`${avatarWidth} ${avatarHeight} relative z-10 shrink-0 overflow-hidden bg-zinc-900`} <button
type="button"
className={`${avatarWidth} ${avatarHeight} relative z-40 shrink-0 overflow-hidden`}
> >
<Image <Image
src={user?.picture || user?.image} src={user?.picture || user?.image}
fallback={DEFAULT_AVATAR} fallback={DEFAULT_AVATAR}
alt={pubkey} alt={pubkey}
className={`${ className={twMerge(
`object-cover ${avatarWidth} ${avatarHeight}`,
size === 'small' ? 'rounded' : 'rounded-lg',
isRepost ? 'ring-1 ring-zinc-800' : '' isRepost ? 'ring-1 ring-zinc-800' : ''
} ${avatarWidth} ${avatarHeight} ${ )}
size === 'small' ? 'rounded' : 'rounded-lg'
} object-cover`}
/> />
</Popover.Button> </button>
</Popover.Trigger>
<div <div
className={`${isRepost ? 'mt-4' : ''} flex flex-1 items-baseline justify-between`} className={twMerge(
'flex flex-1 items-baseline justify-between',
isRepost ? 'mt-4' : ''
)}
> >
<h5 <h5
className={`truncate font-semibold leading-none text-zinc-100 ${ className={twMerge(
'truncate font-semibold leading-none text-white',
size === 'small' ? 'max-w-[10rem]' : 'max-w-[15rem]' size === 'small' ? 'max-w-[10rem]' : 'max-w-[15rem]'
}`} )}
> >
{user?.nip05?.toLowerCase() || {user?.nip05?.toLowerCase() ||
user?.name || user?.name ||
user?.display_name || user?.display_name ||
shortenKey(pubkey)} shortenKey(pubkey)}
</h5> </h5>
<div className="inline-flex items-center gap-2">
<span className="leading-none text-white/50">{createdAt}</span> <span className="leading-none text-white/50">{createdAt}</span>
<button
type="button"
className="inline-flex h-5 w-max items-center justify-center rounded px-1 hover:bg-white/20"
>
<VerticalDotsIcon className="h-4 w-4 rotate-90 transform text-white/50" />
</button>
</div> </div>
</div> </div>
<Transition <Popover.Portal>
as={Fragment} <Popover.Content
enter="transition ease-out duration-200" className="w-[300px] overflow-hidden rounded-md bg-white/10 backdrop-blur-xl"
enterFrom="opacity-0 translate-y-1" sideOffset={5}
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
> >
<Popover.Panel className="absolute top-10 z-50 mt-3"> <div className="flex gap-2.5 border-b border-white/5 px-3 py-3">
<div className="w-[250px] overflow-hidden rounded-md border border-zinc-800/50 bg-zinc-900/90 backdrop-blur-lg">
<div className="flex gap-2.5 border-b border-zinc-800 px-3 py-3">
<Image <Image
src={user?.picture || user?.image} src={user?.picture || user?.image}
fallback={DEFAULT_AVATAR} fallback={DEFAULT_AVATAR}
@ -111,16 +109,14 @@ export function User({
<div className="flex flex-1 flex-col gap-2"> <div className="flex flex-1 flex-col gap-2">
<div className="inline-flex flex-col gap-1"> <div className="inline-flex flex-col gap-1">
<h5 className="text-sm font-semibold leading-none"> <h5 className="text-sm font-semibold leading-none">
{user?.displayName || user?.name || ( {user?.displayName || user?.name}
<div className="h-3 w-20 animate-pulse rounded-sm bg-zinc-700" />
)}
</h5> </h5>
<span className="max-w-[10rem] truncate text-sm leading-none text-zinc-500"> <span className="max-w-[10rem] truncate text-sm leading-none text-white/50">
{user?.nip05 || shortenKey(pubkey)} {user?.nip05 || displayNpub(pubkey, 16)}
</span> </span>
</div> </div>
<div> <div>
<p className="line-clamp-3 break-all leading-tight text-zinc-100"> <p className="line-clamp-3 break-all leading-tight text-white">
{user?.about} {user?.about}
</p> </p>
</div> </div>
@ -129,20 +125,19 @@ export function User({
<div className="flex items-center gap-2 px-3 py-3"> <div className="flex items-center gap-2 px-3 py-3">
<Link <Link
to={`/app/users/${pubkey}`} to={`/app/users/${pubkey}`}
className="inline-flex h-10 flex-1 items-center justify-center rounded-md bg-zinc-700 text-sm font-medium hover:bg-fuchsia-500" className="inline-flex h-10 flex-1 items-center justify-center rounded-md bg-white/10 text-sm font-medium hover:bg-fuchsia-500"
> >
View profile View profile
</Link> </Link>
<Link <Link
to={`/app/chats/${pubkey}`} to={`/app/chats/${pubkey}`}
className="inline-flex h-10 flex-1 items-center justify-center rounded-md bg-zinc-700 text-sm font-medium hover:bg-fuchsia-500" className="inline-flex h-10 flex-1 items-center justify-center rounded-md bg-white/10 text-sm font-medium hover:bg-fuchsia-500"
> >
Message Message
</Link> </Link>
</div> </div>
</div> </Popover.Content>
</Popover.Panel> </Popover.Portal>
</Transition> </Popover.Root>
</Popover>
); );
} }