replaced radix-ui with headless-ui

This commit is contained in:
Ren Amamiya
2023-04-24 16:01:49 +07:00
parent 3e31e7d4dc
commit 188c052a1a
14 changed files with 1094 additions and 1875 deletions

View File

@@ -14,13 +14,7 @@
"**/*.{js,ts,jsx,tsx}": "eslint --fix" "**/*.{js,ts,jsx,tsx}": "eslint --fix"
}, },
"dependencies": { "dependencies": {
"@radix-ui/react-alert-dialog": "^1.0.3", "@headlessui/react": "^1.7.14",
"@radix-ui/react-collapsible": "^1.0.2",
"@radix-ui/react-dialog": "^1.0.3",
"@radix-ui/react-dropdown-menu": "^2.0.4",
"@radix-ui/react-popover": "^1.0.5",
"@radix-ui/react-tabs": "^1.0.3",
"@radix-ui/react-tooltip": "^1.0.5",
"@supabase/supabase-js": "^2.21.0", "@supabase/supabase-js": "^2.21.0",
"@tauri-apps/api": "^1.2.0", "@tauri-apps/api": "^1.2.0",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",

2016
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,11 +8,11 @@ import { DEFAULT_AVATAR, MESSAGE_RELAYS } from '@stores/constants';
import { dateToUnix } from '@utils/getDate'; import { dateToUnix } from '@utils/getDate';
import { createChannel } from '@utils/storage'; import { createChannel } from '@utils/storage';
import * as Dialog from '@radix-ui/react-dialog'; import { Dialog, Transition } from '@headlessui/react';
import { Cancel, Plus } from 'iconoir-react'; import { Cancel, Plus } from 'iconoir-react';
import { useSetAtom } from 'jotai'; import { useSetAtom } from 'jotai';
import { getEventHash, signEvent } from 'nostr-tools'; import { getEventHash, signEvent } from 'nostr-tools';
import { useContext, useEffect, useState } from 'react'; import { Fragment, useContext, useEffect, useState } from 'react';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { navigate } from 'vite-plugin-ssr/client/router'; import { navigate } from 'vite-plugin-ssr/client/router';
@@ -20,12 +20,20 @@ export const CreateChannelModal = () => {
const pool: any = useContext(RelayContext); const pool: any = useContext(RelayContext);
const activeAccount: any = useContext(AccountContext); const activeAccount: any = useContext(AccountContext);
const [open, setOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [image, setImage] = useState(DEFAULT_AVATAR); const [image, setImage] = useState(DEFAULT_AVATAR);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const setChannel = useSetAtom(defaultChannelsAtom); const setChannel = useSetAtom(defaultChannelsAtom);
const closeModal = () => {
setIsOpen(false);
};
const openModal = () => {
setIsOpen(true);
};
const { const {
register, register,
handleSubmit, handleSubmit,
@@ -64,7 +72,7 @@ export const CreateChannelModal = () => {
// insert to database // insert to database
createChannel(event.id, event.content, event.created_at); createChannel(event.id, event.content, event.created_at);
// close modal // close modal
setOpen(false); setIsOpen(false);
// redirect to channel page // redirect to channel page
navigate(`/channel?id=${event.id}`); navigate(`/channel?id=${event.id}`);
}, 2000); }, 2000);
@@ -75,42 +83,65 @@ export const CreateChannelModal = () => {
}, [setValue, image]); }, [setValue, image]);
return ( return (
<Dialog.Root open={open} onOpenChange={setOpen}> <>
<Dialog.Trigger asChild> <button
<div className="group inline-flex items-center gap-2 rounded-md px-2.5 py-1.5 hover:bg-zinc-900"> type="button"
onClick={() => openModal()}
className="group inline-flex items-center gap-2 rounded-md px-2.5 py-1.5 hover:bg-zinc-900"
>
<div className="inline-flex h-5 w-5 shrink items-center justify-center rounded bg-zinc-900 group-hover:bg-zinc-800"> <div className="inline-flex h-5 w-5 shrink items-center justify-center rounded bg-zinc-900 group-hover:bg-zinc-800">
<Plus width={12} height={12} className="text-zinc-500" /> <Plus width={12} height={12} className="text-zinc-500" />
</div> </div>
<div> <div>
<h5 className="text-sm font-medium text-zinc-500 group-hover:text-zinc-400">Add a new channel</h5> <h5 className="text-sm font-medium text-zinc-500 group-hover:text-zinc-400">Add a new channel</h5>
</div> </div>
</div> </button>
</Dialog.Trigger> <Transition appear show={isOpen} as={Fragment}>
<Dialog.Portal> <Dialog as="div" className="relative z-10" onClose={closeModal}>
<Dialog.Overlay className="fixed inset-0 z-50 bg-black bg-opacity-30 backdrop-blur-md data-[state=open]:animate-overlayShow" /> <Transition.Child
<Dialog.Content className="fixed inset-0 z-50 overflow-y-auto"> as={Fragment}
<div className="flex min-h-full items-center justify-center"> enter="ease-out duration-300"
<div className="relative flex h-min w-full max-w-lg flex-col gap-2 rounded-lg border border-zinc-800 bg-zinc-900"> 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 data-[state=open]:animate-overlayShow" />
</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 border-zinc-800 bg-zinc-900">
<div className="h-min w-full shrink-0 border-b border-zinc-800 px-5 py-6"> <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">
<h5 className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-2xl font-semibold leading-none text-transparent"> <Dialog.Title
as="h3"
className="bg-gradient-to-br from-zinc-200 to-zinc-400 bg-clip-text text-2xl font-semibold leading-none text-transparent"
>
Create channel Create channel
</h5> </Dialog.Title>
<Dialog.Close asChild>
<button <button
type="button" type="button"
onClick={closeModal}
autoFocus={false} autoFocus={false}
className="inline-flex h-5 w-5 items-center justify-center rounded hover:bg-zinc-900" className="inline-flex h-5 w-5 items-center justify-center rounded hover:bg-zinc-900"
> >
<Cancel width={20} height={20} className="text-zinc-300" /> <Cancel width={20} height={20} className="text-zinc-300" />
</button> </button>
</Dialog.Close>
</div> </div>
<p className="leading-tight text-zinc-400"> <Dialog.Description className="leading-tight text-zinc-400">
Channels are freedom square, everyone can speech freely, no one can stop you or deceive what to Channels are freedom square, everyone can speech freely, no one can stop you or deceive what to
speech speech
</p> </Dialog.Description>
</div> </div>
</div> </div>
<div className="flex h-full w-full flex-col overflow-y-auto px-5 pb-5 pt-3"> <div className="flex h-full w-full flex-col overflow-y-auto px-5 pb-5 pt-3">
@@ -144,7 +175,9 @@ export const CreateChannelModal = () => {
</div> </div>
</div> </div>
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<label className="text-xs font-semibold uppercase tracking-wider text-zinc-400">Description</label> <label className="text-xs font-semibold uppercase tracking-wider text-zinc-400">
Description
</label>
<div className="relative h-20 w-full shrink-0 overflow-hidden before:pointer-events-none before:absolute before:-inset-1 before:rounded-[11px] before:border before:border-fuchsia-500 before:opacity-0 before:ring-2 before:ring-fuchsia-500/20 before:transition after:pointer-events-none after:absolute after:inset-px after:rounded-[7px] after:shadow-highlight after:shadow-white/5 after:transition focus-within:before:opacity-100 focus-within:after:shadow-fuchsia-500/100 dark:focus-within:after:shadow-fuchsia-500/20"> <div className="relative h-20 w-full shrink-0 overflow-hidden before:pointer-events-none before:absolute before:-inset-1 before:rounded-[11px] before:border before:border-fuchsia-500 before:opacity-0 before:ring-2 before:ring-fuchsia-500/20 before:transition after:pointer-events-none after:absolute after:inset-px after:rounded-[7px] after:shadow-highlight after:shadow-white/5 after:transition focus-within:before:opacity-100 focus-within:after:shadow-fuchsia-500/100 dark:focus-within:after:shadow-fuchsia-500/20">
<textarea <textarea
{...register('about')} {...register('about')}
@@ -212,10 +245,11 @@ export const CreateChannelModal = () => {
</div> </div>
</form> </form>
</div> </div>
</Dialog.Panel>
</Transition.Child>
</div> </div>
</div> </Dialog>
</Dialog.Content> </Transition>
</Dialog.Portal> </>
</Dialog.Root>
); );
}; };

