mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-18 11:13:30 +00:00
wip: port more component to new ui
This commit is contained in:
parent
a85bcf917b
commit
babcd8698e
@ -102,74 +102,64 @@ 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-white/50">
|
||||||
<label htmlFor="privkey" className="font-medium text-zinc-200">
|
Private key
|
||||||
Private key
|
</label>
|
||||||
</label>
|
<div className="relative">
|
||||||
<div className="relative">
|
<input
|
||||||
<input
|
{...register('privkey', { required: true })}
|
||||||
{...register('privkey', { required: true })}
|
type="text"
|
||||||
type="text"
|
placeholder="nsec..."
|
||||||
placeholder="nsec..."
|
className="relative h-12 w-full rounded-lg bg-white/10 px-3.5 py-1 text-white !outline-none placeholder:text-white/10"
|
||||||
className="relative w-full rounded-lg bg-zinc-800 px-3.5 py-3 text-zinc-100 !outline-none placeholder:text-zinc-400"
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
</div>
|
||||||
<label htmlFor="password" className="font-medium text-zinc-200">
|
<div className="flex flex-col gap-1">
|
||||||
Set a new password to protect your key
|
<label htmlFor="password" className="font-medium text-white/50">
|
||||||
</label>
|
Set a new password to protect your key
|
||||||
<div className="relative">
|
</label>
|
||||||
<input
|
<div className="relative">
|
||||||
{...register('password', { required: true })}
|
<input
|
||||||
type={passwordInput}
|
{...register('password', { required: true })}
|
||||||
placeholder="min. 4 characters"
|
type={passwordInput}
|
||||||
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"
|
placeholder="min. 4 characters"
|
||||||
/>
|
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
|
/>
|
||||||
type="button"
|
|
||||||
onClick={() => showPassword()}
|
|
||||||
className="group absolute right-2 top-1/2 -translate-y-1/2 transform rounded p-1 hover:bg-zinc-700"
|
|
||||||
>
|
|
||||||
{passwordInput === 'password' ? (
|
|
||||||
<EyeOffIcon
|
|
||||||
width={20}
|
|
||||||
height={20}
|
|
||||||
className="text-zinc-500 group-hover:text-zinc-100"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<EyeOnIcon
|
|
||||||
width={20}
|
|
||||||
height={20}
|
|
||||||
className="text-zinc-500 group-hover:text-zinc-100"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<span className="text-sm text-red-400">
|
|
||||||
{errors.password && <p>{errors.password.message}</p>}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-center">
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="button"
|
||||||
disabled={!isDirty || !isValid}
|
onClick={() => showPassword()}
|
||||||
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="group absolute right-2 top-1/2 -translate-y-1/2 transform rounded p-1 hover:bg-white/10"
|
||||||
>
|
>
|
||||||
{loading ? (
|
{passwordInput === 'password' ? (
|
||||||
<LoaderIcon className="h-4 w-4 animate-spin text-black dark:text-zinc-100" />
|
<EyeOffIcon className="h-5 w-5 text-white/50 group-hover:text-white" />
|
||||||
) : (
|
) : (
|
||||||
'Continue →'
|
<EyeOnIcon className="h-5 w-5 text-white/50 group-hover:text-white" />
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
<span className="text-sm text-red-400">
|
||||||
</div>
|
{errors.password && <p>{errors.password.message}</p>}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-center">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={!isDirty || !isValid}
|
||||||
|
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 ? (
|
||||||
|
<LoaderIcon className="h-4 w-4 animate-spin text-white" />
|
||||||
|
) : (
|
||||||
|
'Continue →'
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -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>
|
||||||
|
@ -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,114 +10,79 @@ 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}>
|
||||||
<button
|
<Dialog.Trigger asChild>
|
||||||
type="button"
|
<button
|
||||||
onClick={() => openModal()}
|
type="button"
|
||||||
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">
|
||||||
<PlusIcon className="h-3 w-3 text-white" />
|
<PlusIcon className="h-3 w-3 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<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"
|
<div className="flex flex-col gap-1">
|
||||||
leave="ease-in duration-200"
|
<div className="flex items-center justify-between">
|
||||||
leaveFrom="opacity-100"
|
<Dialog.Title className="text-lg font-semibold leading-none text-zinc-100">
|
||||||
leaveTo="opacity-0"
|
New chat
|
||||||
>
|
</Dialog.Title>
|
||||||
<div className="fixed inset-0 z-50 bg-black bg-opacity-30 backdrop-blur-md" />
|
<Dialog.Close className="inline-flex h-6 w-6 items-center justify-center rounded-md hover:bg-white/10">
|
||||||
</Transition.Child>
|
<CancelIcon className="h-4 w-4 text-white/50" />
|
||||||
<div className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
|
</Dialog.Close>
|
||||||
<Transition.Child
|
</div>
|
||||||
as={Fragment}
|
<Dialog.Description className="text-sm leading-none text-zinc-400">
|
||||||
enter="ease-out duration-300"
|
All messages will be encrypted, but anyone can see who you chat
|
||||||
enterFrom="opacity-0 scale-95"
|
</Dialog.Description>
|
||||||
enterTo="opacity-100 scale-100"
|
</div>
|
||||||
leave="ease-in duration-200"
|
</div>
|
||||||
leaveFrom="opacity-100 scale-100"
|
<div className="flex h-[500px] flex-col overflow-y-auto overflow-x-hidden pb-2 pt-2">
|
||||||
leaveTo="opacity-0 scale-95"
|
{status === 'loading' ? (
|
||||||
>
|
<div className="inline-flex items-center justify-center px-4 py-3">
|
||||||
<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">
|
<LoaderIcon className="h-5 w-5 animate-spin text-white" />
|
||||||
<div className="h-min w-full shrink-0 border-b border-zinc-800 px-5 py-5">
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
) : (
|
||||||
<div className="flex items-center justify-between">
|
follows.map((follow) => (
|
||||||
<Dialog.Title
|
<div
|
||||||
as="h3"
|
key={follow}
|
||||||
className="text-lg font-semibold leading-none text-zinc-100"
|
className="group flex items-center justify-between px-4 py-2 hover:bg-white/10"
|
||||||
>
|
>
|
||||||
New chat
|
<User pubkey={follow} />
|
||||||
</Dialog.Title>
|
<div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={closeModal}
|
onClick={() => openChat(follow)}
|
||||||
className="inline-flex h-5 w-5 items-center justify-center rounded hover:bg-zinc-800"
|
className="hidden w-max rounded bg-white/10 px-3 py-1 text-sm font-medium hover:bg-fuchsia-500 group-hover:inline-flex"
|
||||||
>
|
>
|
||||||
<CancelIcon className="h-4 w-4 text-zinc-300" />
|
Chat
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<Dialog.Description className="text-sm leading-tight text-zinc-400">
|
|
||||||
All messages will be encrypted, but anyone can see who you chat
|
|
||||||
</Dialog.Description>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
))
|
||||||
<div className="flex h-[500px] flex-col overflow-y-auto overflow-x-hidden pb-5">
|
)}
|
||||||
{status === 'loading' ? (
|
</div>
|
||||||
<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" />
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
follows.map((follow) => (
|
|
||||||
<div
|
|
||||||
key={follow}
|
|
||||||
className="group flex items-center justify-between px-4 py-3 hover:bg-zinc-800"
|
|
||||||
>
|
|
||||||
<User pubkey={follow} />
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
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"
|
|
||||||
>
|
|
||||||
Chat
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Dialog.Panel>
|
|
||||||
</Transition.Child>
|
|
||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog.Content>
|
||||||
</Transition>
|
</Dialog.Portal>
|
||||||
</>
|
</Dialog.Root>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,115 +1,81 @@
|
|||||||
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}>
|
||||||
<button
|
<Dialog.Trigger asChild>
|
||||||
type="button"
|
<button
|
||||||
onClick={() => openModal()}
|
type="button"
|
||||||
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">
|
||||||
</div>
|
{compactNumber.format(data.length)} unknowns
|
||||||
</button>
|
</h5>
|
||||||
<Transition appear show={isOpen} as={Fragment}>
|
</div>
|
||||||
<Dialog as="div" className="relative z-10" onClose={closeModal}>
|
</button>
|
||||||
<Transition.Child
|
</Dialog.Trigger>
|
||||||
as={Fragment}
|
<Dialog.Portal className="relative z-10">
|
||||||
enter="ease-out duration-300"
|
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/80 backdrop-blur-xl" />
|
||||||
enterFrom="opacity-0"
|
<Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
|
||||||
enterTo="opacity-100"
|
<div className="relative h-min w-full max-w-xl rounded-xl bg-white/10">
|
||||||
leave="ease-in duration-200"
|
<div className="h-min w-full shrink-0 border-b border-white/10 bg-white/5 px-5 py-5">
|
||||||
leaveFrom="opacity-100"
|
<div className="flex flex-col gap-1">
|
||||||
leaveTo="opacity-0"
|
<div className="flex items-center justify-between">
|
||||||
>
|
<Dialog.Title className="text-lg font-semibold leading-none text-zinc-100">
|
||||||
<div className="fixed inset-0 z-50 bg-black bg-opacity-30 backdrop-blur-md" />
|
{data.length} unknowns
|
||||||
</Transition.Child>
|
</Dialog.Title>
|
||||||
<div className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
|
<Dialog.Close className="inline-flex h-6 w-6 items-center justify-center rounded-md hover:bg-white/10">
|
||||||
<Transition.Child
|
<CancelIcon className="h-4 w-4 text-white/50" />
|
||||||
as={Fragment}
|
</Dialog.Close>
|
||||||
enter="ease-out duration-300"
|
</div>
|
||||||
enterFrom="opacity-0 scale-95"
|
<Dialog.Description className="text-sm leading-none text-zinc-400">
|
||||||
enterTo="opacity-100 scale-100"
|
All messages from people you not follow
|
||||||
leave="ease-in duration-200"
|
</Dialog.Description>
|
||||||
leaveFrom="opacity-100 scale-100"
|
</div>
|
||||||
leaveTo="opacity-0 scale-95"
|
</div>
|
||||||
>
|
<div className="flex h-[500px] flex-col overflow-y-auto overflow-x-hidden pb-2 pt-2">
|
||||||
<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">
|
{data.map((user) => (
|
||||||
<div className="h-min w-full shrink-0 border-b border-zinc-800 px-5 py-5">
|
<div
|
||||||
<div className="flex flex-col gap-1">
|
key={user.sender_pubkey}
|
||||||
<div className="flex items-center justify-between">
|
className="group flex items-center justify-between px-4 py-2 hover:bg-white/10"
|
||||||
<Dialog.Title
|
>
|
||||||
as="h3"
|
<User pubkey={user.sender_pubkey} />
|
||||||
className="text-lg font-semibold leading-none text-zinc-100"
|
<div>
|
||||||
>
|
<button
|
||||||
{data.length} unknowns
|
type="button"
|
||||||
</Dialog.Title>
|
onClick={() => openChat(user.sender_pubkey)}
|
||||||
<button
|
className="hidden w-max rounded bg-white/10 px-3 py-1 text-sm font-medium hover:bg-fuchsia-500 group-hover:inline-flex"
|
||||||
type="button"
|
>
|
||||||
onClick={closeModal}
|
Chat
|
||||||
className="inline-flex h-5 w-5 items-center justify-center rounded hover:bg-zinc-800"
|
</button>
|
||||||
>
|
|
||||||
<CancelIcon className="h-4 w-4 text-zinc-300" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<Dialog.Description className="text-sm leading-tight text-zinc-400">
|
|
||||||
All messages from people you not follow
|
|
||||||
</Dialog.Description>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex h-[500px] flex-col overflow-y-auto overflow-x-hidden pb-5">
|
))}
|
||||||
{data.map((user) => (
|
</div>
|
||||||
<div
|
|
||||||
key={user.sender_pubkey}
|
|
||||||
className="group flex items-center justify-between px-4 py-3 hover:bg-zinc-800"
|
|
||||||
>
|
|
||||||
<User pubkey={user.sender_pubkey} />
|
|
||||||
<div>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
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"
|
|
||||||
>
|
|
||||||
Chat
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</Dialog.Panel>
|
|
||||||
</Transition.Child>
|
|
||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog.Content>
|
||||||
</Transition>
|
</Dialog.Portal>
|
||||||
</>
|
</Dialog.Root>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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 shrink-0 rounded-lg object-cover"
|
||||||
className="h-11 w-11 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>
|
||||||
)}
|
)}
|
||||||
|
@ -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} />
|
||||||
))}
|
))}
|
||||||
|
@ -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} />
|
||||||
))}
|
))}
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
<ComposeIcon className="h-4 w-4" />
|
<Button preset="small">
|
||||||
Postr
|
<ComposeIcon className="h-4 w-4" />
|
||||||
</Button>
|
Postr
|
||||||
<Transition appear show={open} as={Fragment}>
|
</Button>
|
||||||
<Dialog as="div" className="relative z-10" onClose={closeModal}>
|
</Dialog.Trigger>
|
||||||
<Transition.Child
|
<Dialog.Portal className="relative z-10">
|
||||||
as={Fragment}
|
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/80 backdrop-blur-xl" />
|
||||||
enter="ease-out duration-300"
|
<Dialog.Content className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
|
||||||
enterFrom="opacity-0"
|
<div className="relative h-min w-full max-w-2xl rounded-xl bg-white/10">
|
||||||
enterTo="opacity-100"
|
<div className="flex items-center justify-between px-4 py-4">
|
||||||
leave="ease-in duration-200"
|
<div className="flex items-center gap-2">
|
||||||
leaveFrom="opacity-100"
|
{account && <ComposerUser pubkey={account.pubkey} />}
|
||||||
leaveTo="opacity-0"
|
<span>
|
||||||
>
|
<ChevronRightIcon className="h-4 w-4 text-white/50" />
|
||||||
<div className="fixed inset-0 z-50 bg-black bg-opacity-30 backdrop-blur-md" />
|
</span>
|
||||||
</Transition.Child>
|
<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">
|
||||||
<div className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
|
New Post
|
||||||
<Transition.Child
|
<ChevronDownIcon className="h-4 w-4" />
|
||||||
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 gap-2">
|
|
||||||
{account && <ComposerUser pubkey={account.pubkey} />}
|
|
||||||
<span>
|
|
||||||
<ChevronRightIcon
|
|
||||||
width={14}
|
|
||||||
height={14}
|
|
||||||
className="text-zinc-500"
|
|
||||||
/>
|
|
||||||
</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">
|
|
||||||
New Post
|
|
||||||
<ChevronDownIcon className="h-4 w-4" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
onClick={closeModal}
|
|
||||||
type="button"
|
|
||||||
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" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<Composer />
|
</div>
|
||||||
</Dialog.Panel>
|
<Dialog.Close
|
||||||
</Transition.Child>
|
onClick={() => closeModal()}
|
||||||
|
className="inline-flex h-8 w-8 items-center justify-center rounded-md hover:bg-zinc-800"
|
||||||
|
>
|
||||||
|
<CancelIcon className="h-5 w-5 text-white/50" />
|
||||||
|
</Dialog.Close>
|
||||||
|
</div>
|
||||||
|
<Composer />
|
||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog.Content>
|
||||||
</Transition>
|
</Dialog.Portal>
|
||||||
</>
|
</Dialog.Root>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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 shrink-0 rounded-md object-cover"
|
||||||
className="h-8 w-8 object-cover"
|
/>
|
||||||
/>
|
<h5 className="text-base font-semibold leading-none text-white">
|
||||||
</div>
|
|
||||||
<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" />
|
||||||
)}
|
)}
|
||||||
|
@ -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,91 +29,63 @@ export function Logout() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Dialog.Root>
|
||||||
<button
|
<Dialog.Trigger asChild>
|
||||||
type="button"
|
<button
|
||||||
onClick={() => openModal()}
|
type="button"
|
||||||
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"
|
<div className="flex flex-col gap-2">
|
||||||
leave="ease-in duration-200"
|
<div className="flex items-center justify-between">
|
||||||
leaveFrom="opacity-100"
|
<Dialog.Title className="text-lg font-semibold leading-none text-white">
|
||||||
leaveTo="opacity-0"
|
Are you sure!
|
||||||
>
|
</Dialog.Title>
|
||||||
<div className="fixed inset-0 z-50 bg-black bg-opacity-30 backdrop-blur-md" />
|
<Dialog.Close className="inline-flex h-6 w-6 items-center justify-center rounded-md hover:bg-white/10">
|
||||||
</Transition.Child>
|
<CancelIcon className="h-4 w-4 text-white/50" />
|
||||||
<div className="fixed inset-0 z-50 flex min-h-full items-center justify-center">
|
</Dialog.Close>
|
||||||
<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 items-center justify-between">
|
|
||||||
<Dialog.Title
|
|
||||||
as="h3"
|
|
||||||
className="text-lg font-semibold leading-none text-zinc-100"
|
|
||||||
>
|
|
||||||
Are you sure!
|
|
||||||
</Dialog.Title>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={closeModal}
|
|
||||||
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>
|
|
||||||
<Dialog.Description className="text-sm leading-tight text-zinc-400">
|
|
||||||
<p className="mb-2">
|
|
||||||
When logout, all local data will be wiped, and restart app then
|
|
||||||
you need to start onboarding process again when you log in.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
In the next version, Lume will support multi account, then you can
|
|
||||||
switch between all account s instead of logout
|
|
||||||
</p>
|
|
||||||
</Dialog.Description>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex h-full w-full flex-col items-end justify-center overflow-y-auto px-5 py-2.5">
|
<Dialog.Description className="text-sm leading-tight text-white/50">
|
||||||
<div className="flex items-center gap-2">
|
<p className="mb-2">
|
||||||
<button
|
When logout, all local data will be wiped, and restart app then you
|
||||||
type="button"
|
need to start onboarding process again when you log in.
|
||||||
onClick={closeModal}
|
</p>
|
||||||
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"
|
<p>
|
||||||
>
|
In the next version, Lume will support multi account, then you can
|
||||||
Cancel
|
switch between all account s instead of logout
|
||||||
</button>
|
</p>
|
||||||
<button
|
</Dialog.Description>
|
||||||
type="button"
|
</div>
|
||||||
onClick={() => logout()}
|
</div>
|
||||||
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"
|
<div className="flex h-full w-full flex-col items-end justify-center overflow-y-auto px-5 py-2.5">
|
||||||
>
|
<div className="flex items-center gap-2">
|
||||||
Confirm
|
<button
|
||||||
</button>
|
type="button"
|
||||||
</div>
|
onClick={closeModal}
|
||||||
</div>
|
className="inline-flex h-9 items-center justify-center rounded-md px-3 text-sm font-medium text-white/50 hover:bg-white/10"
|
||||||
</Dialog.Panel>
|
>
|
||||||
</Transition.Child>
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => logout()}
|
||||||
|
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
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog.Content>
|
||||||
</Transition>
|
</Dialog.Portal>
|
||||||
</>
|
</Dialog.Root>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
|
@ -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 />
|
||||||
|
@ -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't fetch open graph, click to open webpage
|
Can't fetch open graph, click to open webpage
|
||||||
</p>
|
</p>
|
||||||
|
<span className="text-sm leading-none text-white">{domain.hostname}</span>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
|
@ -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,99 +50,94 @@ export function User({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover
|
<Popover.Root>
|
||||||
className={`flex ${size === 'small' ? 'items-center gap-2' : 'items-start gap-3'}`}
|
|
||||||
>
|
|
||||||
<Popover.Button
|
|
||||||
className={`${avatarWidth} ${avatarHeight} relative z-10 shrink-0 overflow-hidden bg-zinc-900`}
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
src={user?.picture || user?.image}
|
|
||||||
fallback={DEFAULT_AVATAR}
|
|
||||||
alt={pubkey}
|
|
||||||
className={`${
|
|
||||||
isRepost ? 'ring-1 ring-zinc-800' : ''
|
|
||||||
} ${avatarWidth} ${avatarHeight} ${
|
|
||||||
size === 'small' ? 'rounded' : 'rounded-lg'
|
|
||||||
} object-cover`}
|
|
||||||
/>
|
|
||||||
</Popover.Button>
|
|
||||||
<div
|
<div
|
||||||
className={`${isRepost ? 'mt-4' : ''} flex flex-1 items-baseline justify-between`}
|
className={twMerge(
|
||||||
|
'relative flex',
|
||||||
|
size === 'small' ? 'items-center gap-2' : 'items-start gap-3'
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<h5
|
<Popover.Trigger asChild>
|
||||||
className={`truncate font-semibold leading-none text-zinc-100 ${
|
|
||||||
size === 'small' ? 'max-w-[10rem]' : 'max-w-[15rem]'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{user?.nip05?.toLowerCase() ||
|
|
||||||
user?.name ||
|
|
||||||
user?.display_name ||
|
|
||||||
shortenKey(pubkey)}
|
|
||||||
</h5>
|
|
||||||
<div className="inline-flex items-center gap-2">
|
|
||||||
<span className="leading-none text-white/50">{createdAt}</span>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="inline-flex h-5 w-max items-center justify-center rounded px-1 hover:bg-white/20"
|
className={`${avatarWidth} ${avatarHeight} relative z-40 shrink-0 overflow-hidden`}
|
||||||
>
|
>
|
||||||
<VerticalDotsIcon className="h-4 w-4 rotate-90 transform text-white/50" />
|
<Image
|
||||||
|
src={user?.picture || user?.image}
|
||||||
|
fallback={DEFAULT_AVATAR}
|
||||||
|
alt={pubkey}
|
||||||
|
className={twMerge(
|
||||||
|
`object-cover ${avatarWidth} ${avatarHeight}`,
|
||||||
|
size === 'small' ? 'rounded' : 'rounded-lg',
|
||||||
|
isRepost ? 'ring-1 ring-zinc-800' : ''
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
</Popover.Trigger>
|
||||||
|
<div
|
||||||
|
className={twMerge(
|
||||||
|
'flex flex-1 items-baseline justify-between',
|
||||||
|
isRepost ? 'mt-4' : ''
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<h5
|
||||||
|
className={twMerge(
|
||||||
|
'truncate font-semibold leading-none text-white',
|
||||||
|
size === 'small' ? 'max-w-[10rem]' : 'max-w-[15rem]'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{user?.nip05?.toLowerCase() ||
|
||||||
|
user?.name ||
|
||||||
|
user?.display_name ||
|
||||||
|
shortenKey(pubkey)}
|
||||||
|
</h5>
|
||||||
|
<span className="leading-none text-white/50">{createdAt}</span>
|
||||||
</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"
|
<div className="flex gap-2.5 border-b border-white/5 px-3 py-3">
|
||||||
leaveFrom="opacity-100 translate-y-0"
|
<Image
|
||||||
leaveTo="opacity-0 translate-y-1"
|
src={user?.picture || user?.image}
|
||||||
>
|
fallback={DEFAULT_AVATAR}
|
||||||
<Popover.Panel className="absolute top-10 z-50 mt-3">
|
alt={pubkey}
|
||||||
<div className="w-[250px] overflow-hidden rounded-md border border-zinc-800/50 bg-zinc-900/90 backdrop-blur-lg">
|
className="h-11 w-11 shrink-0 rounded-lg object-cover"
|
||||||
<div className="flex gap-2.5 border-b border-zinc-800 px-3 py-3">
|
/>
|
||||||
<Image
|
<div className="flex flex-1 flex-col gap-2">
|
||||||
src={user?.picture || user?.image}
|
<div className="inline-flex flex-col gap-1">
|
||||||
fallback={DEFAULT_AVATAR}
|
<h5 className="text-sm font-semibold leading-none">
|
||||||
alt={pubkey}
|
{user?.displayName || user?.name}
|
||||||
className="h-11 w-11 shrink-0 rounded-lg object-cover"
|
</h5>
|
||||||
/>
|
<span className="max-w-[10rem] truncate text-sm leading-none text-white/50">
|
||||||
<div className="flex flex-1 flex-col gap-2">
|
{user?.nip05 || displayNpub(pubkey, 16)}
|
||||||
<div className="inline-flex flex-col gap-1">
|
</span>
|
||||||
<h5 className="text-sm font-semibold leading-none">
|
</div>
|
||||||
{user?.displayName || user?.name || (
|
<div>
|
||||||
<div className="h-3 w-20 animate-pulse rounded-sm bg-zinc-700" />
|
<p className="line-clamp-3 break-all leading-tight text-white">
|
||||||
)}
|
{user?.about}
|
||||||
</h5>
|
</p>
|
||||||
<span className="max-w-[10rem] truncate text-sm leading-none text-zinc-500">
|
|
||||||
{user?.nip05 || shortenKey(pubkey)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="line-clamp-3 break-all leading-tight text-zinc-100">
|
|
||||||
{user?.about}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2 px-3 py-3">
|
|
||||||
<Link
|
|
||||||
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"
|
|
||||||
>
|
|
||||||
View profile
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
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"
|
|
||||||
>
|
|
||||||
Message
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Popover.Panel>
|
<div className="flex items-center gap-2 px-3 py-3">
|
||||||
</Transition>
|
<Link
|
||||||
</Popover>
|
to={`/app/users/${pubkey}`}
|
||||||
|
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
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
to={`/app/chats/${pubkey}`}
|
||||||
|
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
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</Popover.Content>
|
||||||
|
</Popover.Portal>
|
||||||
|
</Popover.Root>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user