View File

@@ -1,12 +1,11 @@
import { AccountContext } from '@components/accountProvider'; import { AccountContext } from '@components/accountProvider';
import { RelayContext } from '@components/relaysProvider'; import { RelayContext } from '@components/relaysProvider';
import Tooltip from '@components/tooltip';
import { MESSAGE_RELAYS } from '@stores/constants'; import { MESSAGE_RELAYS } from '@stores/constants';
import { dateToUnix } from '@utils/getDate'; import { dateToUnix } from '@utils/getDate';
import * as AlertDialog from '@radix-ui/react-alert-dialog';
import * as Tooltip from '@radix-ui/react-tooltip';
import { EyeClose } from 'iconoir-react'; import { EyeClose } from 'iconoir-react';
import { getEventHash, signEvent } from 'nostr-tools'; import { getEventHash, signEvent } from 'nostr-tools';
import { useCallback, useContext } from 'react'; import { useCallback, useContext } from 'react';
@@ -31,55 +30,13 @@ export const HideMessageButton = ({ id }: { id: string }) => {
}, [activeAccount.pubkey, activeAccount.privkey, id, pool]); }, [activeAccount.pubkey, activeAccount.privkey, id, pool]);
return ( return (
<AlertDialog.Root> <Tooltip message="Hide this message">
<Tooltip.Provider> <button
<Tooltip.Root> onClick={() => hideMessage()}
<AlertDialog.Trigger asChild> className="inline-flex h-6 w-6 items-center justify-center rounded hover:bg-zinc-800"
<Tooltip.Trigger asChild> >
<button className="inline-flex h-6 w-6 items-center justify-center rounded hover:bg-zinc-800">
<EyeClose width={16} height={16} className="text-zinc-400" /> <EyeClose width={16} height={16} className="text-zinc-400" />
</button> </button>
</Tooltip.Trigger> </Tooltip>
</AlertDialog.Trigger>
<Tooltip.Portal>
<Tooltip.Content
className="select-none rounded-md bg-zinc-800 px-4 py-2 text-sm leading-none text-zinc-100 shadow-[hsl(206_22%_7%_/_35%)_0px_10px_38px_-10px,_hsl(206_22%_7%_/_20%)_0px_10px_20px_-15px] will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade"
sideOffset={4}
>
Hide this message
<Tooltip.Arrow className="fill-zinc-800" />
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
</Tooltip.Provider>
<AlertDialog.Portal>
<AlertDialog.Overlay className="fixed inset-0 z-50 bg-black bg-opacity-30 backdrop-blur-sm data-[state=open]:animate-overlayShow" />
<AlertDialog.Content className="fixed left-[50%] top-[50%] z-50 max-h-[85vh] w-[90vw] max-w-[500px] translate-x-[-50%] translate-y-[-50%] rounded-md bg-zinc-900 p-6 shadow-[hsl(206_22%_7%_/_35%)_0px_10px_38px_-10px,_hsl(206_22%_7%_/_20%)_0px_10px_20px_-15px] ring-1 ring-zinc-800 focus:outline-none data-[state=open]:animate-contentShow">
<AlertDialog.Title className="m-0 font-medium text-zinc-100">Are you absolutely sure?</AlertDialog.Title>
<AlertDialog.Description className="mb-5 mt-4 text-zinc-400">
This action cannot be undone. This will permanently hide this message and you will never see this again
</AlertDialog.Description>
<div className="flex justify-end gap-4">
<AlertDialog.Cancel asChild>
<button
autoFocus={false}
className="inline-flex h-9 items-center justify-center rounded px-4 font-medium leading-none text-zinc-200 outline-none hover:bg-zinc-900 focus:shadow-[0_0_0_2px]"
>
Cancel
</button>
</AlertDialog.Cancel>
<AlertDialog.Action asChild>
<button
autoFocus={false}
onClick={() => hideMessage()}
className="inline-flex h-9 items-center justify-center rounded bg-red-500 px-4 font-medium leading-none text-white outline-none hover:bg-red-600 focus:shadow-[0_0_0_2px] focus:shadow-red-700"
>
Yes, hide this message
</button>
</AlertDialog.Action>
</div>
</AlertDialog.Content>
</AlertDialog.Portal>
</AlertDialog.Root>
); );
}; };

View File

@@ -1,12 +1,11 @@
import { AccountContext } from '@components/accountProvider'; import { AccountContext } from '@components/accountProvider';
import { RelayContext } from '@components/relaysProvider'; import { RelayContext } from '@components/relaysProvider';
import Tooltip from '@components/tooltip';
import { MESSAGE_RELAYS } from '@stores/constants'; import { MESSAGE_RELAYS } from '@stores/constants';
import { dateToUnix } from '@utils/getDate'; import { dateToUnix } from '@utils/getDate';
import * as AlertDialog from '@radix-ui/react-alert-dialog';
import * as Tooltip from '@radix-ui/react-tooltip';
import { MicMute } from 'iconoir-react'; import { MicMute } from 'iconoir-react';
import { getEventHash, signEvent } from 'nostr-tools'; import { getEventHash, signEvent } from 'nostr-tools';
import { useContext } from 'react'; import { useContext } from 'react';
@@ -31,56 +30,13 @@ export const MuteButton = ({ pubkey }: { pubkey: string }) => {
}; };
return ( return (
<AlertDialog.Root> <Tooltip message="Mute this user">
<Tooltip.Provider> <button
<Tooltip.Root> onClick={() => muteUser()}
<AlertDialog.Trigger asChild> className="inline-flex h-6 w-6 items-center justify-center rounded hover:bg-zinc-800"
<Tooltip.Trigger asChild> >
<button className="inline-flex h-6 w-6 items-center justify-center rounded hover:bg-zinc-800">
<MicMute width={16} height={16} className="text-zinc-400" /> <MicMute width={16} height={16} className="text-zinc-400" />
</button> </button>
</Tooltip.Trigger> </Tooltip>
</AlertDialog.Trigger>
<Tooltip.Portal>
<Tooltip.Content
className="select-none rounded-md bg-zinc-800 px-4 py-2 text-sm leading-none text-zinc-100 shadow-[hsl(206_22%_7%_/_35%)_0px_10px_38px_-10px,_hsl(206_22%_7%_/_20%)_0px_10px_20px_-15px] will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade"
sideOffset={4}
>
Mute user
<Tooltip.Arrow className="fill-zinc-800" />
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
</Tooltip.Provider>
<AlertDialog.Portal>
<AlertDialog.Overlay className="fixed inset-0 z-50 bg-black bg-opacity-30 backdrop-blur-sm data-[state=open]:animate-overlayShow" />
<AlertDialog.Content className="fixed left-[50%] top-[50%] z-50 max-h-[85vh] w-[90vw] max-w-[500px] translate-x-[-50%] translate-y-[-50%] rounded-md bg-zinc-900 p-6 shadow-[hsl(206_22%_7%_/_35%)_0px_10px_38px_-10px,_hsl(206_22%_7%_/_20%)_0px_10px_20px_-15px] ring-1 ring-zinc-800 focus:outline-none data-[state=open]:animate-contentShow">
<AlertDialog.Title className="m-0 font-medium text-zinc-100">Are you absolutely sure?</AlertDialog.Title>
<AlertDialog.Description className="mb-5 mt-4 text-zinc-400">
This action cannot be undone. This will permanently mute this user and you will never receive message from
this user
</AlertDialog.Description>
<div className="flex justify-end gap-4">
<AlertDialog.Cancel asChild>
<button
autoFocus={false}
className="inline-flex h-9 items-center justify-center rounded px-4 font-medium leading-none text-zinc-200 outline-none hover:bg-zinc-900 focus:shadow-[0_0_0_2px]"
>
Cancel
</button>
</AlertDialog.Cancel>
<AlertDialog.Action asChild>
<button
autoFocus={false}
onClick={() => muteUser()}
className="inline-flex h-9 items-center justify-center rounded bg-red-500 px-4 font-medium leading-none text-white outline-none hover:bg-red-600 focus:shadow-[0_0_0_2px] focus:shadow-red-700"
>
Yes, mute this user
</button>
</AlertDialog.Action>
</div>
</AlertDialog.Content>
</AlertDialog.Portal>
</AlertDialog.Root>
); );
}; };

View File

@@ -1,6 +1,7 @@
import Tooltip from '@components/tooltip';
import { channelReplyAtom } from '@stores/channel'; import { channelReplyAtom } from '@stores/channel';
import * as Tooltip from '@radix-ui/react-tooltip';
import { Reply } from 'iconoir-react'; import { Reply } from 'iconoir-react';
import { useSetAtom } from 'jotai'; import { useSetAtom } from 'jotai';
@@ -12,26 +13,13 @@ export const ReplyButton = ({ id, pubkey, content }: { id: string; pubkey: strin
}; };
return ( return (
<Tooltip.Provider> <Tooltip message="Reply">
<Tooltip.Root>
<Tooltip.Trigger asChild>
<button <button
onClick={() => createReply()} onClick={() => createReply()}
className="inline-flex h-6 w-6 items-center justify-center rounded hover:bg-zinc-800" className="inline-flex h-6 w-6 items-center justify-center rounded hover:bg-zinc-800"
> >
<Reply width={16} height={16} className="text-zinc-400" /> <Reply width={16} height={16} className="text-zinc-400" />
</button> </button>
</Tooltip.Trigger> </Tooltip>
<Tooltip.Portal>
<Tooltip.Content
className="select-none rounded-md bg-zinc-800 px-4 py-2 text-sm leading-none text-zinc-100 shadow-[hsl(206_22%_7%_/_35%)_0px_10px_38px_-10px,_hsl(206_22%_7%_/_20%)_0px_10px_20px_-15px] will-change-[transform,opacity] data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade"
sideOffset={4}
>
Reply
<Tooltip.Arrow className="fill-zinc-800" />
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
</Tooltip.Provider>
); );
}; };

View File

@@ -1,23 +1,7 @@
import { ChatModalUser } from '@components/chats/chatModalUser'; import { Plus } from 'iconoir-react';
import { getPlebs } from '@utils/storage';
import * as Dialog from '@radix-ui/react-dialog';
import { Cancel, Plus } from 'iconoir-react';
import { useEffect, useState } from 'react';
export const ChatModal = () => { export const ChatModal = () => {
const [plebs, setPlebs] = useState([]);
useEffect(() => {
getPlebs()
.then((res: any) => setPlebs(res))
.catch(console.error);
}, []);
return ( return (
<Dialog.Root>
<Dialog.Trigger asChild>
<div className="group inline-flex items-center gap-2 rounded-md px-2.5 py-1.5 hover:bg-zinc-900"> <div className="group inline-flex items-center gap-2 rounded-md px-2.5 py-1.5 hover:bg-zinc-900">
<div className="group-hover:800 inline-flex h-5 w-5 shrink items-center justify-center rounded bg-zinc-900 group-hover:bg-zinc-800"> <div className="group-hover:800 inline-flex h-5 w-5 shrink items-center justify-center rounded bg-zinc-900 group-hover:bg-zinc-800">
<Plus width={12} height={12} className="text-zinc-500" /> <Plus width={12} height={12} className="text-zinc-500" />
@@ -26,34 +10,5 @@ export const ChatModal = () => {
<h5 className="text-sm font-medium text-zinc-500 group-hover:text-zinc-400">Add a new chat</h5> <h5 className="text-sm font-medium text-zinc-500 group-hover:text-zinc-400">Add a new chat</h5>
</div> </div>
</div> </div>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 z-50 bg-black bg-opacity-30 backdrop-blur-sm data-[state=open]:animate-overlayShow" />
<Dialog.Content className="fixed inset-0 z-50 overflow-y-auto">
<div className="flex min-h-full items-center justify-center">
<div className="relative flex h-[500px] w-full max-w-2xl flex-col rounded-lg bg-zinc-900 text-zinc-100 ring-1 ring-zinc-800">
<div className="sticky left-0 top-0 flex h-12 w-full shrink-0 items-center justify-between rounded-t-lg border-b border-zinc-800 bg-zinc-950 px-3">
<div className="flex items-center gap-2">
<Dialog.Close asChild>
<button
autoFocus={false}
className="inline-flex h-5 w-5 items-center justify-center rounded bg-zinc-900"
>
<Cancel width={12} height={12} className="text-zinc-300" />
</button>
</Dialog.Close>
<h5 className="font-semibold leading-none text-zinc-500">New chat</h5>
</div>
</div>
<div className="flex flex-col overflow-y-auto">
{plebs.map((pleb) => (
<ChatModalUser key={pleb.id} data={pleb} />
))}
</div>
</div>
</div>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
); );
}; };

View File

@@ -1,73 +1,11 @@
import { DEFAULT_AVATAR } from '@stores/constants'; import { DEFAULT_AVATAR } from '@stores/constants';
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
import { writeText } from '@tauri-apps/api/clipboard';
import { LogOut, ProfileCircle, Settings } from 'iconoir-react';
import { nip19 } from 'nostr-tools';
import { navigate } from 'vite-plugin-ssr/client/router';
export const ActiveAccount = ({ user }: { user: any }) => { export const ActiveAccount = ({ user }: { user: any }) => {
const userData = JSON.parse(user.metadata); const userData = JSON.parse(user.metadata);
const openProfilePage = () => {
navigate(`/user?pubkey=${user.pubkey}`);
};
const copyPublicKey = async () => {
await writeText(nip19.npubEncode(user.pubkey));
};
return ( return (
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<button className="relative h-11 w-11 rounded-lg"> <button className="relative h-11 w-11 rounded-lg">
<img <img src={userData.picture || DEFAULT_AVATAR} alt="user's avatar" className="h-11 w-11 rounded-lg object-cover" />
src={userData.picture || DEFAULT_AVATAR}
alt="user's avatar"
className="h-11 w-11 rounded-lg object-cover"
/>
</button> </button>
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content
className="min-w-[220px] rounded-md bg-zinc-900/80 p-1.5 shadow-input shadow-black/50 ring-1 ring-zinc-800 backdrop-blur-xl will-change-[opacity,transform] data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade data-[side=right]:animate-slideLeftAndFade data-[side=top]:animate-slideDownAndFade"
side="right"
sideOffset={5}
align="start"
>
<DropdownMenu.Item
onClick={() => openProfilePage()}
className="group relative flex h-7 select-none items-center rounded-sm px-1 pl-7 text-sm leading-none text-zinc-400 outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-zinc-800 data-[highlighted]:text-fuchsia-500"
>
<div className="absolute left-0 inline-flex w-6 items-center justify-center">
<ProfileCircle />
</div>
Open profile
</DropdownMenu.Item>
<DropdownMenu.Item className="group relative flex h-7 select-none items-center rounded px-1 pl-7 text-sm leading-none text-zinc-400 outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-zinc-800 data-[highlighted]:text-fuchsia-500">
Update profile
</DropdownMenu.Item>
<DropdownMenu.Item
onClick={() => copyPublicKey()}
className="group relative flex h-7 select-none items-center rounded px-1 pl-7 text-sm leading-none text-zinc-400 outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-zinc-800 data-[highlighted]:text-fuchsia-500"
>
Copy public key
</DropdownMenu.Item>
<DropdownMenu.Separator className="m-1 h-px bg-zinc-700/50" />
<DropdownMenu.Item className="group relative flex h-7 select-none items-center rounded px-1 pl-7 text-sm leading-none text-zinc-400 outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-zinc-800 data-[highlighted]:text-fuchsia-500">
<div className="absolute left-0 inline-flex w-6 items-center justify-center">
<Settings />
</div>
Settings
</DropdownMenu.Item>
<DropdownMenu.Item className="group relative flex h-7 select-none items-center rounded px-1 pl-7 text-sm leading-none text-zinc-400 outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-zinc-800 data-[highlighted]:text-fuchsia-500">
<div className="absolute left-0 inline-flex w-6 items-center justify-center">
<LogOut />
</div>
Logout
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
); );
}; };

View File

@@ -1,17 +1,16 @@
import ChannelList from '@components/channels/channelList'; import ChannelList from '@components/channels/channelList';
import * as Collapsible from '@radix-ui/react-collapsible'; import { Disclosure } from '@headlessui/react';
import { NavArrowUp } from 'iconoir-react'; import { NavArrowUp } from 'iconoir-react';
import { Suspense, useState } from 'react'; import { Suspense } from 'react';
import Skeleton from 'react-loading-skeleton'; import Skeleton from 'react-loading-skeleton';
export default function Channels() { export default function Channels() {
const [open, setOpen] = useState(true);
return ( return (
<Collapsible.Root open={open} onOpenChange={setOpen}> <Disclosure>
{({ open }) => (
<div className="flex flex-col px-2"> <div className="flex flex-col px-2">
<Collapsible.Trigger className="flex cursor-pointer items-center gap-1 px-1 py-1"> <Disclosure.Button className="flex cursor-pointer items-center gap-1 px-1 py-1">
<div <div
className={`inline-flex h-5 w-5 transform items-center justify-center transition-transform duration-150 ease-in-out ${ className={`inline-flex h-5 w-5 transform items-center justify-center transition-transform duration-150 ease-in-out ${
open ? 'rotate-180' : '' open ? 'rotate-180' : ''
@@ -20,13 +19,14 @@ export default function Channels() {
<NavArrowUp width={16} height={16} className="text-zinc-700" /> <NavArrowUp width={16} height={16} className="text-zinc-700" />
</div> </div>
<h3 className="text-[11px] font-bold uppercase tracking-widest text-zinc-600">Channels</h3> <h3 className="text-[11px] font-bold uppercase tracking-widest text-zinc-600">Channels</h3>
</Collapsible.Trigger> </Disclosure.Button>
<Collapsible.Content> <Disclosure.Panel>
<Suspense fallback={<Skeleton count={2} />}> <Suspense fallback={<Skeleton count={2} />}>
<ChannelList /> <ChannelList />
</Suspense> </Suspense>
</Collapsible.Content> </Disclosure.Panel>
</div> </div>
</Collapsible.Root> )}
</Disclosure>
); );
} }

View File

@@ -1,16 +1,14 @@
import ChatList from '@components/chats/chatList'; import ChatList from '@components/chats/chatList';
import * as Collapsible from '@radix-ui/react-collapsible'; import { Disclosure } from '@headlessui/react';
import { NavArrowUp } from 'iconoir-react'; import { NavArrowUp } from 'iconoir-react';
import { useState } from 'react';
export default function Chats() { export default function Chats() {
const [open, setOpen] = useState(true);
return ( return (
<Collapsible.Root open={open} onOpenChange={setOpen}> <Disclosure>
{({ open }) => (
<div className="flex flex-col px-2"> <div className="flex flex-col px-2">
<Collapsible.Trigger className="flex cursor-pointer items-center gap-1 px-1 py-1"> <Disclosure.Button className="flex cursor-pointer items-center gap-1 px-1 py-1">
<div <div
className={`inline-flex h-5 w-5 transform items-center justify-center transition-transform duration-150 ease-in-out ${ className={`inline-flex h-5 w-5 transform items-center justify-center transition-transform duration-150 ease-in-out ${
open ? 'rotate-180' : '' open ? 'rotate-180' : ''
@@ -19,11 +17,12 @@ export default function Chats() {
<NavArrowUp width={16} height={16} className="text-zinc-700" /> <NavArrowUp width={16} height={16} className="text-zinc-700" />
</div> </div>
<h3 className="text-[11px] font-bold uppercase tracking-widest text-zinc-600">Chats</h3> <h3 className="text-[11px] font-bold uppercase tracking-widest text-zinc-600">Chats</h3>
</Collapsible.Trigger> </Disclosure.Button>
<Collapsible.Content> <Disclosure.Panel>
<ChatList /> <ChatList />
</Collapsible.Content> </Disclosure.Panel>
</div> </div>
</Collapsible.Root> )}
</Disclosure>
); );
} }

View File

@@ -1,16 +1,14 @@
import { ActiveLink } from '@components/activeLink'; import { ActiveLink } from '@components/activeLink';
import * as Collapsible from '@radix-ui/react-collapsible'; import { Disclosure } from '@headlessui/react';
import { Bonfire, NavArrowUp, PeopleTag } from 'iconoir-react'; import { Bonfire, NavArrowUp, PeopleTag } from 'iconoir-react';
import { useState } from 'react';
export default function Newsfeed() { export default function Newsfeed() {
const [open, setOpen] = useState(true);
return ( return (
<Collapsible.Root open={open} onOpenChange={setOpen}> <Disclosure>
{({ open }) => (
<div className="flex flex-col px-2"> <div className="flex flex-col px-2">
<Collapsible.Trigger className="flex cursor-pointer items-center gap-1 px-1 py-1"> <Disclosure.Button className="flex cursor-pointer items-center gap-1 px-1 py-1">
<div <div
className={`inline-flex h-5 w-5 transform items-center justify-center transition-transform duration-150 ease-in-out ${ className={`inline-flex h-5 w-5 transform items-center justify-center transition-transform duration-150 ease-in-out ${
open ? 'rotate-180' : '' open ? 'rotate-180' : ''
@@ -19,8 +17,8 @@ export default function Newsfeed() {
<NavArrowUp width={16} height={16} className="text-zinc-700" /> <NavArrowUp width={16} height={16} className="text-zinc-700" />
</div> </div>
<h3 className="text-[11px] font-bold uppercase tracking-widest text-zinc-600">Newsfeed</h3> <h3 className="text-[11px] font-bold uppercase tracking-widest text-zinc-600">Newsfeed</h3>
</Collapsible.Trigger> </Disclosure.Button>
<Collapsible.Content className="flex flex-col text-zinc-400"> <Disclosure.Panel className="flex flex-col text-zinc-400">
<ActiveLink <ActiveLink
href="/newsfeed/following" href="/newsfeed/following"
className="flex h-8 items-center gap-2.5 rounded-md px-2.5 text-sm font-medium hover:text-zinc-200" className="flex h-8 items-center gap-2.5 rounded-md px-2.5 text-sm font-medium hover:text-zinc-200"
@@ -37,8 +35,9 @@ export default function Newsfeed() {
<Bonfire width={16} height={16} className="text-zinc-500" /> <Bonfire width={16} height={16} className="text-zinc-500" />
<span>Circle</span> <span>Circle</span>
</ActiveLink> </ActiveLink>
</Collapsible.Content> </Disclosure.Panel>
</div> </div>
</Collapsible.Root> )}
</Disclosure>
); );
} }

View File

@@ -6,10 +6,10 @@ import { DEFAULT_RELAYS } from '@stores/constants';
import { dateToUnix } from '@utils/getDate'; import { dateToUnix } from '@utils/getDate';
import * as Dialog from '@radix-ui/react-dialog'; import { Dialog, Transition } from '@headlessui/react';
import { ChatLines, OpenNewWindow } from 'iconoir-react'; import { ChatLines, OpenNewWindow } from 'iconoir-react';
import { getEventHash, signEvent } from 'nostr-tools'; import { getEventHash, signEvent } from 'nostr-tools';
import { useContext, useState } from 'react'; import { Fragment, useContext, useState } from 'react';
import { navigate } from 'vite-plugin-ssr/client/router'; import { navigate } from 'vite-plugin-ssr/client/router';
export const NoteComment = ({ export const NoteComment = ({
@@ -28,11 +28,19 @@ export const NoteComment = ({
const pool: any = useContext(RelayContext); const pool: any = useContext(RelayContext);
const activeAccount: any = useContext(AccountContext); const activeAccount: any = useContext(AccountContext);
const [open, setOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [value, setValue] = useState(''); const [value, setValue] = useState('');
const profile = activeAccount ? JSON.parse(activeAccount.metadata) : null; const profile = activeAccount ? JSON.parse(activeAccount.metadata) : null;
const closeModal = () => {
setIsOpen(false);
};
const openModal = () => {
setIsOpen(true);
};
const openThread = () => { const openThread = () => {
navigate(`/newsfeed/note?id=${eventID}`); navigate(`/newsfeed/note?id=${eventID}`);
}; };
@@ -49,24 +57,45 @@ export const NoteComment = ({
event.sig = signEvent(event, activeAccount.privkey); event.sig = signEvent(event, activeAccount.privkey);
pool.publish(event, DEFAULT_RELAYS); pool.publish(event, DEFAULT_RELAYS);
setOpen(false); setIsOpen(false);
}; };
return ( return (
<Dialog.Root open={open} onOpenChange={setOpen}> <>
<Dialog.Trigger asChild> <button
<button className="group flex w-16 items-center gap-1 text-sm text-zinc-500"> type="button"
onClick={() => openModal()}
className="group flex w-16 items-center gap-1 text-sm text-zinc-500"
>
<div className="rounded-md p-1 group-hover:bg-zinc-800"> <div className="rounded-md p-1 group-hover:bg-zinc-800">
<ChatLines width={20} height={20} className="text-zinc-500" /> <ChatLines width={20} height={20} className="text-zinc-500" />
</div> </div>
<span>{count}</span> <span>{count}</span>
</button> </button>
</Dialog.Trigger> <Transition appear show={isOpen} as={Fragment}>
<Dialog.Portal> <Dialog as="div" className="relative z-10" onClose={closeModal}>
<Dialog.Overlay className="fixed inset-0 z-50 bg-black bg-opacity-30 backdrop-blur-sm data-[state=open]:animate-overlayShow" /> <Transition.Child
<Dialog.Content className="fixed inset-0 z-50 overflow-y-auto"> as={Fragment}
<div className="flex min-h-full items-center justify-center"> enter="ease-out duration-300"
<div className="relative w-full max-w-2xl rounded-lg bg-zinc-900 p-4 text-zinc-100 ring-1 ring-zinc-800"> 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 data-[state=open]:animate-overlayShow" />
</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 border-zinc-800 bg-zinc-900">
{/* root note */} {/* root note */}
<div className="relative z-10 flex flex-col pb-6"> <div className="relative z-10 flex flex-col pb-6">
<div className="relative z-10"> <div className="relative z-10">
@@ -121,10 +150,11 @@ export const NoteComment = ({
</div> </div>
</div> </div>
</div> </div>
</Dialog.Panel>
</Transition.Child>
</div> </div>
</div> </Dialog>
</Dialog.Content> </Transition>
</Dialog.Portal> </>
</Dialog.Root>
); );
}; };

View File

@@ -0,0 +1,10 @@
export default function Tooltip({ message, children }: { message: string; children: React.ReactNode }) {
return (
<div className="group relative flex">
{children}
<span className="absolute top-10 scale-0 rounded bg-zinc-800 p-2 text-xs text-zinc-100 transition-all group-hover:scale-100">
{message}
</span>
</div>
);
}

View File

@@ -1,13 +1,7 @@
import NewsfeedLayout from '@components/layouts/newsfeed'; import NewsfeedLayout from '@components/layouts/newsfeed';
import ProfileFollowers from '@components/profile/followers';
import ProfileFollows from '@components/profile/follows';
import ProfileMetadata from '@components/profile/metadata';
import ProfileNotes from '@components/profile/notes';
import { usePageContext } from '@utils/hooks/usePageContext'; import { usePageContext } from '@utils/hooks/usePageContext';
import * as Tabs from '@radix-ui/react-tabs';
export function Page() { export function Page() {
const pageContext = usePageContext(); const pageContext = usePageContext();
const searchParams: any = pageContext.urlParsed.search; const searchParams: any = pageContext.urlParsed.search;
@@ -17,38 +11,7 @@ export function Page() {
return ( return (
<NewsfeedLayout> <NewsfeedLayout>
<div className="scrollbar-hide h-full w-full overflow-y-auto"> <div className="scrollbar-hide h-full w-full overflow-y-auto">
<ProfileMetadata id={pubkey} /> <p>{pubkey}</p>
<Tabs.Root className="flex w-full flex-col" defaultValue="notes">
<Tabs.List className="flex border-b border-zinc-800">
<Tabs.Trigger
className="flex h-10 flex-1 cursor-default select-none items-center justify-center px-5 text-sm font-medium leading-none text-zinc-400 outline-none hover:text-fuchsia-400 data-[state=active]:text-fuchsia-500 data-[state=active]:shadow-[inset_0_-1px_0_0,0_1px_0_0] data-[state=active]:shadow-current"
value="notes"
>
Notes
</Tabs.Trigger>
<Tabs.Trigger
className="flex h-10 flex-1 cursor-default select-none items-center justify-center px-5 text-sm font-medium text-zinc-400 outline-none placeholder:leading-none hover:text-fuchsia-400 data-[state=active]:text-fuchsia-500 data-[state=active]:shadow-[inset_0_-1px_0_0,0_1px_0_0] data-[state=active]:shadow-current"
value="followers"
>
Followers
</Tabs.Trigger>
<Tabs.Trigger
className="flex h-10 flex-1 cursor-default select-none items-center justify-center px-5 text-sm font-medium leading-none text-zinc-400 outline-none hover:text-fuchsia-400 data-[state=active]:text-fuchsia-500 data-[state=active]:shadow-[inset_0_-1px_0_0,0_1px_0_0] data-[state=active]:shadow-current"
value="following"
>
Following
</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="notes">
<ProfileNotes id={pubkey} />
</Tabs.Content>
<Tabs.Content value="followers">
<ProfileFollowers id={pubkey} />
</Tabs.Content>
<Tabs.Content value="following">
<ProfileFollows id={pubkey} />
</Tabs.Content>
</Tabs.Root>
</div> </div>
</NewsfeedLayout> </NewsfeedLayout>
); );